"어떤 환경에서 개발하느냐" 와 "호환성과 미래지향성 중 어디에 우선순위를 두느냐"에 달려 있습니다.
상황 |
추천 방식 |
최신 Node.js 프로젝트 (14버전 이상) |
ES Module (미래지향적) |
프론트엔드 (브라우저) 개발 |
ES Module (표준) |
기존 Node.js 라이브러리와 호환이 필요 |
CommonJS |
빠르게 작성하고 실행해야 할 스크립트 |
CommonJS (간편함) |
장점 |
설명 |
표준화된 방식 |
모든 브라우저와 Node.js에서 공식적으로 지원 |
트리 셰이킹(최적화) 가능 |
불필요한 코드 제거 가능 (프론트엔드에서 중요) |
비동기 로딩 가능 |
import() 와 top-level await 지원 |
미래 지향적 |
Node.js와 모든 모던 JS 도구들이 ESM으로 전환 중 |
이유 |
설명 |
많은 기존 패키지가 CommonJS로 작성됨 |
require() 기반의 코드와 잘 호환됨 |
일부 빌드 도구는 CommonJS에 더 친화적 |
예: ts-node , 일부 Jest 설정 등 |
ESM 설정이 복잡하거나 번거로운 경우 |
package.json 에 "type": "module" 설정 필요 등 |
- 새 프로젝트라면 → 무조건 ES Module
- 기존 프로젝트 유지보수라면 → CommonJS 유지, 필요시 점진적 마이그레이션
- 라이브러리 개발이라면 → 둘 다 지원하는 방식 (dual module) 고려
항목 |
CommonJS (require ) |
ES Module (import ) |
문법 |
require , module.exports |
import , export |
기본 방식 |
Node.js (기본) |
브라우저 / 최신 Node.js |
비동기 로딩 |
❌ 불가능 |
✅ 가능 (top-level await ) |
동적 import |
제한적 (require ) |
✅ import() 가능 |
파일 확장자 |
.js |
.mjs 또는 "type":"module" 설정 필요 |
CommonJS |
ES Module |
const x = require('x') |
import x from 'x' |
module.exports = foo |
export default foo |
exports.foo = bar |
export const foo = bar |
기존 package.json에 type:module을 추가한다.
{
"type": "module"
}
{
"name": "my-test",
"version": "1.0.0",
"description": "",
"type": "module",
..........
}
// legacy.js
const fs = require('fs');
module.exports = { fs };
// modern.mjs
import fs from 'fs';
export { fs };