LLM과 토스 기술블로그 https://toss.tech/article/commonjs-esm-exports-field 포스팅을 활용하여 작성했다.
CommonJS(CJS)와 ECMAScript Modules(ESM) 요약 및 비교
CJS와 ESM은 자바스크립트에서 모듈을 관리하고 사용하는 두 가지 시스템입니다. 각각의 시스템은 서로 다른 장점과 단점을 지니고 있으며, 이로 인해 서로 호환되지 않는 경우가 많습니다.
CJS와 ESM의 비교
특성 | CommonJS (CJS) | ECMAScript Modules (ESM) |
---|---|---|
문법 | require / module.exports | import / export |
로딩 방식 | 동기적 | 비동기적 |
Top-level await 지원 여부 | 지원하지 않음 | 지원함 |
Tree shaking 가능 여부 | 어렵다 | 가능 |
파일 확장자 | .js, .cjs | .js, .mjs |
TypeScript 검사 규칙 | type: "commonjs"기본 | type: "module" |
CJS와 ESM의 상세 비교
- 문법
CJS는 모듈을 정의하고 가져오기 위해 require와 module.exports를 사용합니다.
ESM은 import와 export 문법을 사용합니다. - 로딩 방식
CJS는 모듈을 동기적으로 로드합니다. 이는 종종 서버 환경에서 유용합니다.
ESM은 비동기적으로 모듈을 로드하여 더 효율적인 코드 실행을 가능하게 합니다. - Top-level await
Top-level await은 ESM에서 지원되며, 비동기 코드 작성을 더 간결하게 만들어 줍니다.
CJS는 이를 지원하지 않아서 비동기 작업을 포함하면 특정 함수 내에서만 사용할 수 있습니다. - Tree shaking
CJS는 동적으로 모듈을 로딩하기 때문에 Tree-shaking(필요 없는 코드를 제거하는 과정)이 어렵습니다.
ESM은 정적 구조를 갖고 있어 빌드 타임에 모듈 간의 의존성을 쉽게 분석하고, 불필요한 코드를 제거할 수 있습니다. - 파일 확장자
CJS는 기본적으로 .js와 .cjs 파일을 사용합니다.
ESM은 일반적으로 .js와 .mjs 확장자를 사용하여 구분합니다. - TypeScript 지원
CJS 패키지는 type: "commonjs"가 설정되어야 하며 .ts 파일을 CJS로 해석합니다.
ESM 패키지는 type: "module"이 설정되어야 하며 .ts 파일을 ESM으로 해석합니다.
Top-level Await
Top-level await은 ESM에서 새롭게 도입된 기능으로, 비동기 작업을 모듈의 최상위 수준에서 직접 다룰 수 있게 해줍니다. 주요 특징은 다음과 같습니다:
- 비동기 코드의 간결함: CJS에서는 비동기 코드를 작성하기 위해 함수를 정의해야 했던 반면, ESM에서는 필요할 때마다 최상위에서 비동기 작업을 쉽게 작성할 수 있습니다.
- Promise 사용: Top-level await는 Promise를 기본으로 하며, 이로 인해 더 직관적인 비동기 코드 작성을 가능하게 합니다. 이는 특히 코드가 복잡해지는 경우 유용합니다.
- 호환성: Top-level await은 ESM 환경에서만 사용 가능하므로 CJS와의 호환성 문제를 일으킵니다.
예시:
// ESM에서의 Top-level await 사용 예시
const data = await fetchData(); // fetchData()는 Promise를 반환하는 함수
console.log(data);
결론
CJS와 ESM은 자바스크립트 모듈 관리의 중요한 부분이며, 각 모듈 시스템이 제공하는 기능을 활용하여 다양한 상황에 맞는 코드를 작성할 수 있습니다. Top-level await은 ESM에서 제공되는 유용한 기능으로, 비동기 작업을 더 간결하고 직관적으로 처리할 수 있게 해줍니다.
Node.js 와 브라우저 환경
각각의 환경인 Node.js와 브라우저에서 CommonJS(CJS)와 ECMAScript Modules(ESM)의 동작 방식과 특징을 설명하겠습니다.
Node.js 환경
- CommonJS (CJS)
- 모듈 시스템: Node.js의 기본 모듈 시스템으로, 서버 측에서 동작합니다.
- 동기적 로딩: 모듈을 동기적으로 로드하며, 모든 모듈이 로드될 때까지 실행이 중단됩니다.
- 파일 확장자: .js와 .cjs 확장자를 사용합니다.
예시:
// math.js
function add(a, b) {
return a + b;
}
module.exports = add;
// app.js
const add = require('./math.js');
console.log(add(2, 3)); // 5
- ECMAScript Modules (ESM)
- 모듈 시스템: Node.js 12 버전부터 지원되며, ES6 문법을 사용합니다.
- 비동기적 로딩: ESM은 비동기적으로 모듈을 로드하여 더 효율적인 코드 실행이 가능합니다.
- 파일 확장자: .js와 .mjs 확장자를 사용하며, package.json에서 "type": "module"을 설정하여 ESM을 사용할 수 있습니다.
예시:
// math.mjs
export function add(a, b) {
return a + b;
}
// app.mjs
import { add } from './math.mjs';
console.log(add(2, 3)); // 5
브라우저 환경
- CommonJS (CJS)
- 지원 부족: 브라우저는 기본적으로 CJS를 지원하지 않습니다. 브라우저에서 CJS 모듈을 사용하려면 Webpack, Browserify 등의 번들러를 사용해야 합니다.
- 동기적 로딩: CJS는 동기적으로 모듈을 로드하기 때문에 브라우저 환경에서는 성능 저하를 일으킬 수 있습니다.
예시: (번들러를 사용해야 함)
- ECMAScript Modules (ESM)
- 모듈 시스템: 브라우저에서 기본적으로 지원되는 모듈 시스템입니다.