SHIN
Node.js 실전 팁 20선
20편Node.js는 두 가지 모듈 시스템을 지원합니다. 각 시스템의 특징을 이해하고 올바르게 선택해야 합니다.
| 구분 | CommonJS (CJS) | ES Modules (ESM) |
|---|---|---|
| 문법 | require/module.exports | import/export |
| 로딩 | 동기, 런타임 | 정적, 컴파일 타임 |
| 확장자 | .js, .cjs | .mjs, 또는 package.json type:module |
| __dirname | 기본 제공 | 별도 구현 필요 |
| Tree-shaking | 어려움 | 가능 |
| Top-level await | 불가 | 가능 |
{
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
}
}// ESM에서는 __dirname, __filename이 없음
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const configPath = join(__dirname, '../config/app.json');// ESM에서 동적 로드 (CJS require와 비슷)
const module = await import('./plugin.js');
// 조건부 로드
async function loadConfig() {
if (process.env.NODE_ENV === 'test') {
return (await import('./config.test.js')).default;
}
return (await import('./config.js')).default;
}// CJS에서 ESM 패키지를 동기적으로 require할 수 없음
// ❌ const esm = require('pure-esm-package'); // 에러!
// ✅ 동적 import 사용
async function main() {
const { default: chalk } = await import('chalk');
console.log(chalk.green('Hello!'));
}// tsconfig.json
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2022"
}
}// TypeScript ESM에서 반드시 확장자 포함
import { helper } from './utils/helper.js'; // .ts가 아닌 .js"type": "module")tsup, unbuild 활용)# tsup으로 CJS + ESM 동시 빌드
tsup src/index.ts --format cjs,esm --dts