Node.js · 2026-04-18

모듈 로딩: ES Modules(ESM)과 CommonJS 비교

Node.js 환경에서 모듈 로딩 방식의 차이와 설정, 마이그레이션 절차, 주요 동작 원리 및 오류 대처법을 초보자도 이해하기 쉬운 기술자료

작성일 : 2026-04-18 ㆍ 작성자 : 관리자
post
목차

개요

JavaScript에서 모듈은 코드 재사용과 의존성 관리를 위한 핵심 구조다. Node.js 생태계에서 주로 사용되는 두 가지 표준은 CommonJS와 ES Modules(ESM)이다. 이 글은 두 방식의 근본적 차이와 설정법, 호환성 이슈, 실제 예제까지 다룬다.

모듈 시스템의 기본 개념

CommonJS

CommonJS는 서버사이드 JavaScript에서 널리 쓰여온 규격이다. require를 사용해 동기적으로 모듈을 가져오고 module.exports 또는 exports로 값을 내보낸다. 초기 Node 버전에서 기본 방식으로 채택되어 생태계가 형성되었다.

ES Modules(ESM)

ESM은 ECMAScript 표준에 포함된 모듈 시스템이다. import와 export 문법을 사용하며 정적 분석이 가능해 트리쉐이킹(tree-shaking)과 번들링에 유리하다. 최근 Node에서는 공식적으로 지원되며, 브라우저와의 일관성이 장점이다.

핵심 차이점

  • 문법: CommonJS는 require/module.exports, ESM은 import/export
  • 로딩 방식: CommonJS는 동기적 실행, ESM은 정적 바인딩과 지연 평가 가능
  • 파일 확장자와 설정: .js 파일을 ESM으로 처리하려면 package.json의 type 필드 설정이나 .mjs 확장자가 필요
  • 호환성: CommonJS 패키지가 많아 혼용 시 주의가 필요

Node에서의 설정과 사용

package.json으로 ESM 활성화

프로젝트 전체를 ESM으로 전환하려면 package.json에 type을 선언한다. 이 설정은 .js 파일을 ESM으로 해석하게 만든다.

{
  "name": "example",
  "version": "1.0.0",
  "type": "module"
}

파일 확장자 방식

다른 방법으로는 .mjs 확장자를 사용하면 Node는 해당 파일을 ESM으로 처리한다. 반대로 CommonJS 전용으로 만들려면 .cjs를 사용할 수 있다.

기본적인 문법 예제

CommonJS 예제:

// math.cjs
const add = (a, b) => a + b;
module.exports = { add };

// index.cjs
const { add } = require('./math.cjs');
console.log(add(1, 2));

ESM 예제:

// math.mjs
export const add = (a, b) => a + b;

// index.mjs
import { add } from './math.mjs';
console.log(add(1, 2));

호환성과 상호운용성

CommonJS와 ESM을 섞어 사용할 때 몇 가지 규칙을 기억해야 한다. ESM에서 CommonJS 모듈을 불러올 때는 default 내보내기로 래핑되는 경우가 많다. 반대로 CommonJS에서 ESM을 require로 바로 불러올 수 없으므로 동적 import 또는 변환이 필요하다.

동적 import와 top-level await

ESM은 import() 함수를 사용한 동적 로딩을 지원한다. 또한 최신 Node 버전에서는 top-level await를 허용해 비동기 초기화 코드가 간결해진다.

const module = await import('./dynamic.mjs');

마이그레이션 전략

  • 1단계: package.json에 "type": "module" 적용을 고려하기 전에 테스트 브랜치를 만든다.
  • 2단계: 라이브러리 의존성을 점검해 ESM 지원 여부를 확인한다.
  • 3단계: 파일 확장자 혼용(.cjs, .mjs)을 사용해 점진적 전환을 시도한다.
  • 4단계: 빌드 도구(예: Babel, Rollup, Webpack) 설정을 조정해 트랜스파일링을 적용한다.

주의할 점과 자주 발생하는 오류

  • 파일 확장자와 type 설정이 일치하지 않으면 Unexpected token 에러 발생
  • 모듈 해석 차이로 인해 default/namespace import 혼동 발생
  • 서버 사이드 초기화 코드에서 top-level await 사용 시 버전 호환성 확인 필요

성능과 도구 호환성

ESM은 정적 분석을 통해 번들러 최적화를 돕는다. 반면 CommonJS는 오래된 패키지와 도구에서 높은 호환성을 보인다. 빌드 파이프라인과 배포 환경을 고려해 선택하는 것이 바람직하다.

결론과 추천

새 프로젝트라면 ESM 기반으로 시작하는 것이 장기적으로 이득이다. 다만 기존 생태계와의 호환성, 사용 중인 라이브러리 지원 상태를 우선 점검한다. 단계적 마이그레이션과 파일 확장자 혼용은 안전한 전환 경로다. 실무에서는 "type module Node 설정"과 관련된 문서와 테스트를 먼저 확인한 뒤 전환 계획을 세우는 것을 추천한다.

Node ESM 사용법 CommonJS vs ESM 차이 type module Node 설정 Node 모듈 ESM 사용법 모듈 마이그레이션 동적 import top-level await