[TIL] 250520_JavaScript: CJS와 ESM

지코·2025년 5월 20일
1

Today I Learned

목록 보기
67/74
post-thumbnail

📌 CommonJS와 ESModule에 대하여

자바스크립트에서 모듈 시스템은 코드를 분리하고 재사용성을 높이기 위해 도입된 개념이다. ESModule(ESM)CommonJS 는 대표적인 자바스크립트 모듈 시스템이다.

CommonJS(CJS)

CJS는 Node.js의 기본 모듈 시스템으로, require() 함수를 통해 모듈을 동기적으로 불러오고 module.exports 를 통해 모듈을 내보내는 방식이다. 즉 모듈이 로드될 때까지 다음 코드가 실행되지 않는데, 이 때문에 모듈이 로드된 후에 코드가 실행되는 서버 사이드 렌더링과 같은 환경에서 유리하다. 이 방식은 주로 서버 측에서 사용됐지만, 클라이언트 환경에서도 번들러를 통해 사용할 수 있다.

// CommonJS 방식
const fs = require('fs');        // import

fs.sayHello();

module.exports = {				// export
	sayHello: function() {
      console.log('Hello from CommonJS');
    }
};     

이 방식은 런타임에 동적으로 모듈을 로딩하는 특징이 있어서, 순차적으로 실행되는 서버 환경에는 굉장히 잘 맞는다.

다만 브라우저에서는 CJS를 직접 사용할 수 없어서, 프론트엔드에서 사용하려면 반드시 번들링을 거쳐야 한다.
또한 트리 쉐이킹과 같은 최적화가 어렵다는 단점이 있다. 사용하지 않는 코드까지 번들에 포함되기 때문에, 번들 사이즈 최적화 측면에서는 아쉬운 부분이 많다.

ESModule(ESM)

ESM은 ES6부터 도입된 자바스크립트 표준 모듈 시스템이고, importexport 키워드를 사용한다. ESM은 브라우저와 Node.js 환경에서 모두 사용할 수 있으며, 모듈을 비동기적으로 로드한다.

import { sayHello } from './moduleA';

sayHello();

export function sayHello() {
  console.log('Hello from ES Module');
}

이 방식의 가장 큰 특징은 정적 로딩(정적 분석)이 가능하다는 점이다. 즉 코드가 실행되기 전부터 의존성을 분석할 수 있기 때문에, 번들링 시점에 최적화가 가능하고 트리 쉐이킹을 지원한다.
또한 ESM은 브라우저에서도 바로 사용할 수 있기 때문에, 최근의 프론트엔드 프로젝트에서는 거의 대부분 ESM 기반으로 개발되고 있는 것을 확인할 수 있다.

ESM의 단점으로는 예전부터 Node.js 환경에서는 CJS가 기본으로 사용되어 왔기 때문에, ESM을 사용하기 위해서는 type: module 설정을 하거나, .mjs 확장자를 사용해야 하는 번거로움이 있다는 점이다.

정리

CommonJS는 Node.js 중심의 전통적인 방식이고, 동적 로딩과 서버 환경에 강점을 가지지만, 트리 쉐이킹이 어렵고 브라우저 환경에선 직접 사용이 어렵다는 단점이 있다.

반대로 ESModule은 정적 분석이 가능해서 최적화에 유리하고, 프론트엔드와 브라우저에서 바로 사용할 수 있다는 점이 강점이다.

최근에는 CommonJS와 ESModule을 모두 지원하는 라이브러리가 많이 등장하고 있다. 이는 두 모듈 시스템의 장점을 모두 활용할 수 있도록 한다. Webpack과 같은 번들러를 사용하면, CommonJS와 ESModule을 모두 지원하는 번들을 생성할 수 있다.

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        libraryTarget: 'umd',
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                },
            },
        ],
    },
};

최근에는 Node.js에서도 버전 12부터 네이티브로 ESModule을 공식 지원하기 시작했으며, 브라우저와 서버 간의 모듈 호환성을 위해 풀스택 애플리케이션 개발에서도 ESM이 많이 사용되고 있다. 특히 ESM은 비동기적 로딩과 트리 쉐이킹 같은 최적화 작업에 유리하다는 점에서 점점 더 선호되고 있다.


📌 트리 쉐이킹에 대하여

트리 쉐이킹최종 번들 크기를 줄이기 위해 사용되지 않는 코드를 제거하는 최적화 방법이다. 이 방법은 주로 모듈 시스템을 기반으로 동작하며, 사용되지 않는 데드(dead) 코드를 감지하고, 번들에서 제거하여 최적화한다.

트리 쉐이킹이 작동하려면, ESModule을 사용해야 한다. ESModule은 정적인 구조를 가지기 때문에, 코드 분석 과정에서 어떤 모듈이 사용되고 어떤 모듈이 사용되지 않는지를 쉽게 판단할 수 있다.
반면 CommonJS 같은 동적 모듈 시스템을 사용할 경우, 정확한 코드 분석이 어려워 트리 쉐이킹이 제대로 동작하지 않을 수 있다.

또 트리 쉐이킹을 적용하려면 코드가 사이드 이펙트가 없는 순수 함수 형태로 작성되어야 한다. 만약 모듈 내부에서 실행되는 코드가 전역 상태를 변경하거나 예측할 수 없는 동작을 한다면, 번들러는 해당 코드가 제거되어도 안전한지 확신할 수 없기 때문에 트리 쉐이킹이 적용되지 않을 수 있다.
이를 해결하기 위해 package.json 파일에 “sideEffects”: false 를 설정하면, 해당 패키지가 사이드 이펙트가 없음을 명시하여, 보다 적극적으로 최적화할 수 있다.

🤔 트리 쉐이킹은 어떻게 적용하는가

webpack, Rollup, esbuild 등의 번들러에서 트리 쉐이킹 기능을 지원하는데, 이때 설정 방법은 번들러마다 조금씩 차이가 있다.

최신 버전의 webpack을 예로 들면, mode: 'production' 설정만으로도 기본적인 트리 쉐이킹이 활성화되며, TerserPlugin 같은 플러그인을 추가하면 더욱 강력한 최적화가 가능하다.

🤔 트리 쉐이킹의 효과를 어떻게 측정해볼 수 있을까

트리 쉐이킹이 적용되었는지 확인하는 가장 일반적인 방법은 번들 크기를 비교하는 것이다. 번들링된 결과물을 분석하는 webpack의 bundle analyzer 같은 도구를 사용하면, 번들 이후 코드의 양을 시각적으로 확인할 수 있다.
또한 번들 파일을 직접 확인하여 원래 코드와 비교하는 것도 좋은 방법이다.

Reference

📄 매일메일 - CommonJS와 ES Module의 차이점에 대해 설명해주세요.
✍🏻 TanStack Query (React Query) 핵심 정리
📄 매일메일 - 트리 쉐이킹에 대해 설명해주세요.
🎥 [10분 테코톡] 제레미의 번들 사이즈 최적화

profile
꾸준하고 성실하게, FE 개발자 역량을 키워보자 !

0개의 댓글