트리 쉐이킹
번들에 포함되지만 실제로는 쓰이지 않는 코드를 정적 분석으로 찾아내 제거함으로써 최종 번들 크기를 줄이는 최적화 기법입니다.
동작 원리
나무를 흔들어 죽은 잎을 떨어뜨리는 데서 온 이름입니다. 번들러는 모듈 간의 import/export 관계를 그래프로 만든 뒤, 진입점에서 도달할 수 없는 export를 데드 코드로 판단해 제거합니다.
// utils.js
export function used() { return 'used'; }
export function unused() { return 'unused'; }
// main.js
import { used } from './utils';
console.log(used());
// → 번들 결과에서 unused()는 제거된다
전제 조건
1. ES 모듈을 써야 합니다
ES Module은 import/export가 최상위에만 올 수 있는 정적 구조라, 코드를 실행하지 않고도 무엇이 쓰이는지 판단할 수 있습니다. 반면 CommonJs는 조건에 따라 동적으로 require()할 수 있어 정확한 분석이 어렵습니다.
// CommonJS — 실행해봐야 알 수 있어 트리 쉐이킹이 어렵다
const mod = require(condition ? './a' : './b');
2. 사이드 이펙트가 없어야 합니다
모듈이 로드되는 것만으로 전역 상태를 바꾼다면, 번들러는 그 코드를 지워도 안전한지 확신할 수 없어 그대로 남깁니다.
// package.json — 이 패키지엔 사이드 이펙트가 없다고 선언
{
"sideEffects": false
}
CSS import처럼 실제로 사이드 이펙트가 있는 파일은 배열로 예외를 지정합니다.
{
"sideEffects": ["*.css"]
}
적용 방법
webpack, Rollup, esbuild 등 대부분의 번들러가 지원하며 설정은 조금씩 다릅니다. 최신 webpack은 mode: 'production'만 설정해도 기본 트리 쉐이킹이 켜지고, TerserPlugin 같은 플러그인을 더하면 더 강하게 최적화할 수 있습니다.
효과 측정
- 적용 전후 번들 파일 크기를 비교합니다.
webpack-bundle-analyzer같은 도구로 어떤 모듈이 얼마나 차지하는지 시각적으로 확인합니다.- 번들 결과물을 직접 열어 제거되길 기대한 코드가 실제로 빠졌는지 봅니다.
주의사항
- 라이브러리를 통째로 import하면 트리 쉐이킹이 무력화될 수 있습니다. 필요한 것만 named import 하는 편이 안전합니다.
- 라이브러리 자체가 CommonJS로만 배포되면 트리 쉐이킹이 적용되지 않습니다. 도입 전에 ESM 빌드 제공 여부를 확인하는 것이 좋습니다.
- 트리 쉐이킹은 "쓰지 않는 코드"를 지우는 것이고, 코드 스플리팅은 "지금 당장 필요 없는 코드"를 나중에 불러오는 것입니다. 목적이 다르므로 함께 씁니다.
핵심 정리
- 정적 분석으로 사용되지 않는 export를 제거해 번들 크기를 줄입니다.
- ES 모듈의 정적 구조가 전제이며, CommonJS에서는 제대로 동작하지 않습니다.
- 사이드 이펙트가 있으면 제거되지 않으므로
package.json의sideEffects설정으로 명시합니다.