Developer/Javascript

rollup 빌드 결과 파일 cjs, esm 차이, package.json, rollup.config.js

jaddong 2023. 2. 5. 19:58
320x100

현재 회사에서 디자인시스템을 모노레포로 만드는 작업을 하고 있다. 모노레포에 있는 각각의 라이브러리들을 rollup을 통해 번들링이되는데, 지금 모노레포와 rollup 관련해서 꽤나 삽질을 하고 있다. 그래도 배우는 것이 있어서 정리해보고자 한다.

 

 

현재 rollup.config.js파일이다. 여기선 결과물의 경로나 format을 정할 수 있다 cjs로 선택되어 있는 상태이다.

// rollup.config.js
output: {
    dir: "lib",
    format: "cjs",
    exports: "named",
  },

 

이렇게 나온 번들 결과물을 package.json에서 사용하겠다고 지정을 한다.

main은 이 라이브러리를 사용할 때 참조할 파일이고, types는 타입스크립트 파일이 되겠다.

그러면 module와 esm은 뭐고 왜 필요하지라는 궁금증이 생기게 되었다.

// package.json
{
  ...
  "main": "lib/index.js",
  "module": "lib/index.esm.js",
  "types": "lib/index.d.ts",
  ...
 }

 

여기서 우선 esm과 cjs의 차이를 알아보면 우선 코드상에서  import, export할 때의 차이가 있다는 걸 알 수 있다.

리액트로 개발하는 개발자라면 esm이 눈에는 더 익숙할 것이다.

이 외에도 차이가 다양하니 여기서 자세히보면 되겠다.

CommonJs (CJS)

// util.js
module.exports = (x, y) => x + y;

// main.js
const whateverWeWant = require('util');
console.log(whateverWeWant(2, 4));

 

ECMAScript Modules (ESM)

// util.js
export const sum = (x, y) => x + y;

// main.js
import { sum } from 'util'
console.log(sum(2, 4));

 

그래서 라이브러리의 빌드 결과로 나온 index.js 외에 index.esm.js는 왜 필요한 것인가?

 

main

라이브러리 빌드 결과의 메인 파일로 어떤 프로젝트에서 이 라이브러리를 설치하게되면 index.js 파일을 참조하게된다.

 

module

이 라이브러리를 설치한 프로젝트를 빌드할 때, index.esm.js를 참조해 빌드하게된다.

ems는 트리쉐이킹이 가능해 JavaScript 번들의 사이즈를 줄여서 렌더링이 중단되는 시간을 최소화는 효과가 있다.

 

 

위의 설명보다 자세히 예시를 들어보겠다.

프로젝트에서 아래와 같이 libraryA라는 라이브러리를 설치했다고하자.

import { Button } from "libraryA";

로컬에서 개발할 때 command나 ctrl을 누르상태로 libraryA 부분을 클릭하면 node_modules에 해당 라이브러리의 index.js파일을 가리키는 것을 확인할 수 있다. main에서 지정한 데로 index.js 파일에서 import가 된다는 것이다.

여기서 import로 불러오든 require로 불러오든 상관이 없다! 자세한 건 여기서 확인할 수 있다.

 

This diagram summarizes the above rules.

 

그런데 module에서 선택한 esm의 경우는 개발할 때가 아닌 번들링할 때 참조가 이뤄진다. 프로젝트 완성 후, 완성한 프로젝트를 번들러로 빌드를 할때 libraryA의 package.json의 module에서 지정한 esm형식을 참고해 빌드가 이뤄진다. 그리고 esm은 트리쉐이킹 가능해서 빌드할 때 프로젝트에서 libraryA에서 사용한 것만 긁어서 빌드가 된다.

 

위의 경우 Button만 import했다면 Button만 골라서 빌드가 된다는 것이다.

 

일반적으로 CommonJS형태는 트리쉐이킹을 할 수 없다. lodash를 예로 들면, lodash는 CommonJS 형태로 번들링되어 배포되기 때문에 webpack의 기본설정으로는 lodash를 트리쉐이킹 할 수 없다. 트리쉐이킹을 위해서는 babel-plugin-lodash을 사용하거나 lodash-es를 사용하는 것을 추천한다.

 

 

그래서 결론은  라이브러리를 만들 때 cjs와 esm을 둘다 제공해주는 것이 좋고 esm버전이 프로젝트의 번들사이즈를 줄이는 데 도움을 줄 수 있다는 것이다.

 

 

참고

- https://roseline.oopy.io/dev/translation-why-cjs-and-esm-cannot-get-along

- https://pencilflip.medium.com/using-es-modules-with-commonjs-modules-in-node-js-1015786dab03

- https://maeng2418.github.io/development/library_deploy/

- https://toss.tech/article/commonjs-esm-exports-field

 

CommonJS와 ESM에 모두 대응하는 라이브러리 개발하기: exports field

Node.js에는 두 가지 Module System이 존재합니다. 토스 프론트엔드 챕터에서 운영하는 100개가 넘는 라이브러리들은 그것에 어떻게 대응하고 있을까요?

toss.tech

 

라이브러리 배포 cjs? ejs?

개요 언젠가 한번 나만의 라이브러리를 만들어서 배포해보고 싶다는 생각을 갖구있다가 이번에 디자인시스템 개발을 도전하게 되었다. 리액트 컴포넌트 기반으로 디자인시스템 개발에 대한 자

maeng2418.github.io

 

Using ES modules with CommonJS modules in Node.js

This is not meant to be a comprehensive overview of ES modules and CommonJS modules — I do not go into depth about what modules are and…

pencilflip.medium.com

 

[번역] 왜 CommonJS와 ES Modules는 함께 쓸 수 없는가 | 내 라이브러리에서 esm과 cjs를 동시에 지원하는

Dan Fabulich의 Node Modules at War: Why CommonJS and ES Modules Can’t Get Along 을 번역한 글입니다. 이미지나 설명이 필요한 부분은 추가로 덧붙여 놓았습니다.

roseline.oopy.io

 

반응형