3 분 소요

🚀 시작하며

이번엔 자바스크립트의 두 가지 대표 모듈 시스템인 ESM과 CommonJS를 비교하고,
각각 언제 어떻게 쓰이는지, 어떤 차이가 있는지를 정리합니다.

“모듈 시스템을 왜 도입했는지”, “ESM과 CJS는 어떻게 다르고 어떤 기준으로 선택해야 하는지”,
실무적으로 어떤 의사결정을 내려야 할지를 함께 고민해보는 시간을 가져보겠습니다.


모듈 시스템이란?

✅ 정의

모듈 시스템이란, 코드를 여러 파일로 나누고, 이들 간의 의존성을 정의하여 재사용성과 유지보수성을 높이는 구조적 방식입니다.

✅ 왜 필요한가?

초기의 자바스크립트는 <script> 태그로 여러 파일을 단순히 순서대로 불러오는 방식이었습니다. 이로 인해:

  • 전역 스코프 오염
  • 변수 이름 충돌
  • 파일 순서에 따른 오류 발생
  • 코드 복잡성 증가

등의 문제가 발생했죠.

이를 해결하기 위해 모듈 시스템이 도입되었고,
Node.js 환경에서는 CommonJS,
브라우저와 ES6 이후의 JS 표준에선 ESM(ECMAScript Modules)이 등장했습니다.


CommonJS(CJS)란?

✅ 정의

CommonJS는 Node.js에서 채택한 서버 사이드용 모듈 시스템입니다.
동기 방식이며, require()module.exports 문법으로 모듈을 로딩/내보냅니다.

✅ 주요 특징

  • 동기적 로딩(Synchronous): require()가 실행될 때 모듈을 즉시 읽음
  • Node.js 런타임 환경에 최적화
  • 런타임 로딩 가능: 조건문, 루프 내 require() 가능
  • 완전한 CommonJS 사양을 구현한 환경은 Node.js가 대표적

✅ 문법 예시

// math.js
function add(a, b) {
  return a + b;
}
module.exports = { add };

// main.js
const math = require('./math');
console.log(math.add(1, 2));

✅ 장점과 단점

장점 단점
직관적이고 오래된 프로젝트에서 널리 사용 브라우저에서 직접 실행 불가
require()가 함수이기 때문에 유연함 정적 분석이 어려워 최적화가 제한적임
도입이 쉬움 트리 쉐이킹(Tree-shaking) 불가능

트리 쉐이킹(Tree-shaking)

사용되지 않는 코드를 번들에서 제거하여 최종 결과물을 더 작고 빠르게 만드는 기법

나무를 흔들어서 죽은 가지를 떨어트리는 것을 비유한 거라고 합니다.


ESM (ECMAScript Modules)이란?

✅ 정의

ESM은 ES6(2015)에 도입된 표준 모듈 시스템입니다. 브라우저, Node.js 양쪽 모두를 위한 공식 표준이며, import/export 문법을 사용합니다.

✅ 주요 특징

  • 정적 분석 가능(Static Analysis): import 문이 파일 최상단에 있어야 하며, 런타임 이전에 분석됨
  • 비동기 로딩 방식: 모듈을 필요에 따라 지연 로딩 가능 (import())
  • 브라우저/Node.js 호환: Node에선 "type": "module" 설정 필요

✅ 문법 예시

// math.mjs
export function add(a, b) {
  return a + b;
}

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

또는 .js 파일에서 사용하려면 package.json에 아래 설정을 추가해야 함:

{
  "type": "module"
}

✅ 장점과 단점

장점 단점
정적 분석을 통해 트리 쉐이킹 등 최적화 가능 import는 최상단에서만 사용 가능
브라우저에서도 그대로 사용 가능 기존 CommonJS와 호환 어려움
표준 사양 기반으로 향후 JS 생태계에서 주도적임 .mjs 확장자나 설정이 번거로울 수 있음

주요 차이점 비교

항목 CommonJS (CJS) ESM
문법 require(), module.exports import, export
로딩 방식 동기(Synchronous) 비동기(Asynchronous, 정적 분석 기반)
해석 시점 실행 시 파싱 시(정적 분석)
브라우저 지원 X O
사용 위치 Node.js 브라우저, Node.js
조건부 로딩 가능 제한적 (import() 사용해야 가능)
트리쉐이킹 불가능 가능
파일 확장자 .js .mjs, 또는 "type": "module" 사용 시 .js

Node.js에서 둘 다 쓸 수 있을까?

Node.js는 기본적으로 CommonJS 기반이지만, package.json"type": "module"을 추가하면 ESM도 사용할 수 있습니다.

json복사편집{
  "type": "module"
}

단, 다음과 같은 제약이 생깁니다:

  • .js에서 require() 사용 불가
  • .mjs 또는 .js + "type": "module"로 통일해야 함
  • __dirname, __filename 같은 CJS 전용 변수 사용 불가

✔️ 혼용은 되도록 피하고, 처음부터 프로젝트 구조를 결정하는 것이 좋습니다.


실무에서 언제 무엇을 선택해야 할까?

상황 추천 모듈 시스템 이유
기존 Node.js 프로젝트 CommonJS 호환성, 종속 라이브러리 대부분 CJS 기반
새 Node.js 프로젝트 ESM 최신 표준 채택, 브라우저와 호환성 확보
브라우저 기반 라이브러리 개발 ESM 그대로 script type=”module”로 로딩 가능
번들러 사용 (Vite, Webpack 등) ESM 내부적으로 변환되기 때문에 ESM 권장
라이브러리(NPM) 배포 ESM + CJS 병행 여러 환경에서 사용할 수 있게 dual export 구조 고려 필요

🤔 추가로 생각해볼 점

📦 1. ESM으로의 전환이 왜 중요한가?

자바스크립트 생태계는 점점 ESM 중심으로 이동 중입니다. 최신 도구(Vite, Bun, Deno 등)는 ESM을 기본으로 채택하고 있고, 브라우저와 Node.js 간의 경계도 점점 흐려지고 있습니다.

앞으로는 ESM이 기본이 될 것이므로, 학습과 구조 설계 시 ESM을 전제로 삼는 것이 유리합니다.

🔁 2. ESM으로 이관 시 고려할 점

  • 기존 CJS 기반 라이브러리와의 호환성 문제
  • import.meta.url, top-level await, dynamic import() 같은 기능 이해 필요
  • jest 같은 테스트 도구도 CJS 위주이기 때문에 설정 필요

🎯 3. 트리쉐이킹(Dead Code Elimination)의 중요성

ESM은 정적 분석이 가능하므로 번들링 시 사용하지 않는 코드를 제거할 수 있습니다. 이건 번들 크기, 로딩 속도, 성능 최적화에 큰 영향을 줍니다.

카테고리:

업데이트:

댓글남기기