🌲Tree Shaking

Yujin JungΒ·2025λ…„ 11μ›” 12일

β€œμ“°μ§€ μ•ŠλŠ” μ½”λ“œλŠ” 버렀라.” β€” λ²ˆλ“€ 속 λΆˆν•„μš”ν•œ μžŽμ‚¬κ·€λ₯Ό ν„Έμ–΄λ‚΄λŠ” 기술


🌲 λ“€μ–΄κ°€λ©°

λŒ€λΆ€λΆ„μ˜ 웹앱은 μˆ˜λ§Žμ€ 라이브러리둜 κ΅¬μ„±λ©λ‹ˆλ‹€.
lodash, date-fns, moment, react-icons, three.js...
μš°λ¦¬κ°€ μ‚¬μš©ν•˜λŠ” μ½”λ“œλ³΄λ‹€ μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” μ½”λ“œκ°€ 훨씬 λ§ŽμŠ΅λ‹ˆλ‹€.

이제 λΈŒλΌμš°μ €κ°€ κ·Έ λͺ¨λ“  μ½”λ“œλ₯Ό λ‹€μš΄λ‘œλ“œν•˜κ³ , νŒŒμ‹±ν•˜κ³ , μ‹€ν–‰ν•΄μ•Ό ν•œλ‹€λ©΄?
ν•„μš”ν•˜μ§€ μ•Šμ€ μ½”λ“œλ„ μ‚¬μš©μž κ²½ν—˜μ— 직접적인 μ„±λŠ₯ λΉ„μš©μ„ μœ λ°œν•˜κ²Œ λ©λ‹ˆλ‹€.

이 문제λ₯Ό ν•΄κ²°ν•˜λŠ” 것이 λ°”λ‘œ Tree Shakingμž…λ‹ˆλ‹€.

β€œλΉŒλ“œ λ‹¨κ³„μ—μ„œ μ‚¬μš©λ˜μ§€ μ•ŠλŠ”(Dead) μ½”λ“œλ₯Ό μ œκ±°ν•΄ λ²ˆλ“€μ„ 더 μž‘κ²Œ λ§Œλ“œλŠ” κΈ°μˆ β€


Tree Shaking κ°œλ…

Tree Shaking은 정적 뢄석(Static Analysis) 을 톡해
μ‚¬μš©λ˜μ§€ μ•ŠλŠ” μ½”λ“œλ₯Ό β€œκ°μ§€ν•˜κ³  μ œκ±°β€ν•˜λŠ” λ²ˆλ“€ μ΅œμ ν™” κΈ°μˆ μž…λ‹ˆλ‹€.

말 κ·ΈλŒ€λ‘œ νŠΈλ¦¬μ—μ„œ μ•ˆ μ“°μ΄λŠ” κ°€μ§€(μ½”λ“œ)λ₯Ό β€œν„Έμ–΄λ‚΄λŠ”(shake)” 과정이죠.

μš©μ–΄μ„€λͺ…
β€œTree”λͺ¨λ“ˆ κ°„μ˜ import/export 의쑴 κ·Έλž˜ν”„
β€œShakeβ€μ‚¬μš©λ˜μ§€ μ•ŠλŠ”(exported but unused) μ½”λ“œ 제거


