ESM는 ES6부터 지원하고 있는 JavaScript의 표준 모듈 시스템입니다.
ES6 이전에는 브라우저 환경에서 사용할 수 있는 표준 모델 시스템이 없었습니다.
필요한 파일(모듈)을 생성하여 <script src="script.js"></script>
의 형태로 파일을 직접 불러오는 방법을 사용했습니다.
자바스크립트로 작성된 외부 라이브러리를 사용하기 위해서 공식적으로 배포하는 스크립트 파일을 다운로드한 후 함께 묶어서 배포하거나 CDN으로 제공되는 주소를 이용하는 방법을 주로 사용했습니다.
하지만 <script>
만으로는 복잡한 시스템에서 많은 파일(모듈)들을 효율적으로 관리하기 어렵기 때문에 이를 해결하기 위한 모듈 시스템이 필요했습니다.
CommonJS, AMD 등 여러 모듈 시스템이 등장했지만 JavaScript 자체에서 모듈 시스템을 지원해야한다는 필요성이 높아졌습니다.
그래서 ES6 사양에서 JavaScript의 표준 모듈 시스템이 명세되었고, 이를 ESM(ECMAScript Module, ES Module) 이라고 부릅니다.
현재 모듈에서 다른 모듈을 사용할 때 import
, 현재 모듈에서 다른 모듈로 내보낼 때는 export
를 사용합니다.
export
export
에는 default export
(기본 내보내기)와 named export
(유명 내보내기)가 있습니다.
named export
export
구문을 통해 내보낼 수 있고, 하나의 모듈 내에서 named export
는 여러 개 존재할 수 있습니다. 따라서 여러 값을 내보낼 때 유용합니다.
// module.js
const myVariable = 'My variable';
const myFunction = () => {
console.log('My Function');
}
export { myVariable, myFunction };
// main.js
import { myVariable, myFunction } from './module.js'
console.log(myVariable);
myFunction();
named export
를 import
할 때 내보낸 이름과 동일한 이름을 사용해야 하고, 가져올 모듈을 중괄호 안에 작성합니다.
default export
default export
는 export default
구문을 통해 내보내기를 하고, 하나의 모듈 내에 하나만 존재할 수 있습니다.
import
할 때 중괄호 없이 가져올 모듈의 이름을 통해 가져옵니다.
또한, default export
는 내보낸 이름과 다른 이름으로 가져올 수 있습니다.
// module.js
const myVariable = 'My variable';
const myFunction = () => {
console.log('My Function');
}
export { myVariable, myFunction };
// main.js
import { myVariable, myFunction } from './module.js'
console.log(myVariable);
myFunction();
export from
export from
은 import
와 export
를 한 번에 처리할 수 있는 문법으로 이를 이용하여 가져온 모듈을 다시 내보낼 수 있습니다.
주로 패키지의 다른 모듈들을 한 번에 모아 일관된 형태로 내보내거나 관리하고 싶을 경우 사용합니다.
// 가져와서 내보내기
export myVariable from "myVariable.js";
// import, export를 이용하여 가져와서 내보내기
import myVariable from 'myVariable.js';
export myVariable;
import
/export
하기import
또는 export
하는 모듈 이름 뒤에 as
를 붙여 다른 이름으로 import
/export
를 할 수 있습니다.
// 다른 이름으로 export 하기
const myVariable = 'My variable';
export { myVariable as variable }; // myVariable를 variable 라는 이름으로 export
// 다른 이름으로 import 하기
import { myVariable as variable } from 'myVariable.js'; // myVariable를 variable 라는 이름으로 import
동기/비동기 로드를 모두 지원하고 문법이 간단합니다.
그리고 실제 객체/함수를 바인딩하기 때문에 순환 참조 관리가 편합니다.
CommonJS는 require
/module.exports
를 동적으로 동작할 수 있습니다.
따라서, 빌드타임에 정적 분석을 적용하기 어렵고 런타임에 모듈 관계를 파악할 수 있습니다.
// require
const utilName = /* 동적인 값 */
const util = require(`./utils/${utilName}`);
// module.exports
function foo() {
if (/* 동적인 조건 */) {
module.exports = /* ... */;
}
}
foo();
ESM은 정적 구조를 가지며 모듈끼리 의존하도록 강제합니다. import path
에 동적인 값을 사용할 수 없고, export
는 항상 최상위 스코프에서만 사용할 수 있습니다.
import util from `./utils/${utilName}.js`; // 불가능
import { add } from "./utils/math.js"; // 가능
function foo() {
export const value = "foo"; // 불가능
}
export const value = "foo"; // 가능
따라서 ESM은 빌드 단계에서 정적 분석을 통해 모듈 간의 의존 관계를 파악할 수 있고, 트리쉐이킹을 쉽게 할 수 있습니다.
참고