module system

처음에는 작은 스크립트로 모듈로 분할하는 메커니즘 같은건 필요 없었다. 하지만 점점 자바스크립트 개발의 규모가 커짐으로 인해 네임 스페이스와 종속성 처리 문제가 발생하였고, 이에 대한 해결책으로 여러 모듈 시스템이 등장하게 되었다.

CommonJS 모듈

모듈을 'require'를 통해 가져오며(import), 'module.exports'를 통해 내보낸다(export)

자바스크립트 모듈을 손쉽게 결합하기 위해 가장 널리 사용되는 방식이며, CommonJS 모듈의 주요 개념은 require라는 함수에 있다. 종속성 모듈 이름과 함께 이 함수를 호출하면 해당 모듈을 불러오고 모둘의 인터페이스가 반환된다.

이 로더는 해당 모듈의 모든 코드를 함수로 래핑하기 때문에 모듈은 자동으로 자체 로컬 범위를 갖는다. 모듈에는 종속성에 접근하기 위해 require를 호출하고 exports에 바인딩된 객체에 모듈의 인터페이스를 배치하기만 하면 된다.

// 아주 간단한 형태의 require 정의하기
require.cache = Object.create(null);

function require(name) {
  if(!(name in require.cache)) {
    let code = readFile(name);
    let module = { exports: {} };
    require.cache[name] = module;
    
    let wrapper = Function("require, exprots, module", code);
    wrapper(require, module.exports, module);
  }
  return require.cache[name].exports;
}

CommonJS 모듈의 독특한 점은 이 모듈 시스템에서 빈 인터페이스 객체(exports에 바인딩)가 생성되지만, module.exports를 덮어써 값을 바꿀 수 있다는 점이다. 많은 모듈에서 인터페이스 객체 대신 단일 값을 내보내기 위해 이러한 방식을 많이 사용한다.

또한 중요한 점은 import 시에 object가 생기기 때문에 트리 쉐이킹이 되지 않는다.

ECMAScript 모듈(= 하모니, Harmony)

모듈을 'import'를 통해 가져오고, 'export'를 통해 내보낸다.

CommonJS 모듈의 경우 표기법이 조금 어색하며, require는 문자열 리터럴뿐만 아니라 어떤 종류의 인수도 받을 수 있는 일반 함수 호출이므로 코드를 실행하지 않고 모듈의 종속성을 결정하기는 어려울 수 있다. 이러한 이유로 2015년의 자바스크립트 표준에 다른 모듈 시스템이 도입됐다(ES6).

종속성에 접근하기 위해 함수를 호출하는 대신 import 키워드를 사용한다. 마찬가지로 export 키워드는 내보내는데 사용된다. 함수나 클래스, 바인딩 정의(let, const, var) 앞에 사용할 수 있다.

ES 모듈의 인터페이스는 단일 값이 아니라 명명된 바인딩의 집합이다. 내보내는 모듈에서는 언제든지 바인딩 값을 변경할 수 있으며, 이를 가져오는 모듈에서는 새 값을 바라보게 된다. default라는 바인딩이 있다면 모듈의 기본 내보내기 값으로 처리된다.

export default ["A", "B", "C"];

다른 중요한 차이점은 모듈의 스크립트가 실행되기 전에 ES 모듈 가져오기(import)가 발생한다는 것이다. import 선언은 함수나 블록 안에 작성하지 않고, 종속성 이름은 임의의 표현식이 아닌 따옴표로 묶인 문자열이어야 한다.

또한 중요한 점은 컴파일 타임에 importexport를 결정할 수 있는 정적 분석을 지원하므로, 정적 분석을 통한 트리 쉐이킹이 지원된다.

Last updated