Tree Shaking μž‘λ™ 원리 (μ‚¬μš©λ˜μ§€ μ•ŠλŠ” μ½”λ“œ 제거 κ³Όμ •)

  • μƒλ‹¨μ˜ Input: { Used: { scope1 } } λŠ” λ²ˆλ“€λŸ¬(Webpack, Rollup λ“±)κ°€ μ‹€μ œλ‘œ μ‚¬μš© 쀑인 ν•¨μˆ˜(scope1) 만 μΆ”μ ν•œλ‹€λŠ” 것을 μ˜λ―Έν•©λ‹ˆλ‹€.
  • μ½”λ“œ λ‚΄μ—μ„œ scope1κ³Ό scope2λŠ” μ‹€ν–‰λ˜μ§€λ§Œ, scope3은 ν˜ΈμΆœλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
  • λ”°λΌμ„œ scope3μ—μ„œ μ‚¬μš©ν•˜λŠ” isNumber() ν•¨μˆ˜λŠ” μ‹€μ œ λΉŒλ“œ κ²°κ³Όμ—μ„œ 제거(Tree Shaking) λ©λ‹ˆλ‹€.
    πŸ‘‰ Tree Shaking은 μ΄λ ‡κ²Œ β€œμ‚¬μš©λ˜μ§€ μ•ŠλŠ” λͺ¨λ“ˆ(import)”을 정적 뢄석(Static Analysis) 을 톡해 κ°μ§€ν•˜κ³ , μ΅œμ’… λ²ˆλ“€μ—μ„œ μ œμ™Έν•¨μœΌλ‘œμ¨ 파일 크기λ₯Ό μ΅œμ†Œν™”ν•˜κ³  μ„±λŠ₯을 μ΅œμ ν™”ν•©λ‹ˆλ‹€.

🌲 μ™œ Tree Shaking이 ν•„μš”ν•œκ°€?

문제 상황

λͺ¨λ“ˆ λ‹¨μœ„μ˜ import/export ꡬ쑰λ₯Ό κ°€μ§€λŠ” ES6(ESM) ν™˜κ²½μ—μ„œλŠ”,
λ‹€μŒμ²˜λŸΌ μž‘μ€ κΈ°λŠ₯ ν•˜λ‚˜λ§Œ 가져와도 사싀상 전체 μ½”λ“œκ°€ λ²ˆλ“€λ  수 μžˆμŠ΅λ‹ˆλ‹€.

// utils.js
export function add(a, b) { return a + b; }
export function sub(a, b) { return a - b; }
export function mul(a, b) { return a * b; }

// main.js
import { add } from './utils.js';
console.log(add(2, 3));

μœ„ μ½”λ“œμ˜ κ²°κ³ΌλŠ”?

πŸ‘‰ 기본적으둜 add, sub, mul μ„Έ ν•¨μˆ˜κ°€ λͺ¨λ‘ λ²ˆλ“€μ— ν¬ν•¨λ©λ‹ˆλ‹€.
μ™œλƒν•˜λ©΄ λ²ˆλ“€λŸ¬κ°€ β€œμ–΄λ–€ ν•¨μˆ˜κ°€ μ‹€μ œλ‘œ μ“°μ΄λŠ”μ§€β€ λΆ„μ„ν•˜μ§€ μ•ŠμœΌλ©΄ 전체 λͺ¨λ“ˆμ„ κ°€μ Έμ˜€κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

Tree Shaking은 이 쀑 μ‚¬μš©λœ ν•¨μˆ˜(add)만 남기고, λ‚˜λ¨Έμ§€(sub, mul)λ₯Ό μ œκ±°ν•©λ‹ˆλ‹€.


Tree Shaking 적용 μ „ν›„ ꡬ쑰 비ꡐ

  • μ™Όμͺ½(Before Tree Shaking): index.jsκ°€ μ—¬λŸ¬ λͺ¨λ“ˆ(Module 1~3, file1~4.js)κ³Ό ν•¨μˆ˜λ“€μ„ μ°Έμ‘°ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ 이 쀑 λ‹€μˆ˜μ˜ λͺ¨λ“ˆκ³Ό ν•¨μˆ˜λŠ” μ‹€μ œ μ‹€ν–‰λ˜μ§€ μ•ŠμŒμ—λ„ λ²ˆλ“€μ— ν¬ν•¨λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.
  • 였λ₯Έμͺ½(After Tree Shaking): λ²ˆλ“€λŸ¬(Webpack/Rollup λ“±)κ°€ 정적 뢄석(Static Analysis) 을 톡해 μ‹€μ œλ‘œ μ°Έμ‘°λ˜λŠ” ν•¨μˆ˜(file3.js β†’ function) 만 남기고, μ‚¬μš©λ˜μ§€ μ•ŠλŠ” μ½”λ“œ(죽은 κ°€μ§€, Dead Code)λ₯Ό λͺ¨λ‘ μ œκ±°ν–ˆμŠ΅λ‹ˆλ‹€.
    πŸ‘‰ Tree Shaking은 μ΄λ ‡κ²Œ β€œν•„μš”ν•œ μ½”λ“œλ§Œ μ‚΄μ•„λ‚¨κ²Œβ€ ν•˜μ—¬ λ²ˆλ“€ 크기λ₯Ό μ΅œμ†Œν™”ν•˜κ³ , λΆˆν•„μš”ν•œ λ‘œλ“œ 및 νŒŒμ‹± λΉ„μš©μ„ μ€„μ΄λŠ” 핡심 μ΅œμ ν™” κΈ°λ²•μž…λ‹ˆλ‹€.

