노드 학습 3일차
ECMAScript 모듈 vs CommonJS
ECMA에서 정한 표준 모듈 사용법이라 할 수 있습니다.
CommonJS
와 문법적으로도 차이가 있으며 근래에는 ECMAScript
가 자바스크립트의 표준 문법으로 널리 사용되고 있습니다.
차이점 | CommonJS | ECMAScript |
---|---|---|
문법 | require(‘./a’); module.exports = A const A = require(‘./a’); exports.C = D; const E = F; exports.E = E; const {C,E} = require(‘./b’) | import ‘./a.mjs’ export default A; import A from ‘./a.mjs’; export const C = D; const E = F; export {E}; import {C,E} from ‘./b.mjs’ |
확장자 | js cjs | mjs |
확장자 생략 | 가능 | 불가능 |
다이나믹 임포트 | 가능 | 불가능 |
인덱스 생략 | 가능 | 불가능 |
top level await | 불가능 | 가능 |
__filename ,__dirname ,require, module.exports, exports | 사용 가능 | 사용 불가능 ( __filename 대신 import.meta.url 사용) |
서로 간 호출 | 가능 | 가능 |
확장자 비교
CommonJS 확장자 .cjs
일반적인 자바스크립트 파일은 .js
확장자를 가집니다. CommonJS
의 확장자는 .cjs
를 사용합니다. 하지만 CommonJS
의 확장자는 .js
로 생략이 가능하며 일반적으로 사용하는 .js
파일은 전부 CommonJS
파일이라고 볼 수 있습니다.
ECMAScript
의 .mjs
확장자 파일과 구분짓기 위해 명시적으로 .cjs
라고 사용하기도 합니다.
ECAMScript 모듈의 확장자 .mjs
ECMAScript
의 확장자는 .mjs
를 사용합니다. 이는 별도의 설정이 없다면 반드시 지켜야 합니다.
모듈화 비교
CommonJS
내보내기
ECMAScript 모듈처럼 named export 와 default export 가 구분되어 있지 않으며 기능적으로 이와 유사한 기능을 하도록 사용할 수 있습니다.
exports에 값을 그대로 담는 방법을 통해 default export 와 유사한 기능을 구현할 수 있습니다. 그리고 exports 에 객체를 담는 방법을 통해 named export 와 유사한 기능을 구현할 수 있습니다.
객체 형식으로 exports 하는 예시
1
2
3
const foo = "hello :)";
const bar = "bye :("
module.exports = {foo, bar};
값을 exports 하는 예시
1
2
const foobar = "Hello world";
module.exports = foobar;
불러오기
CommonJS 에서는 모듈을 불러올 때 require()
를 사용합니다. 객체 형식으로 exports 한 경우에는 구조 분해 할당을 사용 할 수 도 있습니다.
1
2
3
4
const helloWorld require("파일경로");
// 이 경우 객체의 속성에 맞는 이름을 사용해야합니다.
const {foo, bar} require('파일경로');
ECMAScript
내보내기
ECMAScript 에서는 모듈을 내보낼 때 named export 와 default export 를 구분 지어 사용합니다.
named export 예제
1
2
export const foo = "foo";
export const bar = "bar";
default export 예제
1
2
3
4
5
const sayHello(){
console.log("Hello!");
}
export default sayHello;
불러오기
import ... from
문법을 사용합니다.
별도의 설정이 없는 경우 확장자까지 명시합니다.
named export 한 모듈과 default export 한 모듈의 불러오는 방법이 다릅니다. named export 모듈의 경우 변수명에 맞게 import 해야하며 default export 모듈은 변수명과 무관하게 import 할 수 있습니다.
1
import foo from "파일.mjs";
다이나믹 임포트
다이나믹 임포트는 조건에 따라 임포트를 하는 것입니다.
CommonJS에서는 사용이 가능합니다.
1
2
3
4
const a = true;
if(a){
require('./b');
}
위의 예제와 같이 조건에 따라 require
하는 것을 다이나믹 임포트라 합니다.
하지만 ECMAScript에서 import
는 일반적으로 지원되지 않으며 이를 구현하기 위해서는 import
키워드가 아닌 import()
함수를 실행하여 사용합니다.
1
2
3
4
5
6
const a = true;
if(a){
const b = await import('./b.mjs');
const c = await import('./c.mjs');
}
module.exports
vs export default
module.exports
와 export default
는 언뜻 기능이 같은 것으로 보입니다. 하지만 내부적으로 이는 조금 다른 구조를 가집니다.
hello!
라는 문자열을 내보내는 두 가지의 경우를 비교해보겠습니다.
module.exports
이 경우에는 코드를 천천히 보면
1
2
3
const 변수 = "안녕";
module.exports = 변수;
console.log(module);
이러한 형식을 가집니다. 곧 module
의 exports
의 값이 곧 뒤에 오는 값을 그대로 가지게 됨을 의미합니다. 그렇기 때문에 위의 경우 로그에는 다음과 같이 나오게 됩니다.
1
2
3
4
5
6
7
8
9
Module {
id: '.',
path: 'C:\\', // 파일 경로
exports: 'hello!', // module.exports 에 저장한 값
filename: 'C:\\moduleExportsTest.js', // 파일명
loaded: false,
children: [],
paths: [ 'C:\\node_modules' ]
}
즉 CommonJS에서 모듈을 받기 위해 사용하는 require()
은 Module 객체의 exports 속성의 값을 가져온다는 것을 알 수 있습니다.
export default
export default
의 경우를 로그로 남겨보기 위해 다음의 모듈을 만들어봤습니다.
1
export default "hello";
이제 이 모듈을 로그에 남겨보겠습니다.
1
2
import * as moduleObj from './exportDefaultTest.mjs';
console.log(moduleObj);
이 경우 로그에는 다음과 같이 남습니다.
1
[Module: null prototype] { default: 'hello' }
Module: null prototype
구문은 프로토타입에 대한 로그이니 여기서는 생략하겠습니다.
바로 다음을 보면 default에 데이터가 담겨있는 것을 알 수 있습니다.
이 부분이 차이입니다. module.exports
와 export default
가 완전히 동일한 것은 아님을 알고 넘어가면 좋겠습니다.