특정한 기능을 하는 함수나 변수들의 집합을 말한다.
모듈은 자체로도 하나의 프로그램이면서 다른 프로그램의 부품으로도 사용할 수 있다. 자바스크립트에서 코드를 재사용하기 위해 함수로 만드는 것과 비슷하다.
노드에서는 CommonJS
, ECMAScript
두 가지 모듈을 사용한다.
CommonJS 모듈은 표준 자바스크립트 모듈은 아니지만 표준이 나오기 이전부터 쓰였기 때문에 노드 생태계에서 가장 널리 쓰이는 모듈이다.
var.js에 변수 두 개를 선언하고 module.exports
에 변수들을 담은 객체를 대입했다.
// var.js
const odd = '홀수';
const even = '짝수';
module.export = {
odd,
even,
};
이제 이 파일은 변수들을 모아둔 모듈로서 다른 파일에서 이 파일을 불러오면 module.exports
에 대입된 값을 사용할 수 있다.
다른 파일에서 require
함수 안에 불러올 모듈의 경로를 기입해 모듈을 불러올 수 있다.
const { odd, even } = require('./var');
function checkOddOrEven(num) {
if(num % 2){
return odd;
}
return even
}
module.exports = checkOddOrEven;
이렇게 다른 모듈을 사용하는 파일도 다시 모듈로 만들 수 있다. 또한 module.exports
에는 객체만 대입해야 하는 것은 아니며 함수나 변수를 대입해도 된다.
module.exports
외에도 exports
객체로도 모듈을 만들 수 있다.
exports.odd = '홀수';
exports.even = '짝수';
module.exports
로 한 번에 대입하는 대신 각각의 변수를 exports
객체에 하나씩 넣어는데도 둘은 동일하게 동작한다.
그 이유는 module.exports === exports
이기 때문이다.
require
는 함수이고, 함수는 객체이므로 속성을 몇 개 갖고 있다.
그중에서 require.cache
와 require.main
을 살펴보자.
// /root
// └─ var.js
// └─ require.js
// require.js
console.log("require가 가장 위에 오지 않아도 된다.");
module.exports = "저를 찾아보세요";
require("./var");
console.log("require.cache입니다.");
console.log(require.cache);
console.log("require.main입니다.");
console.log(require.main === module);
console.log(require.main.filename);
실행 결과 >
require가 가장 위에 오지 않아도 된다.
{ odd: 'CJS 홀수1입니다.', even: 'CJS 짝수1입니다.' }
require.cache입니다.
[Object: null prototype] {
'D:\\바탕화면\\study\\node.js\\require.js': {
id: '.',
path: 'D:\\바탕화면\\study\\node.js',
exports: '저를 찾아보세요',
filename: 'D:\\바탕화면\\study\\node.js\\require.js',
loaded: false,
children: [ [Object] ],
paths: [
'D:\\바탕화면\\study\\node.js\\node_modules',
'D:\\바탕화면\\study\\node_modules',
'D:\\바탕화면\\node_modules',
'D:\\node_modules'
]
},
'D:\\바탕화면\\study\\node.js\\var.js': {
id: 'D:\\바탕화면\\study\\node.js\\var.js',
path: 'D:\\바탕화면\\study\\node.js',
exports: { odd: 'CJS 홀수1입니다.', even: 'CJS 짝수1입니다.' },
filename: 'D:\\바탕화면\\study\\node.js\\var.js',
loaded: true,
children: [],
paths: [
'D:\\바탕화면\\study\\node.js\\node_modules',
'D:\\바탕화면\\study\\node_modules',
'D:\\바탕화면\\node_modules',
'D:\\node_modules'
]
}
}
require.main입니다.
true
D:\바탕화면\study\node.js\require.js
require
는 반드시 파일 최상단에 위치할 필요가 없고,
module.exports
도 최하단에 위치할 필요가 없다.
require.cache
객체에 require.js
나 var.js
같은 파일 이름이 속성명으로 들어 있다. 속성값으로는 각 파일의 모듈 객체가 들어 있다. 한번 require한 파일은 require.cache
에 저장되므로 새로 불러오지 않고 재사용이 가능하다.
require.main
은 노드 실행 시 첫 모듈을 가리킨다. 현재 node require
로 실행했으므로 require.js
가 require.main
이 된다.
ECMAScript 모듈(ES모듈)은 공식적인 자바스크립트 모듈 형식이다.
// var.mjs
export const odd = '홀수';
export const even = '짝수';
// func.mjs
import { odd, even } from './var.mjs';
function checkOddOrEven(num) {
if(num % 2) { // 홀수이면
return odd;
}
return even;
}
export default checkOddOrEven
.js
확장자에서 import
를 사용하면 SyntaxError: Cannot use import statement outside a module 에러가 발생한다. .js
확장자를 사용하면서 ES 모듈을 사용하려면 package.json
에 type: "module"
속성을 넣으면 된다.
CommonJS 모듈과는 다르게 import 시 파일 경로에서 .js
, .mjs
같은 확장자는 생략할 수 없다.
브라우저
require
조건부로 모듈을 불러오는 것을 다이내믹 임포트라고 한다.
const a = false;
if(a) {
require('./func);
}
console.log('성공');
하지만 ES모듈은 조건문 안에서 import하는 것이 불가능하다.
// dynamic.mjs
const a = true;
if(a) {
import './func.mjs;' // SyntaxError: Unexpected string
}
console.log('성공');
대신 import()
함수를 사용해 모듈을 동적으로 불러올 수 있다.
// dynamic.mjs
const a = true;
if(a) {
const m1 = await import('./func.mjs;');
console.log(m1);
}
$ node dynamic.mjs
[Module: null prototype] { default: [Function: checkOddOrEven] }
import()
는 Promise를 반환 하기 때문에 await
이나 then
을 붙여야 한다. 위 코드에서는 async
함수를 사용하지 않았는데, ES 모듈의 최상위 스코프에서는 async
함수 없이도 await
할 수 있다.
노드에서는 파일 사이에 모듈 관계가 있는 경우가 많으므로 현재 파일의 경로나 파일명을 알아야 하는 경우가 있다.
__filename
, __dirname
키워드로 현재 파일명과 현재 파일의 경로를 확인할 수 있다.
// filename.js
console.log(__filename);
console.log(__dirname);
$ node filename.js
D:\바탕화면\study\node.js\filename.js
D:\바탕화면\study\node.js
ES모듈에서는 __filename
과 __dirname
을 사용할 수 없어서 import.meta.url
로 경로를 가져올 수 있다.
// filename.mjs
console.log(import.meta.url);
console.log("__filename은 에러");
console.log(__filename);
$ node filename.mjs
file:///D:/%EB%B0%94%ED%83%95%ED%99%94%EB%A9%B4/study/node.js/filename.mjs
__filename은 에러
file:///D:/%EB%B0%94%ED%83%95%ED%99%94%EB%A9%B4/study/node.js/filename.mjs:3
console.log(__filename);
^
ReferenceError: __filename is not defined in ES module scope
(생략)