🌲 Tree Shaking의 μž‘λ™ 원리

Tree Shaking은 ES Modules(ESM) ꡬ쑰와 정적 뢄석(Static Analysis) 을 기반으둜 μž‘λ™ν•©λ‹ˆλ‹€.

μž‘λ™ 단계

단계섀λͺ…
β‘  μ˜μ‘΄μ„± κ·Έλž˜ν”„ 생성import/export 관계λ₯Ό 뢄석
β‘‘ μ‚¬μš© 경둜 μΆ”μ μ‹€μ œλ‘œ 참쑰된 export만 β€œν™œμ„±(active)” ν‘œμ‹œ
β‘’ λ―Έμ‚¬μš© μ½”λ“œ μ œκ±°ν™œμ„±λ˜μ§€ μ•Šμ€ exportλŠ” λΉŒλ“œ κ²°κ³Όμ—μ„œ 제거
β‘£ Dead Code Elimination (DCE)μ••μΆ•κΈ°(Uglify/Terser)κ°€ 남은 μ½”λ“œ 쀑 λΆ€μˆ˜νš¨κ³Ό μ—†λŠ” λΆ€λΆ„ 제거

μ˜ˆμ‹œλ‘œ μ΄ν•΄ν•˜κΈ°

// math.js
export const add = (a, b) => a + b;
export const minus = (a, b) => a - b;

// main.js
import { add } from "./math.js";
console.log(add(2, 3));

λΉŒλ“œ κ²°κ³Ό (Before)

const add=(a,b)=>a+b;const minus=(a,b)=>a-b;console.log(add(2,3));

λΉŒλ“œ κ²°κ³Ό (After Tree Shaking)

console.log(((a,b)=>a+b)(2,3));

πŸ‘‰ minus()λŠ” 제거됨


ES Module의 Binding ꡬ쑰

  • ES Module은 λ‹¨μˆœνžˆ 값을 λ³΅μ‚¬ν•˜μ§€ μ•Šκ³ , β€œμ°Έμ‘°(reference)β€λ‘œ μ—°κ²°λœ 바인딩(binding) 을 μœ μ§€ν•©λ‹ˆλ‹€.
  • 즉, ν•œ λͺ¨λ“ˆμ—μ„œ exportν•œ λ³€μˆ˜κ°€ λ³€κ²½λ˜λ©΄, 이λ₯Ό importν•œ λ‹€λ₯Έ λͺ¨λ“ˆμ—μ„œλ„ μžλ™μœΌλ‘œ μ΅œμ‹  값이 λ°˜μ˜λ©λ‹ˆλ‹€.
  • ν•˜μ§€λ§Œ λ°˜λŒ€λ‘œ importν•œ μͺ½μ—μ„œ 값을 λ³€κ²½ν•˜λ €λŠ” μ‹œλ„λŠ” λΆˆκ°€λŠ₯ν•©λ‹ˆλ‹€ (읽기 μ „μš© 바인딩).
    πŸ‘‰ μ΄λŸ¬ν•œ 정적 ꡬ쑰적 μ—°κ²°μ„± 덕뢄에 Tree Shaking 같은 정적 뢄석 기반 μ΅œμ ν™”κ°€ κ°€λŠ₯ν•΄μ§‘λ‹ˆλ‹€.

