23. 모듈 (ESM)
한 파일에 모든 코드를 넣으면 충돌과 유지보수가 어렵습니다. 모듈은 파일별로 스코프를 격리하고 필요한 것만 노출합니다.
이 강의에서 배우는 것
- 1ES Modules의 `import`/`export`를 사용할 수 있다.
- 2named export와 default export를 구분한다.
- 3`<script type="module">`로 브라우저에서 모듈을 로드한다.
- 4re-export로 모듈을 묶어 제공한다.
소개
한 파일에 모든 코드를 넣으면 충돌과 유지보수가 어렵습니다. 모듈은 파일별로 스코프를 격리하고 필요한 것만 노출합니다.
핵심 개념
1. 왜 모듈인가
한 파일에 모든 코드를 넣으면 충돌과 유지보수가 어렵습니다. 모듈은 파일별로 스코프를 격리하고 필요한 것만 노출합니다.
2. export — named / default
// math.mjs
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export default function multiply(a, b) { return a * b; }named export는 이름 그대로, default는 한 모듈당 1개만 가능합니다.
3. import
import multiply, { PI, add } from './math.mjs';
import * as math from './math.mjs';
import { add as plus } from './math.mjs';default는 중괄호 없이, named는 중괄호 안에 적습니다.
4. 브라우저에서 사용
<script type="module" src="./02_import.mjs"></script>`type="module"`이 있어야 import 구문이 동작합니다. 모듈은 자동으로 `defer`되며 strict mode입니다.
5. re-export
한 진입점에서 여러 모듈을 묶어 제공할 때.
// index.mjs
export { add, PI } from './math.mjs';
export { format } from './format.mjs';핵심 예제
| 파일 | 다루는 내용 |
|---|---|
| 01_export.mjs | named & default export |
| 02_import.mjs | 01에서 import |
| 03_named_default.mjs | 두 방식 혼합 |
| 04_reexport.mjs | re-export 패턴 |
| index.html | `<script type="module">` |
`index.html`을 로컬 서버로 열어야 합니다 (`file://`은 CORS 문제).
src/01_export.mjs
/**
* named export와 default export 예시
*/
export const PI = 3.14159;
/**
* 두 수의 합
*/
export function add(a, b) {
return a + b;
}
/**
* 두 수의 곱 (default)
*/
export default function multiply(a, b) {
return a * b;
}
src/02_import.mjs
/**
* 01_export.mjs에서 가져와 사용
*/
import multiply, { PI, add } from './01_export.mjs';
console.log('PI:', PI);
console.log('1+2 =', add(1, 2));
console.log('3*4 =', multiply(3, 4));
src/03_named_default.mjs
/**
* named와 default를 함께 import / 별칭(alias) 사용
*/
import multiply, { add as plus, PI } from './01_export.mjs';
import * as math from './01_export.mjs';
console.log(plus(10, 20));
console.log(multiply(2, PI));
console.log('네임스페이스 사용:', math.add(5, 7));
src/04_reexport.mjs
/**
* re-export — 다른 모듈을 묶어서 다시 내보내기
* 외부에서는 이 파일 하나만 import 하면 됨
*/
export { add, PI } from './01_export.mjs';
export { default as multiply } from './01_export.mjs';
src/index.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>ESM 데모</title>
</head>
<body>
<h1>ESM 데모</h1>
<p>콘솔을 확인하세요.</p>
<script type="module" src="./02_import.mjs"></script>
</body>
</html>
자주 하는 실수
- `type="module"` 빼먹어서 `import`가 SyntaxError.
- default를 중괄호 `{ }`로 import.
- 상대경로에 `./` 누락 → 베어 모듈로 오인.
- 확장자 `.mjs`/`.js` 누락 (브라우저는 명시 필요).
- `file://`로 직접 열기 — 로컬 서버를 사용.
FAQ
Q1. `.js`와 `.mjs` 차이?
Node에서 `.mjs`는 ESM, `.js`는 package.json의 `type`에 따름. 브라우저는 `type="module"`만 보면 둘 다 OK. **Q2. CommonJS와 섞어 쓸 수 있나요?** A. Node에서 제한적으로 가능합니다. 24편에서 다룹니다. **Q3. 동적 import도 있나요?** A. `await import('./x.mjs')`로 런타임 로드 가능합니다.
과제
유틸 모듈을 만들어 HTML 페이지에서 사용하세요.
homework/README.md
## 과제 1 `utils.mjs`에 `formatPrice(n)` (예: 1000 → "1,000원"), `discount(price, percent)` 두 함수를 named export로 만들고, `main.mjs`에서 import 해 HTML 페이지에 출력하세요.
homework/answer/homework_01.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>유틸 모듈 데모</title>
</head>
<body>
<h1>가격 계산</h1>
<div id="out"></div>
<script type="module" src="./homework_01_main.mjs"></script>
</body>
</html>
homework/answer/homework_01_main.mjs
/**
* utils 모듈을 사용해 화면에 표시
*/
import { formatPrice, discount } from './homework_01_utils.mjs';
const price = 25000;
const sale = discount(price, 20);
const out = document.getElementById('out');
out.innerHTML = `
<p>정가: ${formatPrice(price)}</p>
<p>할인가(20%): ${formatPrice(sale)}</p>
`;
homework/answer/homework_01_utils.mjs
/**
* 가격을 천단위 콤마와 '원'으로 포맷
*/
export function formatPrice(n) {
return `${n.toLocaleString('ko-KR')}원`;
}
/**
* 할인가 계산
*/
export function discount(price, percent) {
return Math.floor(price * (1 - percent / 100));
}
다음 단원
[24_Node_환경](../../07_Node_입문/24_Node_환경/)