테마
Rollup과 Terser: 라이브러리 번들링의 정석
Rollup은 ES Module 기반의 모듈 번들러로 라이브러리 번들링에 특화되어 있으며, Terser는 JavaScript 코드를 압축하고 난독화하는 도구이다.
학습 목표
- Rollup의 설계 철학과 ES Module 기반 번들링 원리를 이해한다
- Rollup이 라이브러리 번들링에 특화된 이유를 설명할 수 있다
- Tree Shaking의 원리와 ES Module과의 관계를 파악한다
- Terser의 코드 압축(Minification)과 난독화(Mangling) 기능을 구분한다
- rollup.config.js 설정을 작성하고 플러그인을 활용할 수 있다
1. Rollup이란?
Rollup은 공식 사이트에서 스스로를 **"The JavaScript Module Bundler"**라고 소개한다. 작은 코드 조각들을 컴파일하여 더 크고 복잡한 산출물로 만드는 것이 핵심 역할이다.
1.1 Rollup의 핵심 특성
| 특성 | 설명 |
|---|---|
| ES Module 네이티브 | ESM import/export 문법을 기반으로 의존성 그래프를 구축한다 |
| Tree Shaking 선구자 | 사용하지 않는 코드를 자동으로 제거하는 기능을 처음 대중화했다 |
| 깔끔한 출력 | 번들 결과물이 사람이 읽을 수 있을 정도로 깔끔하다 |
| 다중 출력 포맷 | ESM, CJS, UMD, IIFE 등 다양한 모듈 형식으로 출력 가능하다 |
| 라이브러리 특화 | 오픈소스 라이브러리 번들링에서 사실상 표준이다 |
2. 모듈 시스템 이해
Rollup을 이해하려면 먼저 JavaScript 모듈 시스템을 알아야 한다.
핵심 차이: ESM은 import/export가 파일 최상위에서 정적으로 선언되므로, 빌드 타임에 어떤 코드가 실제로 사용되는지 분석할 수 있다. 이것이 Tree Shaking의 근본 원리이다.
3. Tree Shaking
Tree Shaking은 "나무를 흔들어 죽은 잎을 떨어뜨린다"는 비유에서 유래한 용어로, 사용하지 않는 코드를 번들에서 제거하는 최적화 기법이다.
3.1 Tree Shaking 동작 원리
javascript
// utils.js
export function plus1(num) { return num + 1; }
export function plus2(num) { return num + 2; } // 사용되지 않음
export function plus3(num) { return num + 3; } // 사용되지 않음
// index.js
import { plus1 } from './utils.js';
console.log(plus1(5));Rollup으로 번들링하면 plus2와 plus3은 최종 번들에 포함되지 않는다. ESM의 정적 구조 덕분에 빌드 타임에 plus1만 사용된다는 것을 정확히 판단할 수 있다.
3.2 Tree Shaking이 동작하지 않는 경우
| 상황 | 이유 |
|---|---|
CommonJS require() 사용 | 동적 로드라 정적 분석 불가능 |
| 사이드 이펙트가 있는 코드 | 실행 자체가 결과를 만들므로 제거 불가 |
| 동적 import 경로 | import(variable) 형태는 분석 불가 |
import * 후 간접 접근 | 어떤 것이 사용될지 확정 불가 |
4. Rollup 설정과 실습
4.1 기본 설정
javascript
// rollup.config.js
module.exports = {
input: 'src/main.js', // 진입점
output: {
file: 'dist/bundle.js', // 출력 파일
format: 'cjs', // 출력 형식: cjs, esm, umd, iife
name: 'MyLibrary', // UMD/IIFE에서 전역 변수명
sourcemap: true // 소스맵 생성
},
external: ['react', 'lodash'] // 번들에 포함하지 않을 외부 의존성
};4.2 다중 출력 설정
라이브러리를 배포할 때는 CJS와 ESM을 동시에 제공하는 것이 일반적이다.
javascript
// rollup.config.js
module.exports = {
input: 'src/index.js',
output: [
{ file: 'dist/index.cjs.js', format: 'cjs', sourcemap: true },
{ file: 'dist/index.esm.js', format: 'esm', sourcemap: true },
{ file: 'dist/index.umd.js', format: 'umd', name: 'MyLib', sourcemap: true }
]
};4.3 주요 플러그인
| 플러그인 | 역할 |
|---|---|
@rollup/plugin-node-resolve | node_modules에서 모듈을 찾아 번들에 포함 |
@rollup/plugin-commonjs | CJS 모듈을 ESM으로 변환하여 번들링 |
@rollup/plugin-babel | Babel을 통한 트랜스파일 |
@rollup/plugin-typescript | TypeScript 컴파일 |
@rollup/plugin-terser | Terser를 통한 코드 압축 |
@rollup/plugin-json | JSON 파일 import 지원 |
5. Terser란?
Terser는 JavaScript 코드를 **압축(Minification)**하고 **난독화(Mangling)**하는 도구이다. ES6+ 문법을 지원하며, 코드의 동작은 유지하면서 파일 크기를 대폭 줄인다.
5.1 Terser가 수행하는 작업
5.2 Terser CLI 사용
bash
# 설치
npm install --save-dev terser
# CLI로 압축
npx terser src/index.js --compress --mangle --output dist/index.min.js
# 소스맵과 함께 압축
npx terser src/index.js --compress --mangle --source-map --output dist/index.min.js5.3 주요 옵션
| 옵션 | 기능 | 예시 |
|---|---|---|
compress | 코드 최적화 (데드 코드 제거, 조건문 단순화) | compress: { dead_code: true } |
mangle | 변수명을 짧게 변환 | mangle: { toplevel: true } |
format | 출력 형식 제어 | format: { comments: false } |
sourceMap | 소스맵 생성 | sourceMap: true |
6. Rollup + Terser 통합
Rollup에서 Terser를 플러그인으로 사용하면 번들링과 압축을 한 번에 수행할 수 있다.
javascript
// rollup.config.js
const terser = require('@rollup/plugin-terser');
const resolve = require('@rollup/plugin-node-resolve');
const commonjs = require('@rollup/plugin-commonjs');
module.exports = {
input: 'src/index.js',
output: [
{
file: 'dist/index.js',
format: 'esm',
sourcemap: true
},
{
file: 'dist/index.min.js',
format: 'esm',
sourcemap: true,
plugins: [terser()] // 이 출력에만 압축 적용
}
],
plugins: [
resolve(),
commonjs()
],
external: ['react', 'react-dom']
};7. 라이브러리 vs 애플리케이션 번들링
| 기준 | 라이브러리 번들링 | 애플리케이션 번들링 |
|---|---|---|
| 권장 번들러 | Rollup | Webpack, Vite |
| 외부 의존성 | external로 제외 | 번들에 포함 |
| 출력 포맷 | CJS + ESM + UMD | 단일 포맷 |
| Tree Shaking | 매우 중요 (소비자에게 혜택) | 중요 (번들 크기 감소) |
| 코드 분할 | 보통 불필요 | 라우트별 분할 중요 |
| 타입 정의 | .d.ts 반드시 제공 | 불필요 |
| 소스맵 | 선택적 | 디버깅용 필수 |
라이브러리에 Rollup이 적합한 이유:
- ESM 네이티브 지원으로 Tree Shaking이 완벽하다
- 출력 코드가 깨끗해서 소비자가 디버깅하기 쉽다
- 다중 출력 포맷을 간편하게 설정할 수 있다
- external 설정으로 peer dependency를 자연스럽게 제외한다
핵심 정리
| 개념 | 핵심 내용 |
|---|---|
| Rollup | ES Module 기반 번들러. 라이브러리 번들링의 사실상 표준이다 |
| Tree Shaking | 사용하지 않는 코드를 제거하는 최적화. ESM 정적 구조 덕분에 가능하다 |
| 다중 출력 | CJS, ESM, UMD 등 여러 포맷을 동시에 생성할 수 있다 |
| Terser | JavaScript 코드 압축(Compress) + 난독화(Mangle) 도구이다 |
| 통합 사용 | @rollup/plugin-terser로 번들링과 압축을 한 번에 수행한다 |
| 포지셔닝 | 라이브러리는 Rollup, 애플리케이션은 Webpack/Vite가 일반적이다 |
다음 단계
- 다음 문서 04-esbuild.md에서 Go 기반의 초고속 번들러 esbuild의 원리, 주요 기능, 그리고 기존 도구와의 성능 차이를 학습한다.