exports/require에 관하여

FGPRJS·2021년 11월 2일
0
post-custom-banner

기본적으로 JS는 약간의 상호작용만을 위해 만들어진 간단한 언어이므로 큰 스크립트가 필요하지 않았으나, 프로젝트가 커지면서 모듈 분할을 위한 메커니즘이 필요하게 되었다.

따라서 모던JS에서는 모듈화를 위한 import/export 기능을 제공한다. 이것을 통한 모듈을 ESM(Ecma Script Module)이라고 한다.


ESM과 CJS에서의 모듈

하지만 NodeJS가 사용하는 CommonJS(CJS)에서 사용하는 모듈 생태계를 위한 시스템은 이미 존재하고 있었으며 이것은 requireexportsimport/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을 기본으로 보는 것이 현재로서는 조금 더 옳다.


CJS에서의 exports와 require

다음과 같이 사용한다.

  • 사용할 모듈 (first_module.js)
//모듈들의 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());
    }
}
  • 만든 모듈 사용하기(main.js)
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
  • JEST로 만든 모듈 테스트해보기 (first_module.test.js)
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
  • JEST로 만든 모듈 테스트해보기 (second_module.test.js)
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
profile
FGPRJS
post-custom-banner

0개의 댓글