IT의 IT 블로그

CommonJS 와 ES Module 차이 이해하기 본문

Frontend Basic (기초 이론)

CommonJS 와 ES Module 차이 이해하기

IT의 IT 블로그 2026. 2. 20. 19:06

안녕하세요. 오늘은 자바스크립트 모듈 시스템의 두 가지 방식에 대해 정리해보려고 합니다.

자바스크립트의 모듈 시스템은 크게 두 가지로 구분됩니다.

하나는 Node.js 환경에서 사용되어 온 CommonJS,
다른 하나는 ECMAScript 표준으로 도입된 ES Module(ESM) 입니다.

이 두 방식은 단순한 문법 차이에 그치지 않습니다.
모듈을 로딩하는 시점, 코드를 분석하는 방식, 그리고 번들링 구조에까지 영향을 미칩니다.

이번 글에서는

  • 왜 이 두 방식이 등장했는지
  • 구조적으로 무엇이 다른지
  • 그리고 왜 이 차이가 트리쉐이킹과 연결되는지

를 순서대로 정리해보겠습니다.


1. 왜 ‘모듈’이 필요했을까?

1.1 스크립트 태그 시대의 문제점

초기 자바스크립트는 HTML에 <script> 태그를 여러 개 붙여 사용하는 방식이었습니다.

 
<script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script>
 

이 방식은 단순하지만, 규모가 커지면 치명적인 문제가 발생합니다.


1.2 전역 스코프 오염

초기 자바스크립트에서는 여러 파일이 모두 하나의 전역 스코프를 공유했습니다.

특히 var로 선언한 변수와 함수 선언문은 전역에서 선언될 경우
전역 객체(window)의 프로퍼티로 등록됩니다.

 
var count = 0;
 

이 경우:

console.log(window.count); // 0
 

다른 파일에서도 같은 이름을 var로 선언하면
같은 전역 공간을 공유하므로 충돌이 발생합니다.

let과 const는 전역 객체에 바인딩되지는 않지만,
여전히 전역 스코프 자체는 공유합니다.

결과적으로 파일이 많아질수록 변수 충돌과 예측하기 어려운 버그가 발생하게 됩니다.


1.3 파일 분리의 한계

파일을 나눠도 의존성 관리가 불가능했습니다.

  • 어떤 파일이 먼저 로딩되어야 하는지
  • 어떤 파일이 어떤 파일을 사용하는지
  • 로딩 순서가 꼬이면 에러 발생

이 문제를 해결하기 위해 등장한 것이 모듈 시스템입니다.


2. CommonJS란 무엇인가?

2.1 등장 배경

CommonJS는 Node.js 환경에서 모듈화를 위해 등장한 규격입니다.

브라우저가 아닌, 서버 환경을 중심으로 발전했습니다.


2.2 기본 문법

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

// import
const add = require('./add');
 

핵심 키워드는 두 가지입니다.

  • module.exports
  • require()

2.3 로딩 구조: 런타임 로딩

CommonJS의 가장 중요한 특징은 이것입니다.

require는 "함수"다.

즉, 실행 시점에 모듈을 불러옵니다.

 
if (condition) {
const module = require('./something');
}

이처럼 조건문 안에서도 require가 가능합니다.

이는 매우 유연하지만, 동시에 구조적인 한계를 만듭니다.


2.4 장점

  • Node.js와 자연스럽게 결합
  • 동적 로딩 가능
  • 단순하고 직관적인 구조

하지만 이 구조는 번들링 관점에서 단점이 됩니다.

그 이유는 뒤에서 설명합니다.


3. ES Module이 등장한 이유

3.1 브라우저 표준화 필요성

CommonJS는 Node 중심이었습니다.

하지만 브라우저에는 표준 모듈 시스템이 없었습니다.

그래서 ECMAScript 2015 (ES6)에서 공식 모듈 시스템이 도입됩니다.

 

이것이 ES Module (ESM) 입니다.


3.2 기본 문법

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

// import
import { add } from './add.js';
 

문법 차이는 단순해 보이지만, 내부 구조는 완전히 다릅니다.


3.3 가장 중요한 차이

import는 문법이다.

require는 함수이고,
import는 자바스크립트 문법입니다.

이 차이가 모든 것을 바꿉니다.


4. 정적 분석 가능 여부

4.1 정적 분석이란 무엇인가?

정적 분석이란,
코드를 실행하지 않고도 구조를 분석할 수 있는 것을 의미합니다.


4.2 CommonJS는 왜 어려운가?

const name = './module-' + version;
const mod = require(name);
 

이 코드는 실행해보기 전까지 어떤 파일을 불러오는지 알 수 없습니다.

즉, 빌드 도구가 사전에 분석하기 어렵습니다.


4.3 ES Module은 왜 가능한가?

import { add } from './add.js';
 

import 경로는 반드시 문자열 리터럴이어야 합니다.

따라서 빌드 시점에:

  • 어떤 모듈을 사용하는지
  • 어떤 export를 사용하는지
  • 어떤 코드를 제거할 수 있는지

정확히 파악할 수 있습니다.

이것이 바로 정적 분석 가능 구조입니다.


5. 트리쉐이킹과의 연결

5.1 트리쉐이킹이란?

트리쉐이킹(Tree Shaking)은
사용하지 않는 코드를 제거하는 최적화 기법입니다.


5.2 ES Module이 유리한 이유

// utils.js
export function a() {}
export function b() {}
export function c() {}
 
 
import { a } from './utils.js';
 

ESM에서는 a만 사용했다는 것을 빌드 단계에서 정확히 알 수 있습니다.

따라서 b, c는 제거됩니다.


5.3 CommonJS에서 어려운 이유

const utils = require('./utils');
utils.a();
 

require는 전체 객체를 가져옵니다.

어떤 속성을 사용할지 사전에 확정되지 않습니다.

그래서 보수적으로 전부 포함하게 됩니다.


6. 런타임 로딩 vs 정적 분석 기반

정리해보겠습니다.

6.1 CommonJS

  • require는 함수
  • 런타임 로딩
  • 동적 구조
  • 트리쉐이킹 어려움

6.2 ES Module

  • import는 문법
  • 빌드 타임 분석
  • 정적 구조
  • 트리쉐이킹 가능

7. 왜 현대 번들링은 ESM 중심인가

현대 번들러 (Webpack, Rollup, Vite 등)는
ESM 구조를 기반으로 최적화합니다.

이유는 단순합니다.

정적 분석이 가능해야 최적화가 가능하기 때문입니다.

ESM은 번들러 친화적인 구조입니다.


8. 마무리 정리

오늘 내용을 한 줄로 요약하면:

  • CommonJS = 런타임 중심 모듈 시스템
  • ES Module = 정적 분석 중심 모듈 시스템

그리고 이 차이가:

  • 트리쉐이킹
  • 번들 크기
  • 빌드 최적화
  • 현대 프론트엔드 구조

모두에 영향을 줍니다.


지금까지는 “모듈의 구조”를 이해하는 단계였습니다.
이제는 “모듈을 다루는 도구”를 이해할 차례입니다.

import와 export로 연결된 수많은 파일들은 실제 빌드 과정에서 어떤 과정을 거칠까요?

다음 글에서는
번들러가 의존성 그래프를 생성하고,
트리쉐이킹과 코드 스플리팅을 수행하는 과정을
구조 중심으로 정리해보겠습니다.