기본적으로 JS는 약간의 상호작용만을 위해 만들어진 간단한 언어이므로 큰 스크립트가 필요하지 않았으나, 프로젝트가 커지면서 모듈 분할을 위한 메커니즘이 필요하게 되었다.
따라서 모던JS에서는 모듈화를 위한 import/export
기능을 제공한다. 이것을 통한 모듈을 ESM(Ecma Script Module)
이라고 한다.
하지만 NodeJS
가 사용하는 CommonJS(CJS)
에서 사용하는 모듈 생태계를 위한 시스템은 이미 존재하고 있었으며 이것은 require
과 exports
로 import/export
의 그것과는 다르다.
테스트 모듈인 JEST
의 경우 @jest/globals
모듈등의 존재로 import/export
를 지원하기는 하나, 공식 문서에서 mock(테스트 대상이 아니나 테스트를 위해 필요한 가상 유닛)
등을 완전하게 사용할 수 없다고 기재되어 있다.
(공식 문서 번역) ESM과 CommonJS의 차이점
JSET 공식 문서
차이점의 대부분은 Node의 문서에 설명되어 있지만 거기에 언급된 것 외에도 Jest는 실행되는 모든 파일에 특수 변수(jest
)를 삽입합니다 . ESM에서 이 개체에 액세스하려면@jest/globals
모듈 에서 가져와야 합니다 .
현재 jest.mock은 ESM에서 깔끔한 방식으로 지원하지 않지만 향후 적절한 지원을 추가할 예정입니다.
다음과 같은 질문을 통해 ESM의 모듈을 자주 사용하는지에 대한 의견을 살펴볼 수 있다.
ESM에 대해 긍정적으로 보이지 않는 반응들
따라서 당장은 ESM보다는 CommonJS를 통하는 방법이 효율적이라 볼 수 있다. 따라서 exports/require에 관하여 기재하는 것이 당장 지금까지의 모듈을 사용하는 것에 있어서는 옳다.
CommonJS(CJS)
은Node.js
에서 사용하는모듈 생태계
에 대한 규칙을 수립하는 것을 목표로 하는 프로젝트이다.
보통은Node.js
를 사용한서버
측 JavaScript프로그래밍에 널리 사용된다.브라우저
측 JavaScript개발에도 사용되긴 하지만,브라우저
는CommonJS
를 지원하지 않아서, 코드를트랜스파일러
(모던ES코드를 오래된 브라우저도 사용할 수 있게 예전 버전의 ES코드로 바꾸어준다.Babel
이 유명하다.)로 패키징해야 한다.
단, 이것은 이전까지의 이야기이지 현재의 이야기가 아니다.
ESM는 [ES 공식]이기 때문에 그 힘은 매우 강력하며, 점점 더 많은 module들이 ESM를 지원하고 있다.
Three.js처럼 ESM만을 지원하는 모듈도 있기 때문에, ESM을 기본으로 보는 것이 현재로서는 조금 더 옳다.
다음과 같이 사용한다.
//모듈들의 Parent. exports에 넣지 않았으므로 이걸 직접 사용할 수는 없다.
class BaseModule {
constructor(moduletype){
this.moduletype = moduletype;
}
printModuleType(){
console.log(this.moduletype);
}
}
//exports에 FirstModule이라는 이름으로
//이 파일 내의 'MyFirstModule' 클래스를 export한다.
exports.FirstModule = class MyFirstModule extends BaseModule{
constructor(name){
super("FIRST");
this.name = name;
}
printLowerName(){
console.log(this.name.toLowerCase());
}
}
//exports에 SecondModule이라는 이름으로
//이 파일 내의 'MySecondModule' 클래스를 export한다.
exports.SecondModule = class MySecondModule extends BaseModule{
constructor(name){
super("SECOND");
this.name = name;
}
printUpperName(){
console.log(this.name.toUpperCase());
}
}
const { FirstModule, SecondModule } = require("./first_module")
//exports에 FirstModule이라고 적어놓았으니
//MyFirstModule이 아닌 FirstModule로 사용할 수 있다.
local_firstmodule = new FirstModule("my FIRST module");
//exports에 SecondModule이라고 적어놓았으니
//MySecondModule이 아닌 SecondModule로 사용할 수 있다.
local_secondmodule = new SecondModule("my SECOND module");
local_firstmodule.printModuleType(); //FIRST
local_firstmodule.printLowerName(); //my first module
local_secondmodule.printModuleType(); //SECOND
local_secondmodule.printUpperName(); //MY SECOND MODULE
const { FirstModule } = require("../first_module")
test('First Module', () => {
let modulename = "my FIRST module"
let test_firstmodule = new FirstModule(modulename);
expect(test_firstmodule.moduletype === "FIRST").toBe(true);
expect(test_firstmodule.moduletype === "SECOND").toBe(false);
expect(test_firstmodule.name === modulename).toBe(true);
});
//result : PASS
const { SecondModule } = require("../first_module")
test('Second Module', () => {
let modulename = "my SECOND module"
let test_secondmodule = new SecondModule(modulename);
expect(test_secondmodule.moduletype === "FIRST").toBe(false);
expect(test_secondmodule.moduletype === "SECOND").toBe(true);
expect(test_secondmodule.name === modulename).toBe(true);
});
//result : PASS