Tree Shaking: 개념, 데드 코드 제거 메커니즘 및 도구

저자: IT Sectr 게시일: 2026-05-18 읽는 시간: 8 분

Tree Shaking은 애플리케이션 빌드 단계에서 사용되지 않는 코드를 제거하는 메커니즘(dead code elimination)입니다. Tree Shaking은 ES 모듈의 정적 구조를 분석하고 어디에도 가져오지(import) 않은 내보낸(export) 함수, 클래스 및 변수를 제외합니다. Webpack Documentation에 따르면, Tree Shaking을 올바르게 구성하면 애플리케이션 기능을 변경하지 않고 번들 크기를 30~60% 줄일 수 있습니다.

주요 포인트

  • Tree Shaking — import/export의 정적 분석을 기반으로 ES 모듈에서 사용되지 않는 내보내기 제거
  • ES 모듈 (import/export) — Tree Shaking을 지원하는 유일한 형식, CommonJS는 지원되지 않음
  • Webpack과 Rollup — 플러그인을 통해 Tree Shaking을 지원하는 주요 번들러
  • Side effects — 모듈의 부작용이 Tree Shaking을 차단함, package.json의 sideEffects: false 플래그가 문제 해결
  • Used exports — 정확한 데드 코드 제거를 위한 Webpack 프로덕션 모드의 내보내기 사용 분석

Tree Shaking이란?

Tree Shaking은 최종 번들에서 사용되지 않는 모듈과 함수를 제외하는 코드 최적화 기술입니다. 이 용어는 2015년 Rollup 팀에 의해 도입되었으며 프로세스를 은유적으로 설명합니다. 종속성 트리를 흔들어(shaking) 사용되지 않는 가지가 떨어지는 것입니다. 수동 최적화와 달리 Tree Shaking은 빌드 단계에서 자동으로 실행됩니다.

Tree Shaking은 ES 모듈(ECMAScript Modules)에서만 작동합니다. ES 모듈에서는 종속성이 import와 export를 통해 정적으로 결정됩니다. CommonJS(require/module.exports)는 Tree Shaking을 지원하지 않습니다. require가 동적으로 실행되기 때문에 번들러는 어떤 함수가 실제로 사용되는지 미리 판단할 수 없습니다. 최신 라이브러리(Lodash, Moment.js, RxJS)는 Tree Shaking을 지원하기 위해 ES 버전을 출시합니다.

Rollup: Tree Shaking의 선구자

Rollup은 2015년에 Tree Shaking을 최초로 구현한 번들러입니다. Webpack과 달리 Rollup은 처음부터 ES 모듈용으로 설계되었으며 더 적극적으로 데드 코드를 제거합니다. Rollup은 개별 내보내기뿐만 아니라 전체 모듈을 분석합니다. 모듈에 부작용이 없고 어떤 내보내기도 사용되지 않는 경우 Rollup은 전체 모듈을 번들에서 제외합니다.

Rollup은 모든 킬로바이트가 중요한 라이브러리 및 SDK에 특히 효과적입니다. Vue.js 프레임워크는 프로덕션 버전 빌드에 Rollup을 사용합니다. React는 2020년에 Rollup으로 전환했습니다. 애플리케이션의 경우 Webpack이 더 풍부한 플러그인 생태계(Hot Module Replacement, code splitting, CSS modules)로 인해 더 일반적으로 사용되지만, 라이브러리 빌드 시 최대 Tree Shaking 효과를 얻기 위해 Rollup은 업계 표준으로 남아 있습니다.

Tree Shaking의 절감 효과는 프로젝트 아키텍처에 크게 의존합니다. Ant Design 라이브러리를 사용하는 React 애플리케이션에서 Tree Shaking은 UI 컴포넌트 코드의 최대 70%를 제거할 수 있습니다. 모든 가져오기가 구체적이고 특화된 프로젝트에서는 절감 효과가 5~15%입니다. Webpack 연구에 따르면 평균 절감 효과는 번들 크기의 30~40%입니다.

데드 코드란

데드 코드 유형예시Tree Shaking 감지
사용되지 않는 내보내기export function unusedHelper()
사용되지 않는 가져오기import { unused } from "lib"
죽은 조건 분기if (false) { ... }아니오 (축소기가 제거)
DCE 후 호출되지 않은 함수function a(){} a() 여기서 a가 호출되지 않음부분적으로

Tree Shaking 작동 방식: 모듈 정적 분석

Tree Shaking 메커니즘은 번들러가 프로젝트의 모든 import/export 문에서 구축하는 종속성 그래프를 기반으로 합니다. 첫 번째 단계에서 번들러는 진입점(entry point)에서 모든 파일을 탐색하고 모듈 트리를 수집합니다. 두 번째 단계에서는 각 모듈의 어떤 내보내기가 다른 모듈에서 실제로 가져와지는지 분석합니다.