🌲 Tree Shaking이 κ°€λŠ₯ν•œ μ „μ œ 쑰건

Tree Shaking이 μž‘λ™ν•˜λ €λ©΄ λͺ‡ κ°€μ§€ ν•„μˆ˜ 쑰건이 μžˆμŠ΅λ‹ˆλ‹€.

쑰건섀λͺ…
βœ… ES Module(ESM) μ‚¬μš©import/export ꡬ쑰만 정적 뢄석 κ°€λŠ₯. require()λŠ” λΆˆκ°€λŠ₯
βœ… λΆ€μˆ˜νš¨κ³Ό(side effects)κ°€ μ—†μ–΄μ•Ό 함λͺ¨λ“ˆ λ‘œλ“œμ‹œ μ‹€ν–‰λ˜λŠ” μ½”λ“œκ°€ 있으면 제거 λΆˆκ°€
βœ… λ²ˆλ“€λŸ¬ μ΅œμ ν™” μ˜΅μ…˜ ν™œμ„±ν™”Webpack: usedExports, Rollup: treeshake: true
βœ… Production λͺ¨λ“œ λΉŒλ“œDevelopmentμ—μ„œλŠ” μ΅œμ ν™” 미적용

// webpack.config.js
module.exports = {
  mode: "production",
  optimization: { usedExports: true },
};

🧭 주의:
CommonJS(require)λŠ” 동적 import 경둜λ₯Ό 뢄석할 수 μ—†μ–΄ Tree Shaking이 λΆˆκ°€λŠ₯ν•©λ‹ˆλ‹€.


ESM vs CJS: Tree Shaking이 κ°€λŠ₯ν•œ 이유

  • CJS(CommonJS) λŠ” require()λ₯Ό λŸ°νƒ€μž„μ— μ‹€ν–‰ν•˜λ―€λ‘œ, μ–΄λ–€ λͺ¨λ“ˆμ΄ μ‹€μ œλ‘œ μ‚¬μš©λ μ§€ 정적 뢄석 μ‹œμ μ— μ•Œ 수 μ—†μŠ΅λ‹ˆλ‹€.
  • ESM(ES Modules) 은 import/exportκ°€ 파일 μƒλ‹¨μ—μ„œ μ •μ μœΌλ‘œ μ„ μ–Έλ˜κΈ° λ•Œλ¬Έμ—, λ²ˆλ“€λŸ¬(Webpack, Rollup λ“±)κ°€ 의쑴 κ·Έλž˜ν”„λ₯Ό μ‰½κ²Œ λΆ„μ„ν•˜μ—¬ μ‚¬μš©λ˜μ§€ μ•ŠλŠ” μ½”λ“œλ₯Ό μ•ˆμ „ν•˜κ²Œ 제거(Tree Shaking) ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    πŸ‘‰ Tree Shaking은 ESM의 정적 ꡬ쑰 덕뢄에 κ°€λŠ₯ν•œ μ΅œμ ν™”μž…λ‹ˆλ‹€.

🌲 Tree Shaking vs Dead Code Elimination (DCE)

ν•­λͺ©Tree ShakingDead Code Elimination
λ™μž‘ μ‹œμ λͺ¨λ“ˆ κ·Έλž˜ν”„ λ‹¨κ³„μ½”λ“œ μ••μΆ• 단계
μ£Όμ²΄λ²ˆλ“€λŸ¬ (Webpack, Rollup λ“±)μ••μΆ•κΈ° (Terser, UglifyJS λ“±)
λŒ€μƒλ―Έμ‚¬μš© exportμ‹€ν–‰λ˜μ§€ μ•ŠλŠ” μ½”λ“œ 블둝
핡심 원리정적 μ˜μ‘΄μ„± 뢄석쑰건 λΆ„κΈ° 제거
μ˜ˆμ‹œμ‚¬μš©λ˜μ§€ μ•Šμ€ ν•¨μˆ˜ 제거if (false) { ... } 블둝 제거

