1. 모듈이란?
모듈(module)
- 전체를 이루는 부품 하나하나
- JavaScript 파일 하나
- 다른 모듈에 있는 함수를 사용하려면 그 모듈을 가져와야 함 = 모듈을 로드(load)
- require는 모듈을 로드해서 객체 하나를 리턴
- require 함수가 리턴하는 객체를 가져다 쓸 수 있도록 변수에 대입
2. 모듈 내부의 함수 공개하기
- 모듈 내부의 것들을 외부로 공개해줘야 다른 모듈에서도 그것을 사용할 수 있음
math-tools.js
function add(a,b) {
return a+b;
}
exports.add = add;
main.js
let m = require('./math-tools.js');
console.log(m.add(1,2));
3. 모듈 더 알아보기
1) 모듈의 확장자인 .js를 꼭 붙이지 않아도됨
let m = require('./math-tools');
2) 모듈의 경로를 정확하게 작성해야 함
3) require 함수가 리턴하는 객체는 상수로 대입하는 것이 좋음
const m = require('./math-tools.js');
- 모듈이 리턴한 객체를 변수로 받으면, 나중에 본인 또는 다른 개발자가 변수 m에 다른 값을 실수로 다시 저장하게 될 수도 있기 때문
4) 모듈 안의 모든 것들을 공개할 수 있음
4. 하나의 객체로 모아서 외부에 공개하기
- exports = 모듈 안에 있는 것들을 하나씩 공개
- module.exports = 공개하고 싶은 것들을 모은 객체를 외부에 공개
math-tools.js
let calculator = {
PI :3.14,
add: (a,b) => a+b,
subtract: (a,b) => a-b,
multiply: (a,b) => a*b,
divide : (a,b) => a/b,
};
module.exports = calculator;
main.js
const me = requir('./math-tools');
console.log(m.PI);
console.log(m.add(2,4));
console.log(m.subtract(1,2));
console.log(m.multiply(1,2));
console.log(m.divide(1,2));
5. exports와 module.exports(심화)
1) Module wrapper function
- Node.js가 모듈을 로드할 때는 모듈 내의 전체 코드를 감싸주는 작업인 Module wrapper function을 해줘야 함
(function (exports, require, module, __filename, __dirname) {
// 모듈 코드
});
(function (exports, require, module, __filename, __dirname) {
function add(a, b) {
return a + b;
}
exports.add = add;
});
- Module wrapper function의 다섯 가지 인자 exports, require, module, __filename, __dirname에는 Node.js가 각각 알맞은 것들을 전달함
-> 이 때문에 이 다섯가지 인자는, 우리가 직접 정의해준 적이 없더라도, 모듈 안에서 항상 자유롭게 접근 가능
function add(a, b) {
return a + b;
}
console.log('exports ------------------------->');
console.dir(exports);
console.log('require ------------------------->');
console.dir(require);
console.log('module ------------------------->');
console.dir(module);
console.log('__filename ------------------------->');
console.dir(__filename);
console.log('__dirname ------------------------->');
console.dir(__dirname);
- 5가지 인자에 자유롭게 접근 가능
exports 객체
- 안에 아무 프로퍼티(속성)도 없는 텅 비어있는 객체
module 객체
- exports라는 프로퍼티가 있고, 그 프로퍼티가 빈 객체 하나를 가리키고 있음
- exports 객체와 module 객체의 exports 프로퍼티가 가리키는 객체는 '동일한' 객체. 그리고 우리가 모듈 내부의 것들을 외부로 공개하기 위해 exports나 module.exports를 썼던 것은 바로 이 객체에 접근하기 위해서 였던 것
- 이 객체가 다른 모듈에서 require 함수로 이 모듈을 로드할 때 리턴되는 객체
-> 다른 모듈에서 이 math-tools.js 모듈을 로드하면 require 함수는 'exports 객체 (= module 객체의 exports 프로퍼티가 가리키는 객체)'를 리턴
const m = require('./math-tools');
2) 정리 및 주의할 점
- exports. 속성 = 값
- module.exports = 객체
- require 함수는 'module 객체의 exports 프로퍼티가 가리키는 객체'를 리턴하기 때문에, exports 객체를 아예 새로 설정해버리면 안됨
-> exports 키워드로는 'exports.속성 = ~'이런 식으로 해당 객체에 프로퍼티를 추가하는식으로만 사용 가능
6. 코어 모듈
- 모듈 중에는 가져다 쓸 수 있도록 이미 준비된 것들도 있음
- Node.js에서 모듈은 내가 직접 만든 모듈과 이미 만들어져 있는 모듈 두가지로 나눌 수 있음
- 이미 만들어져 있는 모듈은 코어 모듈과 서드파티 모듈로 나뉨
코어모듈
- Node라는 실행 프로그램 안에 이미 포함되어 있는 모듈
- Node만 설치하면 바로 사용 가능
ex) fs는 파일이나 디렉토리에 관한 작업을 할 때 필요한 코어 모듈
const fs = require('fs');
let fileList = fs.readdirSync('.');
console.log(fileList);
ex) os는 컴퓨터에 설치된 운영체제 관련 정보들을 가져오는 코어 모듈
const os = require('os');
console.log(os.cpus());
서드파티 모듈
- 여러 개발자 또는 회사들이 만들어서 인터넷의 공개 저장소에 제공하는 모듈들
- 내가 만든 모듈도 아니고 노드에 내장된 모듈도 아닌 제 3자가 만든 모듈
7. Node.js와 브라우저의 차이
1) 제공하는 API가 다름
API
- Application Programming Interface
- 어떤 플랫폼이나 실행환경 등에서 제공하는 인터페이스
- 특정 환경에서 자유롭게 가져다 쓸 수 있는 함수나 객체 등을 의미
- 브라우저와 달리 Node.js에서는 시각적으로 보여주는 UI 부분을 구현할 필요 없음
- UI에 관한 API들이 브라우저에는 존재하지만 Node.js에는 존재하지 않음
- window, document 같은 객체들도 Node.js에는 존재하지 않음
- Node.js에만 있는 API
ex) 컴퓨터 제어 API (파일 생성, 컴퓨터 정보 확인,,,,)
2) 자바스크립트를 실행하는 엔진의 차이
- Node.js의 경우 구글 크롬 브라우저에서 사용되는 v8 엔진을 자바스크립트 실행 엔진으로 사용
- 브라우저는 그 종류와 버전에 따라 엔진이 다름
8. 서드파티 모듈
서드파티 모듈(3rd party)
- 다른 개발자나 회사들이 만들어서 인터넷상의 공개 저장소에서 제공하는 모듈
- 서드파티 모듈을 사용하려면 그 서드파티 모듈을 인터넷상의 공개 저장소에서 내 컴퓨터로 가져와야 함
ex) cowsay 모듈 설치
const cowsay = require('cowsay');
console.log(cowsay.say({
text: "I love javascript",
}));
9. 서드파티 모듈 설치 후 생겨난 것들
1) package-lock.json 파일
- package-lock.json 파일에는 nodeStudy 디렉토리 안에 설치된 모든 서드파티 모듈들에 관한 정보가 기록되어 있음
- dependencies라는 필드에는, 현재 nodeStudy 디렉토리에 설치된 서드파티 모듈들의 정보가 담겨 있음
- dependencies는 '의존하고 있는 것들'이라는 뜻인데 , 이 프로젝트가 이 서드파티 모듈들에 의존하고 있기 때문에 이 필드 안에 그 정보들이 담긴 것
- 하나의 서드파티 모듈이 설치될 때는 그것이 의존하는 다른 서드파티 모듈들도 함께 설치됨
cowsay 모듈 -> string-width 모듈 -> strip-ansi 모듈 -> ansi-regex 모듈, 이렇게 여러 단계에 걸쳐
cowsay 모듈은 결국 ansi-regex라는 모듈에 의존
2) node_modules 디렉토리
- node_modules 디렉토리는 서드파티 모듈들이 실제로 설치되는 공간
- 모든 서드파티 모듈들은 각각 하나의 디렉토리
- 모듈은 하나의 파일일 수도 있고, 하나의 디렉토리일 수도 있음
10. 비동기 프로그래밍과 콜백
동기 실행
const fs = require('fs');
console.log('Start');
let content = fs.readFileSync('./new', 'utf8'); //동기 실행
console.log(content);
console.log('Finish');
- 첫 번째로 Start가 출력되고 나면 readFileSync 함수는 파일의 내용을 읽기 시작
- 파일 읽기가 끝나기 전까지는 절대 다음 코드로 넘어가지 않음
- 파일 읽기가 다 끝나고 content라는 변수에 파일 내용이 리턴되면 실행이 다음 코드로 넘어가고 Finish가 출력됨
비동기 실행
- 특정 작업이 완료되었을 때 실행할 콜백을 등록해두고 바로 다음 코드로 실행을 넘기는 것
const fs = require('fs');
console.log('Start');
fs.readFile('./new', 'utf8', (error, data) => { //비동기 실행
console.log(data);
});
console.log('Finish');
- Start가 출력되고 나면 그 다음 readFile 함수가 실행
- readFile 함수가 실행되면 new 파일을 읽기 시작
- new 파일을 다 읽었을 때 콜백함수 등록
콜백 : 어떤 작업이 완료되었을 때 실행될 함수
- console.log 함수로 실행 흐름이 넘어감
- new 파일에 대한 읽기 작업이 완료되면 그제서야 readFile 함수에서 등록해줬던 콜백이 실행되는 것
- readFileSync 함수는 동기 실행 함수, readFile 함수는 비동기 실행 함수
- Node.js는 비동기 실행 함수를 사용하는 것이 권장되는 실행 환경
11. 비동기 프로그래밍 정리
const fs = require('fs');
let test = 1; // '첫 번째'
fs.readFile('./new', 'utf8', (err, data) => {
test = 2; // '세 번째'
});
console.log(test); // '두 번째'
- readFile 함수가 비동기 실행되는 함수이기 때문에 일단 console.log(test)가 먼저 실행되고, 그 후에야 test 변수에 2가 대입
1) 비동기 실행에 자주 쓰이는 setTimeout 함수
setTimeout(callback, milliseconds)
- milliseconds(1000분의 1초) 후에 callback 인자에 설정한 함수를 실행
setTimeout(() => {
num = 2;
}, 1000);
- 1초 후에 num 변수에 2를 대입하라는 뜻
- setTimeout 함수는 개발자가 직접 비동기 실행을 구현하고 싶을 때 사용할 수 있는 유용한 함수
let num = 1; // 첫번째
setTimeout(() => {
num = 2; // 세번째
}, 1000);
num = 3; // 두번째
console.log(num); //3
- num 변수에 1을 대입
- setTimeout 함수 실행 (1초 후에 실행될 콜백 설정)
- num 변수에 3을 대입
- num 변수의 값 출력
- setTimeout에서 설정해둔 콜백 실행
2) Node.js에서의 비동기 실행
- 프로세스 (Process)가 하나의 실행 흐름이라면, 스레드(Thread)는 그 안에 있는, 더 작은 단위의 실행 흐름
-프로그램 실행 과정
(1) 하드디스크나 SSD에 저장되어 있던 프로그램의 내용을,
(2) 메모리(memory)에 올려서
(3) CPU(Central Processing Unit)가 실행되도록 만듬
- 프로그램을 실행하면 실행흐름으로 하나의 '프로세스'가 생성되고 그 안에서 하나의 '스레드'가 실행중인 상태가 됨
ex)
크롬 브라우저에서 영화 파일 하나를 다운로드 하고 (Thread-2), 최신 유행 음악을 재생하면서 (Thread-3), 구글 검색(Thread-4)을 하는 등 새로운 작업을 시작할 때 마다, 크롬 프로세스 안의 스레드 수는 하나씩 늘어남
- Node.js 내부에서의 비동기 실행 구현 방법 중 한가지
(1) 스레드 1개 (메인 스레드)
- 자바스크립트 코드 실행
- 이때 오래 걸리는 작업A은 다른 스레드에 넘기기
- 그리고 일단 그 다음 작업 B를 시작하기
- 작업 A가 완료되었다는 알림과 그 작업 결과를 받으면
- 작업 결과를 가지고 콜백 실행하기
(2) 스레드 10개
- 메인 스레드가 요청한 작업 처리 하기
- 작업이 완료되면 끝났다고 메인 스레드에 알려주고, 작업 결과 전달하기
- 메인 스레든느 빠르게 처리할 수 있는 작업들을 집중해서 '혼자'처리하고,
- 파일 읽기와 같이 시간이 오래 걸리는 작업들은 다른 스레드에 맡김
12. 비동기 프로그래밍과 이벤트
- 이벤트를 사용하여 비동기 프로그래밍
- Node.js의 이벤트 = 어떤 일이 발생했음을 알리는 신호
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
myEmitter.on('test', () => {
console.log('Success!');
});
myEmitter.emit('test');
- events라는 이름의 코어 모듈을 가져옴
- events 코어 모듈은 EventEmitter 클래스 하나를 외부에 공개
- EventEmitter 클래스가 있어야 이벤트를 사용할 수 있음
- EventEmitter 클래스로 myEmitter라는 객체 생성
- on 메서드는 어떤 이벤트가 발생했을 때 실행할 콜백을 등록하는 함수
- test라는 이벤트가 발생하면 Success라는 문자열이 출력되도록 콜백을 등록
- emit 메서드로 test라는 이벤트를 발생
- Node,js에서 제공하는 주요 API들은 이벤트 기반 구조 위에서 작성되었고 그 구조에서는 특정 객체가 이벤트를 발생시키면 그 이벤트에 관한 콜백이 실행됨
13. EventEmitter 객체 사용법
- EventEmitter 객체를 사용할 때는 이벤트를 발생시키기 전에 콜백 설정을 먼저 해줘야 함
- 하나의 이벤트에 대해 여러 개의 콜백을 설정할 수 있음
-> 설정된 콜백들은 그 이벤트가 발생했을 때 설정했던 순서대로 하나씩 진행
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
myEmitter.on('test', () => {
console.log('1');
});
myEmitter.on('test', () => {
console.log('2');
});
myEmitter.on('test', () => {
console.log('3');
});
myEmitter.emit('test');
- 이벤트를 발생시키고 그 이벤트에 대한 콜백이 실행되는 현상은 하나의 EventEmitter 객체 내에서만 이루어짐
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
const myEmitter2 = new EventEmitter();
myEmitter.on('test', () => {
console.log('1');
});
myEmitter.on('test', () => {
console.log('2');
});
myEmitter2.on('test', () => {
console.log('3');
});
myEmitter.emit('test');
-> 1,2만 출력됨
14. EventEmitter 객체 사용법 더 알아보기
1) on 메소드
- on 메소드는 이벤트 핸들러를 설정하는 메소드
- on 메소드와 같은 용도를 가진 addListener라는 메소드도 있음
2) emit 메소드
- emit 메소드는 인위적으로 이벤트를 발생시키기 위해 쓰는 메소드
3) once 메소드
- once 메소드는 특정 이벤트에 대한 이벤트 핸들러를 등록한다는 점에서 on 메소드와 유사한 메소드
- 그 이벤트 핸들러가 해당 이벤트에 대해서 딱 한번만 반응해서 실행되도록 함
- once 메소드로 등록된 이벤트 핸들러는 한번 실행된 후에는 삭제됨
- 특정 이벤트가 어차피 한 번밖에 발생하지 않는 경우나, 이벤트 핸들러가 딱 한번만 실행되기를 바라는 경우 사용
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
myEmitter.once('test', () => {
console.log('Success!');
});
myEmitter.emit('test');
myEmitter.emit('test');
myEmitter.emit('test');
- 'Success!'가 딱 한번만 출력됨
4) listeners 메소드
- 특정 이벤트에 대한 이벤트 핸들러들을 출력해주는 메소드
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
myEmitter.once('test', () => {
console.log('A');
});
myEmitter.once('test', () => {
console.log('B');
});
myEmitter.once('test', () => {
console.log('C');
});
console.log(myEmitter.listeners('test'));
- test 이벤트의 총 3개의 이벤트 핸들러가 설정돼있다는 걸 알 수 있음
5) off 메소드
- 이벤트 핸들러를 해제하는 메소드
- 해제할 이벤트 핸들러를 정확히 지정해줘야 함
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
myEmitter.on('test', () => { // --- (A)
console.log('Success!');
});
myEmitter.off('test', () => { // --- (B)
console.log('Success!');
});
myEmitter.emit('test');
15. 이벤트에 추가 정보 함께 전달하기
- 전달하고 싶은 내용들을 인자로 추가
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
myEmitter.on('test', () => {
console.log('Success!');
});
myEmitter.emit('test','apple', 'banana', 'pear');
- 콜백에서 추가 정보를 받는 방법
myEmitter.on('test', (arg1, arg2, arg3) => {
console.log(arg1);
console.log(arg2);
console.log(arg3);
});
- 실무적으로 이벤트에 여러 인자를 주는 것보다는 여러 정보를 담고 있는 객체 하나만 전달하는 게 더 깔끔
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
const obj = {type: 'text', data: 'Hello Codeit', data: '2020-09-01'};
myEmitter.on('test', (info) => {
console.log(info);
});
myEmitter.emit('test', obj);
'Node.js' 카테고리의 다른 글
[코드잇] ORM으로 하는 데이터베이스 작업 (0) | 2023.10.26 |
---|---|
[코드잇] Express 기본 익히기 (1) | 2023.10.26 |
[코드잇] 서드파티 모듈과 npm 제대로 배우기 (1) | 2023.10.24 |
[코드잇] 초간단 웹서버 만들기 (0) | 2023.10.07 |