Javascript - module

2020. 10. 28. 22:06javascript&typescript

CommonJS & AMD(Asynchronous Module Definition)

옛날 html에서 주로 보조적인 용도로 많이 사용했었던 javascript는 module에 대한 개념이 크게 활성화되지 않았었습니다. html에서 다른 사람이 작성하거나 분리되어 작성된 모듈을 사용하려면, script상단에서 사용할 모듈을 불러오고 window 프로퍼티로 저장된 모듈을 내 스크립트에서 사용해야 했습니다. 하지만 문제는 모든 모듈이 window 객체에 전역으로 등록되다 보니 변수명이 같으면 충돌이 일어나서 일일이 다 체크해야 했습니다.

<script src="jquery.js"></script>
<script src="zepto.js"></script>
<script>
window.$ // ???
</script>

그래서 이러한 문제들을 해결하기 위해서 다양한 모듈 라이브러리들이 만들어졌고 대표적으로 CommonJS와 AMD가 있습니다. 현재 Node에서는 CommonJS와 ES6 module 방식을 사용하고 있습니다. AMD는 제가 사용해보지 못했고 주로 nodejs 환경에서 작업했기 때문에 AMD는 따로 살펴보고, CommonJS예시와 기존 moduel library와 ES6 module의 차이에 대해서 자세히 알아보도록 하겠습니다.

// * CommonJS modules
module.exports.VALUE = 1234;
module.exports.SUM = function(val1, val2) { return val1 + val2; }
module.exports.TWICE = function(val) { return val * 2; }

function POW(val) { return val*val; }
module.exports.POW = POW;

// * Using commonjs modules
const math = require('math.js');
console.log(math.SUM(1, 2));    // 3
console.log(POW(2));        // 4

ESModule - import/export

TL;DR
esmodule은 es6부터 추가되었고, 기존에 module.exports 오브젝트에 모두 다 집어넣는 단순한 구조와 다르게, named export/default export/aliasing 등 다양한 기능을 제공한다.

ES6부터는 javascript 언어 내부적으로 모듈 의존성을 지원합니다. import/export/default 의 모듈 관리 키워드를 통해서 정적 또는 동적으로도 모듈 의존성을 작성할 수 있습니다.

// * named exporting ------
const VALUE = 1234;
const SUM = function(val1, val2) {
  return val1 + val2;
};

export {VALUE , SUM}

export const TWICE = function(val) { return val*2; }
export function POW(val) { return val*val; }

// * default exporting ------
export default function defaultFunction() {
  console.log('this is default function');
}
// * Named importing ------
import {VALUE, SUM} from 'math.js';
import {TWICE} from 'math.js';
console.log(VALUE);        // 1234
console.log(SUM(1, 2));        // 3
console.log(TWICE(2));        // 4

// * Modules object importing ------
import * as mathFunctions from 'math.js';
mathFunctions.TWICE(2);        // 4
mathFunctions.POW(2);        // 4

// * Default importing ------
import DefaultFunction from 'math.js';
DefaultFunction();        // 'this is default function'

ES6 모듈과 기존 module library의 큰 차이점은 named export라고 볼 수 있습니다. 기존에는 module의 기능들을 하나의 오브젝트로 묶어서 exporting 하거나 importing 해야 했다면, ES6 모듈은 기존처럼 비슷하게 default export로 묶을 수도 있고, named export를 통해서 부분적으로 필요한 기능들만 가져가서 사용할 수 있도록 할 수 있습니다.

ES6에서의 모듈 의존성 표현은 기존의 CommonJS 및 AMD 모듈과 상호 운용도 가능합니다. CommonJS 모듈을 ES6 module로 호환하여 사용할 때, CommonJS의 모듈은 ES6 module의 default exporting으로 등록됩니다. 그렇기 때문에 ES6 module의 named importing은 사용하지 못하고, 아래 예시처럼 default importing만 사용할 수 있습니다.

// * commonjs modules ------
module.exports.SUM = function(val1, val2) { return val1 + val2; };

module.exports.TWICE = function(val) { return val*2; };

module.exports.POW(val) { return val*val; };
// * commonjs modules importing ------

// import MATH from 'math.js';        -> ERROR
// import {SUM, POW} from 'math.js';  -> ERROR
// CommonJS does not support named exports.
// CommonJS modules can be imported by importing the default export

import {default as MathFunctions} from 'math.js';
console.log(MathFunctions.SUM(1, 2));
console.log(MathFunctions.TWICE(2));

import * as MATH from 'math.js';
console.log(MATH.default.SUM(1, 2));
console.log(MATH.default.TWICE(2));

# Tree shaking

Tree shaking은 사용하지 않는 소스코드를 제거하여 소스코드의 양을 줄이는 방법을 말합니다. webpack과 같은 번들러로 번들링 결과물을 만들 때, named export/import를 통해서 ES6 module로부터 사용하는 소스와 사용하지 않는 소스 코드를 파악하여 사용하지 않는 소스코드를 제거하여 번들링함으로써 결과물의 크기를 줄여 성능을 향상할 수 있습니다. named export/import가 필요하기 때문에 기존의 module library로는 불가능합니다.

 

Tree shaking에 대해서는 다음 기회에 이야기해보도록 하겠습니다.

'javascript&typescript' 카테고리의 다른 글

Javascript - Iterator pattern  (0) 2020.10.28