κ²°κ΅­ Tree Shaking은 DCE의 μ „μ²˜λ¦¬ 단계라고 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.
β†’ λ²ˆλ“€λŸ¬κ°€ β€œλ¬΄μ˜λ―Έν•œ export”λ₯Ό μ œκ±°ν•˜κ³ ,
β†’ μ••μΆ•κΈ°κ°€ β€œμ‘°κ±΄μƒ μ ˆλŒ€ μ‹€ν–‰λ˜μ§€ μ•ŠλŠ” μ½”λ“œβ€λ₯Ό μ œκ±°ν•©λ‹ˆλ‹€.


🌲 Tree Shaking의 ν•œκ³„μ™€ μ‹€νŒ¨ 사둀

원인섀λͺ…ν•΄κ²°μ±…
⚠️ CommonJSrequire()λŠ” 정적 뢄석 λΆˆκ°€ESM(import/export)둜 λ³€κ²½
⚠️ 동적 import κ²½λ‘œλ¬Έμžμ—΄ μ‘°ν•©μœΌλ‘œ 경둜 κ²°μ • μ‹œ 뢄석 λΆˆκ°€μ •μ  λ¬Έμžμ—΄ μ‚¬μš©
⚠️ λΆ€μˆ˜νš¨κ³Ό(side effect)import μ‹œ μ „μ—­ λ³€μˆ˜ μˆ˜μ •, μ½˜μ†” 좜λ ₯ λ“±λͺ¨λ“ˆμ„ β€œμˆœμˆ˜(pure)β€ν•˜κ²Œ μœ μ§€
⚠️ 배럴 파일(index.js)λͺ¨λ“  λͺ¨λ“ˆμ„ 재export ν•˜λ©΄ λͺ¨λ‘ 포함직접 경둜둜 import
⚠️ ESLint / Babel 트랜슀파일require 기반 λ³€ν™˜ μ‹œ Tree Shaking 깨짐modules: false μ„€μ • μœ μ§€

// babel.config.json
{
  "presets": [["@babel/preset-env", { "modules": false }]]
}


배럴 파일 import 문제 도식

  • index.jsμ—μ„œ λͺ¨λ“  λͺ¨λ“ˆμ„ ν•œκΊΌλ²ˆμ— exportν•˜λ©΄, μ‚¬μš©λ˜μ§€ μ•ŠλŠ” ν•¨μˆ˜κΉŒμ§€ λͺ¨λ‘ λ²ˆλ“€ 파일(bundle.js) 에 ν¬ν•¨λ©λ‹ˆλ‹€.
  • 즉, Tree Shaking이 μ œλŒ€λ‘œ μž‘λ™ν•˜μ§€ μ•Šμ•„ λ²ˆλ“€ 크기가 μ»€μ§€λŠ” 원인이 λ©λ‹ˆλ‹€.
    πŸ‘‰ "배럴 κ΅¬μ‘°λŠ” Tree Shaking의 적"이라고 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

🌲 마무리

Tree Shaking은 β€œμ½”λ“œλ₯Ό μ§€μš΄λ‹€β€λŠ” ν–‰μœ„κ°€ μ•„λ‹™λ‹ˆλ‹€.
β€œμ‚¬μš©μžμ—κ²Œ λ„λ‹¬ν•˜μ§€ μ•ŠλŠ” μ½”λ“œκ°€ μ‘΄μž¬ν•˜μ§€ μ•Šλ„λ‘ ν•œλ‹€β€λŠ” μ›μΉ™μž…λ‹ˆλ‹€.

