[Javascript] ES Modules (import / export)
JS ES Modules, ESM, import export, ES6 module
정의
ES Modules (ESM) 은 표준 JS 모듈 시스템 (ES6). import / export 키워드. 브라우저와 Node.js 16+ 모두 지원.
export
named export
// utils.js
export const PI = 3.14;
export function add(a, b) { return a + b; }
export class Calculator { ... }
// 일괄 export
const NAME = 'lib';
function foo() {}
export { NAME, foo };
// rename
export { foo as bar };
default export
// math.js
export default function add(a, b) { return a + b; }
// 또는
function add(a, b) { return a + b; }
export default add;
한 파일에 default 1 개, named 여러 개 가능.
import
named import
import { PI, add } from './utils.js';
import { add as plus } from './utils.js'; // rename
import { PI, add, default as Lib } from './lib.js'; // default + named
default import
import add from './math.js'; // 어떤 이름이든
import myAdd from './math.js'; // 같음
namespace import
import * as utils from './utils.js';
utils.PI;
utils.add(1, 2);
side-effect only
import './polyfill.js'; // 실행만, export 안 가져옴
재-export
// index.js (barrel pattern)
export { foo } from './foo.js';
export { bar } from './bar.js';
export * from './baz.js';
export { default } from './main.js';
여러 모듈을 한 진입점으로.
dynamic import
const module = await import('./heavy.js');
module.someFunction();
// 또는 then
import('./heavy.js').then(m => m.use());
조건부 / 지연 로딩에 유용. 코드 스플리팅의 핵심.
ESM 의 특징
1. strict mode 자동
// 모든 ESM 은 자동으로 strict
'use strict'; // 불필요
2. top-level await (ES2022)
// ESM 에서만
const data = await fetch('/api/init').then(r => r.json());
export { data };
3. import.meta
console.log(import.meta.url); // 모듈의 URL
// 브라우저, Node 둘 다
ESM vs CommonJS
| 항목 | ESM | CommonJS |
|---|---|---|
| 문법 | import/export | require/module.exports |
| 동기/비동기 | async (static), top-level await | 동기 |
| 정적 분석 | ✓ (tree-shaking) | ✗ |
| 호이스팅 | export 가 호이스팅 | 즉시 평가 |
| 브라우저 | ✓ | ✗ (번들러 필요) |
| Node 파일 확장자 | .mjs 또는 "type": "module" | .cjs 또는 기본 |
Node.js 에서 ESM 사용
// package.json
{
"type": "module"
}
또는 파일 확장자 .mjs.
함정
1. import 경로의 확장자
// 브라우저
import { x } from './foo'; // ❌ 확장자 필요
import { x } from './foo.js'; // ✓
// Node ESM
import { x } from './foo.js'; // ✓ (필수)
// 번들러 (Vite, webpack)
import { x } from './foo'; // 알아서 해결
플랫폼별 차이.
2. import 가 호이스팅됨
foo();
import { foo } from './foo.js'; // ✓ 동작 (호이스팅)
3. import 의 immutable binding
import { count } from './module.js';
count = 5; // ❌ TypeError (immutable)
import 한 변수는 상수 binding. 모듈에서 변경하면 import 한 곳에서도 반영.
4. circular dependency
// a.js
import { b } from './b.js';
export const a = 1;
// b.js
import { a } from './a.js';
export const b = a + 1; // a 가 아직 정의 전일 수도
순환 import 시 부분 평가된 모듈이 노출. 가능하면 피한다.
5. CJS interop
// Node ESM 에서 CJS import
import lodash from 'lodash'; // default 만 가능
import { merge } from 'lodash'; // ⚠️ 일부 라이브러리는 안 됨
tree-shaking
// 좋은 import (필요한 것만)
import { partition } from 'lodash-es';
// 안 좋음 (전체)
import _ from 'lodash';
_.partition(...);
ESM 만 tree-shaking 가능. CJS 는 정적 분석 어려움.
💬 댓글