.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.jsontype 필드에 따라 결정항상 CommonJS항상 ES Modules
** 문법**환경에 따라 다름require() / module.exportsimport / export
** 로딩 방식**환경에 따라 다름동기 로딩비동기 로딩
** 사용 환경**브라우저, Node.js 등 범용주로 Node.js 서버Node.js, 모던 브라우저

package.jsontype 필드

Node.js에서 .js 파일의 모듈 시스템은 가장 가까운 상위 디렉터리의 package.json에 있는 type 필드로 결정됩니다.

JSON
{
  "type": "module"
}
type.js 파일의 해석
"module"ES Modules로 해석
"commonjs" (기본값)CommonJS로 해석

** 핵심 **: .cjs.mjstype 필드와 무관하게 항상 각각의 모듈 시스템으로 해석됩니다. 프로젝트의 type 설정과 다른 모듈 시스템을 사용해야 하는 파일이 있을 때 유용합니다.


언제 어떤 확장자를 사용하는가?

.js - 기본 선택

대부분의 경우 .js를 사용하고, package.jsontype 필드로 모듈 시스템을 지정합니다.

.mjs - ESM 프로젝트 내 명시적 ESM 파일

  • type"commonjs"인 프로젝트에서 일부 파일만 ESM으로 작성하고 싶을 때
  • 모듈 시스템을 파일 수준에서 명확히 하고 싶을 때

.cjs - ESM 프로젝트 내 CommonJS 파일

  • type"module"인 프로젝트에서 일부 파일만 CommonJS로 작성해야 할 때
  • 대표적인 예: ESM 프로젝트의 설정 파일 (jest.config.cjs, eslint.config.cjs 등)

실무 예시

JAVASCRIPT
// math.mjs - ES Modules
export function add(a, b) {
  return a + b;
}
JAVASCRIPT
// math.cjs - CommonJS
function add(a, b) {
  return a + b;
}
module.exports = { add };
JAVASCRIPT
// app.mjs - ESM에서 import
import { add } from './math.mjs';
console.log(add(1, 2)); // 3
JAVASCRIPT
// app.cjs - CommonJS에서 require
const { add } = require('./math.cjs');
console.log(add(1, 2)); // 3

** 주의 **: CommonJS에서 ESM 모듈을 require()로 직접 불러올 수 없습니다. 동적 import()를 사용해야 합니다.

댓글 로딩 중...