JavaScript 모듈 시스템
CommonJS, ES Modules, AMD의 차이점과 번들러가 처리하는 과정을 학습합니다
모듈 시스템 선택
모듈 구조
main.js
// ES Modules
import { process } from './utils.js';
import { fetchData } from './api.js';
function main() {
const data = fetchData();
const result = process(data);
console.log(result);
}
export default main;Import/Export 문법 비교
Export 문법
CommonJS
단일 내보내기
module.exports = myFunction;여러 내보내기
module.exports = { func1, func2 };개별 추가
exports.myFunc = myFunction;ESM
기본 내보내기
export default myFunction;명명된 내보내기
export { func1, func2 };인라인 내보내기
export const myFunc = () => {};AMD
객체 반환
return { func1, func2 };함수 반환
return myFunction;의존성과 함께
define(['dep'], function(dep) { return {}; });Import 문법
CommonJS
전체 가져오기
const module = require('./module');구조 분해
const { func1, func2 } = require('./module');조건부 가져오기
if (condition) require('./module');ESM
기본 가져오기
import module from './module.js';명명된 가져오기
import { func1, func2 } from './module.js';동적 가져오기
const module = await import('./module.js');AMD
의존성 정의
require(['module'], function(module) {});설정과 함께
requirejs.config({ paths: {} });조건부 로드
require(['module'], callback, errorCallback);동적 Import
ES Modules (권장)
// 조건부 로딩
if (condition) {
const module = await import('./heavy-module.js');
module.doSomething();
}
// 지연 로딩
button.addEventListener('click', async () => {
const { Modal } = await import('./modal.js');
new Modal().show();
});
// 경로 동적 결정
const locale = getUserLocale();
const messages = await import(`./i18n/${locale}.js`);번들러 최적화
Code Splitting
동적 import는 번들러가 코드를 분할하는 지점이 됩니다.
- • Webpack: 자동으로 청크 생성
- • Vite: ES 모듈 기반 최적화
- • Rollup: 수동 청크 설정 가능
순환 참조 (Circular Dependencies)
moduleA.js
// moduleA.js
import { b } from './moduleB.js';
console.log('moduleA loading');
export const a = 'A';
console.log('moduleB.b:', b); // ReferenceError!moduleB.js
// moduleB.js
import { a } from './moduleA.js';
console.log('moduleB loading');
export const b = 'B';
console.log('moduleA.a:', a);⚠️ 순환 참조 문제
- • CommonJS: 부분적으로 내보낸 객체 반환 (undefined 가능)
- • ES Modules: ReferenceError 발생 가능
- • AMD: 의존성 해결 실패
해결책: 의존성 구조 재설계, 지연 로딩, 또는 의존성 주입 패턴 사용
모듈 시스템 상세 비교
| 특징 | CommonJS | ES Modules | AMD |
|---|---|---|---|
| 로딩 방식 | 동기적 | 비동기적 | 비동기적 |
| 주 사용처 | Node.js | 브라우저 & Node.js | 브라우저 (레거시) |
| Tree Shaking | ❌ 불가능 | ✅ 가능 | ❌ 불가능 |
| 정적 분석 | △ 제한적 | ✅ 가능 | △ 제한적 |
| 순환 참조 | 부분 지원 | 에러 발생 | 처리 어려움 |
| 브라우저 지원 | 번들러 필요 | 모던 브라우저 | 라이브러리 필요 |
CommonJS → ES Modules 마이그레이션
❌ Before (CommonJS)
const fs = require('fs');
const { readFile } = require('fs/promises');
class MyClass {
// ...
}
module.exports = MyClass;
module.exports.helper = () => {};✅ After (ES Modules)
import fs from 'fs';
import { readFile } from 'fs/promises';
export default class MyClass {
// ...
}
export const helper = () => {};💡 마이그레이션 팁
- • package.json에 "type": "module" 추가
- • .js 확장자를 명시적으로 포함
- • __dirname, __filename 대신 import.meta.url 사용
- • require() 대신 동적 import() 사용
🧑💻
1nnovator 김민성
JavaScript 학습 센터 개발자
🌟 이 프로젝트가 도움이 되셨다면 블로그에서 더 많은 개발 이야기를 확인해보세요!
🤖이 페이지는 생성형 AI의 도움을 받아 제작되었습니다.