각 모듈에 대해 Webpack 또는 Rollup은 내보내기를 사용됨 또는 사용되지 않음으로 표시합니다. 사용되지 않는 내보내기는 번들에서 제외됩니다. 그러나 모듈 자체는 내보내기 중 하나라도 사용되면 번들에 남아 있습니다. 모듈을 완전히 제외할 수 있는 경우는 sideEffects 플래그를 사용하거나 모듈에 부작용이 전혀 없는 경우뿐입니다.

예시: Tree Shaking 전후

js
// utils.js — 함수가 있는 모듈
export function formatDate(date) {
  return date.toISOString().slice(0, 10);
}

export function formatCurrency(amount) {
  return "$" + amount.toFixed(2);
}

export function slugify(text) {
  return text.toLowerCase().replace(/\s+/g, "-");
}
js
// app.js — 진입점
import { formatDate } from "./utils";

const today = formatDate(new Date());
console.log(today);
js
// Tree Shaking 후 — 번들에 formatDate만 있음
function formatDate(date) {
  return date.toISOString().slice(0, 10);
}
const today = formatDate(new Date());
console.log(today);

Tree Shaking은 app.js에서 가져오지 않았기 때문에 최종 번들에서 formatCurrency와 slugify를 제외했습니다. utils.js 모듈의 크기는 3개 함수에서 1개로 줄었습니다. utils.js에 부작용(예: 전역 초기화)이 있는 경우 Tree Shaking은 사용되지 않는 내보내기도 제거할 수 없습니다.

Webpack에서 Tree Shaking: 구성 및 최적화

Webpack은 프로덕션 모드에서 TerserPlugin을 통해 내장된 Tree Shaking 지원을 포함합니다. Tree Shaking을 활성화하려면 두 가지 조건으로 충분합니다. mode가 production(mode: "production")으로 설정되고 모듈이 ES 구문(import/export)을 사용하는 것입니다. Webpack은 자동으로 사용되지 않는 내보내기를 표시하고 제거를 위해 Terser에 전달합니다.

optimization.webpack.config.js에서 usedExports: true 추가 구성은 모듈 내에서 내보내기 사용에 대한 상세 분석을 활성화합니다. 이 옵션은 어떤 내보내기가 실제로 사용되고 어떤 내보내기가 단순히 제공되는지만(provided) 결정합니다. usedExports와 Terser의 조합은 최대의 데드 코드 제거 효율성을 제공합니다.

Tree Shaking을 위한 Webpack 구성

js
// webpack.config.js — Tree Shaking 구성
module.exports = {
  mode: "production",
  entry: "./src/app.js",
  output: {
    filename: "bundle.js",
  },
  optimization: {
    usedExports: true,
    minimize: true,
    concatenateModules: true,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules\/(?!(my-lib)\/).*/,
        use: {
          loader: "babel-loader",
          options: {
            presets: [
              ["@babel/preset-env", { modules: false }],
            ],
          },
        },
      },
    ],
  },
};

핵심 매개변수는 @babel/preset-env의 modules: false입니다. 기본적으로 Babel은 ES 모듈을 CommonJS로 변환하여 Tree Shaking을 무효화합니다. modules: false는 Babel이 import/export를 변환하는 것을 방지하여 Webpack을 위해 ES 구문을 유지합니다. concatenateModules는 추가로 모듈을 공유 범위로 병합하여 IIFE 수를 줄이고 번들 크기를 줄입니다.

Side effects 문제와 sideEffects 플래그

Side effects(부작용)는 모듈이 가져와질 때 내보내진 값과 관련 없는 작업을 수행하는 것입니다. 전역 스타일(import "./styles.css"), 폴리필(import "core-js/stable"), 전역 변수 초기화 또는 Service Worker 등록이 이에 해당합니다. 모듈에 부작용이 포함된 경우 번들러는 내보내기가 하나도 사용되지 않더라도 번들에서 안전하게 제거할 수 없습니다.

package.json의 sideEffects 플래그는 번들러에게 패키지의 어떤 모듈에 부작용이 없는지 알려줍니다. 모든 모듈이 순수(함수만 내보내기)한 패키지의 경우 "sideEffects": false를 지정해야 합니다. CSS 또는 폴리필이 있는 패키지의 경우 부작용이 있는 파일 경로의 배열을 지정합니다: "sideEffects": ["*.css"]. 이 플래그가 없으면 Tree Shaking은 사용되지 않는 함수도 제거하지 않습니다.

코드에서 부작용을 식별하는 방법

모듈에 부작용이 있는지 확인하려면 다음 질문을 해보세요. 이 가져오기가 값을 내보내는 것과 관련 없는 작업을 수행합니까? import "./styles.css"는 CSS를 DOM에 추가합니다. 이는 부작용입니다. import { throttle } from "lodash-es"에는 부작용이 없습니다. 단지 throttle 함수를 사용 가능하게 만듭니다. 폴리필(import "core-js/stable")에는 부작용이 있습니다. 전역 프로토타입을 수정합니다.