즉, μ„±λŠ₯ μ΅œμ ν™”μ˜ λͺ©μ μ€ β€œμ½”λ“œλ₯Ό 덜 μ“°λŠ” 것”이 μ•„λ‹ˆλΌ,
β€œμ‚¬μš©ν•˜μ§€ μ•ŠλŠ” μ½”λ“œλ₯Ό μ•„μ˜ˆ λ°°ν¬ν•˜μ§€ μ•ŠλŠ” 것”이죠.

이건 κ²°κ΅­ β€œμ‚¬μš©μžμ˜ λ‹€μš΄λ‘œλ“œ μ‹œκ°„μ„ μ€„μ΄λŠ” UX μ΅œμ ν™”β€μ΄κΈ°λ„ ν•©λ‹ˆλ‹€.

Tree Shaking은 κ°€λ²Όμš΄ μ‚¬μš©μž κ²½ν—˜μ„ μœ„ν•œ 사전 μ²­μ†ŒκΈ°λ‹€.

profile
맀일맀일 μ‘°κΈˆμ”© μ„±μž₯ν•˜λ € λ…Έλ ₯ν•˜λŠ” ν”„λ‘ νŠΈμ—”λ“œ κ°œλ°œμžμž…λ‹ˆλ‹€!

2개의 λŒ“κΈ€

comment-user-thumbnail
2025λ…„ 11μ›” 12일

ESM의 바인딩 ꡬ쑰 μ„€λͺ… 덕뢄에 λͺ¨λ“ˆμ˜ μ΅œμ‹ κ°’μ„ λ°˜μ˜ν•  수 μžˆλ‹€λŠ” 것을 μƒˆλ‘œ μ•Œκ²Œ λ˜μ—ˆκ³  이 바인딩 ꡬ쑰 덕뢄에 μ»€μŠ€ν…€ ν›…μ˜ μž‘λ™ 원리에 λŒ€ν•΄μ„œ 더 깊게 이해할 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

νŠΈλ¦¬μ…°μ΄ν‚Ήμ˜ μž‘λ™ 단계도 ν‘œλ₯Ό 톡해 μ‰½κ²Œ μ•Œ 수 μžˆμ–΄ dead code elimination과의 차이, μˆœμ„œμ— λŒ€ν•΄ λ°°μ›Œκ°‘λ‹ˆλ‹€

λ‹΅κΈ€ 달기
comment-user-thumbnail
2025λ…„ 11μ›” 13일

μ €λŠ” tree shaking에 λŒ€ν•΄ κ°„λ‹¨νžˆ μ΄ν•΄ν•˜κ³  λ„˜μ–΄κ°”λŠ”λ° μ•„μ£Ό μžμ„Ένžˆ μ¨μ£Όμ…”μ„œ 깊게 이해할 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. μ΅œκ·Όμ— 배럴 파일이 μ‚¬μš©λœ μ½”λ“œλ₯Ό 보고 배럴 파일 μ‚¬μš©μ΄ 가독성 ν–₯상에 도움이 λœλ‹€κ³  느껴 λ‹€μŒμ— μ¨λ¨Ήμ–΄λ΄μ•Όκ² λ‹€λŠ” 생각을 ν–ˆλŠ”λ° tree shaking μΈ‘λ©΄μ—μ„œλŠ” 였히렀 μ’‹μ§€ μ•Šκ΅°μš”! μƒˆλ‘œ λ°°μ›Œκ°‘λ‹ˆλ‹€πŸ€“ tree shaking의 μ „μ œ 쑰건과 μ‹€νŒ¨ ν•΄κ²° 사둀 λ“± 싀전적인 뢀뢄듀도 잘 μž‘μ„±ν•΄μ£Όμ…”μ„œ 제 ν”„λ‘œμ νŠΈμ— tree shaking을 μ μš©ν•  일이 μžˆμ„ λ•Œ 또 μ½μ–΄λ³΄λŸ¬ μ™€μ•Όκ² μ–΄μš”πŸ˜Š

λ‹΅κΈ€ 달기