자바스크립트 파일 확장자 차이
.js파일이 CommonJS인지 ESM인지 Node.js는 어떻게 판단할까요?
JavaScript에는 두 가지 모듈 시스템(CommonJS, ESM)이 공존하고, .js 파일만으로는 어느 쪽인지 알 수 없습니다. 이 모호함을 해결하기 위해 .cjs와 .mjs 확장자가 도입되었습니다.
왜 확장자가 여러 개인가?
JavaScript는 원래 모듈 시스템이 없었습니다. Node.js가 등장하면서 CommonJS(CJS) 모듈 시스템(require/module.exports)이 사실상 표준이 되었고, 이후 ECMAScript 표준으로 ES Modules(ESM) (import/export)이 정의되었습니다.
문제는 두 모듈 시스템의 문법과 동작 방식이 다르다는 것입니다. Node.js는 .js 파일을 만났을 때 이것이 CommonJS인지 ESM인지 판단해야 합니다. 이 모호함을 해결하기 위해 ** 명시적인 확장자 **가 도입되었습니다.
확장자별 비교
| 항목 | .js | .cjs | .mjs |
|---|---|---|---|
| ** 모듈 시스템** | package.json의 type 필드에 따라 결정 | 항상 CommonJS | 항상 ES Modules |
| ** 문법** | 환경에 따라 다름 | require() / module.exports | import / export |
| ** 로딩 방식** | 환경에 따라 다름 | 동기 로딩 | 비동기 로딩 |
| ** 사용 환경** | 브라우저, Node.js 등 범용 | 주로 Node.js 서버 | Node.js, 모던 브라우저 |
package.json의 type 필드
Node.js에서 .js 파일의 모듈 시스템은 가장 가까운 상위 디렉터리의 package.json에 있는 type 필드로 결정됩니다.
{
"type": "module"
}
type 값 | .js 파일의 해석 |
|---|---|
"module" | ES Modules로 해석 |
"commonjs" (기본값) | CommonJS로 해석 |
** 핵심 **:
.cjs와.mjs는type필드와 무관하게 항상 각각의 모듈 시스템으로 해석됩니다. 프로젝트의type설정과 다른 모듈 시스템을 사용해야 하는 파일이 있을 때 유용합니다.
언제 어떤 확장자를 사용하는가?
.js - 기본 선택
대부분의 경우 .js를 사용하고, package.json의 type 필드로 모듈 시스템을 지정합니다.
.mjs - ESM 프로젝트 내 명시적 ESM 파일
type이"commonjs"인 프로젝트에서 일부 파일만 ESM으로 작성하고 싶을 때- 모듈 시스템을 파일 수준에서 명확히 하고 싶을 때
.cjs - ESM 프로젝트 내 CommonJS 파일
type이"module"인 프로젝트에서 일부 파일만 CommonJS로 작성해야 할 때- 대표적인 예: ESM 프로젝트의 설정 파일 (
jest.config.cjs,eslint.config.cjs등)
실무 예시
// math.mjs - ES Modules
export function add(a, b) {
return a + b;
}
// math.cjs - CommonJS
function add(a, b) {
return a + b;
}
module.exports = { add };
// app.mjs - ESM에서 import
import { add } from './math.mjs';
console.log(add(1, 2)); // 3
// app.cjs - CommonJS에서 require
const { add } = require('./math.cjs');
console.log(add(1, 2)); // 3
** 주의 **: CommonJS에서 ESM 모듈을
require()로 직접 불러올 수 없습니다. 동적import()를 사용해야 합니다.