자체 모듈의 경우 다음을 권장합니다. 스타일과 폴리필을 별도의 진입점으로 분리하고, 순수 유틸리티(부작용 없는 함수)와 부작용이 있는 모듈(초기화, 로깅, Service Worker 등록)을 분리합니다. 최상위 프로젝트 package.json에서는 모든 모듈이 순수한 경우에만 "sideEffects": false를 지정합니다. 스타일이 있는 경우 "sideEffects": ["*.css"]를 정확히 지정합니다.

sideEffects 구성 예시

json
{
  "name": "my-ui-lib",
  "version": "2.1.0",
  "sideEffects": [
    "*.css",
    "polyfills.js"
  ],
  "module": "dist/index.esm.js",
  "main": "dist/index.cjs.js"
}

"sideEffects": ["*.css", "polyfills.js"]는 모든 CSS 파일에 부작용이 있고(제거 불가) polyfills.js에도 부작용이 있음을 의미합니다. 패키지의 다른 모든 JS 파일은 순수하며 안전하게 shake할 수 있습니다. module 필드는 Tree Shaking을 위해 번들러가 CommonJS 버전(main) 대신 사용해야 하는 ES 버전 패키지의 경로를 지정합니다.

React Native 및 Metro에서 Tree Shaking

React Native와 Metro Bundler는 제한된 버전의 Tree Shaking을 지원합니다. Metro는 Webpack처럼 usedExports에 대한 완전한 정적 분석을 수행하지 않습니다. 대신 Metro는 축소(minification) 중에 Terser에 의존하여 모듈의 사용되지 않는 부분을 제거합니다. 이 접근 방식의 효과는 Webpack의 완전한 Tree Shaking보다 낮습니다.

React Native 프로젝트의 최대 최적화를 위해 다음을 권장합니다: ES 모듈이 있는 라이브러리 사용(package.json의 module 필드), 디버그 코드 제거를 위한 babel-plugin-transform-remove-console 추가, Terser를 위한 Metro transformer.minifierConfig 구성. 또한 Ram Bundle(번들을 모듈로 분할)은 사용되지 않는 화면의 로딩을 줄입니다.

자주 묻는 질문

왜 Tree Shaking이 CommonJS에서 작동하지 않나요?

CommonJS(require/module.exports)는 정적 분석을 지원하지 않습니다. require는 조건문과 함수 내에서 동적으로 호출될 수 있습니다. 번들러는 모듈의 어떤 부분이 실제로 사용되는지 판단할 수 없습니다. 정적 import/export가 있는 ES 모듈만 Tree Shaking을 가능하게 합니다.

TypeScript에서 Tree Shaking을 사용할 수 있나요?

TypeScript는 tsconfig.json이 ES 모듈용으로 구성된 경우("module": "esnext") Tree Shaking과 완전히 호환됩니다. TypeScript 컴파일러는 import/export를 CommonJS로 변환하지 않고 유지해야 합니다. @babel/preset-typescript 및 modules: false를 사용한 Babel도 ES 모듈을 Webpack에 올바르게 전달합니다.

Tree Shaking이 작동했는지 확인하는 방법은?

Webpack Bundle Analyzer는 번들 구성을 대화형 다이어그램으로 시각화하는 플러그인입니다. 라이브러리가 번들에 존재하지만 해당 함수가 사용되지 않는 경우 Tree Shaking이 작동하지 않은 것입니다. 출력 파일을 분석할 수도 있습니다. grep을 통해 번들 텍스트에서 사용되지 않는 내보내기를 검색합니다.

왜 Lodash가 기본적으로 tree-shake되지 않나요?

Lodash v4는 CommonJS 패키지로 배포됩니다. Tree Shaking을 위해서는 lodash-es — 라이브러리의 ES 버전을 사용해야 합니다. import throttle from "lodash/throttle"를 import { throttle } from "lodash-es"로 바꾸고 lodash를 lodash-es로 대체하도록 Webpack에서 resolve.alias를 구성합니다.

Tree Shaking이 빌드 시간에 영향을 미치나요?

Tree Shaking은 종속성 그래프 분석 및 사용된 내보내기 표시 단계를 추가하므로 빌드 시간이 약간 증가합니다(5~15%). 개발 모드에서는 Tree Shaking이 일반적으로 속도를 위해 비활성화됩니다. 프로덕션에서는 추가 시간이 번들 크기의 상당한 감소로 정당화됩니다.

요약

  • Tree Shaking — 빌드 시 사용되지 않는 ES 내보내기를 자동으로 제거하여 번들 크기를 30~60% 감소
  • ES 모듈 — 정적 분석을 지원하는 유일한 형식, CommonJS는 Tree Shaking에 적합하지 않음
  • Webpack과 Rollup은 프로덕션 모드에서 usedExports와 Terser를 통해 Tree Shaking 제공
  • 부작용이 모듈 제거를 차단함, 순수 라이브러리의 경우 package.json의 sideEffects: false 플래그가 문제 해결
  • Babel은 ES 모듈이 CommonJS로 변환되는 것을 방지하기 위해 modules: false로 구성해야 함
  • React Native Metro는 축소 단계에서 Terser에 의존하는 제한된 Tree Shaking을 가짐

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기