본문으로 건너뛰기
Advertisement

18.4 번들 최적화 — Tree Shaking과 타입 스트립핑

Tree Shaking과 TypeScript

Tree shaking은 사용하지 않는 코드를 번들에서 제거합니다. TypeScript 코드가 제대로 tree shaking되려면 주의할 점이 있습니다.

// ❌ 사이드 이펙트가 있어 tree shaking 불가
export function util() { /* ... */ }
console.log('모듈 로드됨') // 사이드 이펙트!

// ✅ 순수 함수형 내보내기 — tree shaking 가능
export function util() { /* ... */ }
export function anotherUtil() { /* ... */ }

package.json sideEffects 설정

{
"sideEffects": false,
// 또는 사이드 이펙트가 있는 파일만 명시
"sideEffects": [
"*.css",
"./src/polyfills.ts",
"./src/global-styles.ts"
]
}

TypeScript 타입 스트립핑 (Type Stripping)

타입 스트립핑이란?
타입 주석만 제거하고 TypeScript를 직접 실행하는 방식
트랜스파일이 아니라 타입 정보만 지워 빠른 실행 가능

지원 도구:
- Node.js 22.6+ (--experimental-strip-types 플래그)
- Node.js 23.6+ (기본 지원)
- tsx (esbuild 기반)
- ts-node --transpile-only
# Node.js 22.6+ — 타입 스트립핑
node --experimental-strip-types src/index.ts

# Node.js 23.6+ — 기본 지원
node src/index.ts

# tsx — 빠른 실행 (권장)
npx tsx src/index.ts

tsconfig 번들 최적화

// tsconfig.json — 라이브러리 제작자용
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext", // ESM 모듈 — tree shaking 가능
"moduleResolution": "bundler",
"declaration": true, // .d.ts 생성
"declarationMap": true, // 소스맵과 타입 연결
"sourceMap": true,
"strict": true,
"isolatedModules": true, // 파일별 독립 빌드 가능 (Vite 호환)
"verbatimModuleSyntax": true, // import type 강제 — tree shaking 개선
}
}
// verbatimModuleSyntax — 타입 전용 import 명시
// ✅ 올바른 방법 — 번들러가 타입 import 제거 가능
import type { User } from './types'
import { createUser } from './utils'

// ❌ 잘못된 방법 — 번들러가 타입인지 모를 수 있음
import { User, createUser } from './utils'

번들 분석

# webpack-bundle-analyzer
npm install --save-dev webpack-bundle-analyzer

# next.js — @next/bundle-analyzer
npm install --save-dev @next/bundle-analyzer
// next.config.ts
import withBundleAnalyzer from '@next/bundle-analyzer'

const bundleAnalyzer = withBundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
})

export default bundleAnalyzer({
// next.js 설정
})
# 번들 분석 실행
ANALYZE=true npm run build

코드 분할 (Code Splitting)

// 동적 import — 라우트별 코드 분할
const HeavyComponent = lazy(() => import('./HeavyComponent'))
const AdminPanel = lazy(() => import('./AdminPanel'))

// 조건부 로드
async function loadFeature() {
if (needsFeature) {
const { feature } = await import('./feature')
return feature
}
}

// Next.js — 서버/클라이언트 경계로 자동 분할
// app/page.tsx → Server Component (번들에 미포함)
// 'use client' → Client Component (번들에 포함)

라이브러리 번들 크기 최적화

// ❌ 전체 라이브러리 import
import _ from 'lodash'
const result = _.groupBy(users, 'role')

// ✅ 필요한 함수만 import (tree shaking)
import groupBy from 'lodash/groupBy'
const result = groupBy(users, 'role')

// ✅ 더 나은 방법 — 경량 대안 사용
import { groupBy } from 'remeda' // lodash보다 훨씬 작음, TypeScript-first

// 날짜 처리
// ❌ moment.js — 67KB gzipped
// ✅ date-fns — 필요한 함수만 (tree shaking 최적화)
import { format, addDays } from 'date-fns'

// 스키마 검증
// Zod 13KB vs Valibot ~2KB (엣지 환경)

고수 팁

번들 크기 CI 검사

# .github/workflows/bundle-size.yml
- name: Check bundle size
run: |
npm run build
SIZE=$(du -sh dist/ | cut -f1)
echo "Bundle size: $SIZE"

# 크기 제한 초과 시 실패
MAX_SIZE=5242880 # 5MB
ACTUAL=$(du -sb dist/ | cut -f1)
if [ $ACTUAL -gt $MAX_SIZE ]; then
echo "Bundle too large: $ACTUAL bytes (max: $MAX_SIZE)"
exit 1
fi

- name: Bundle size comment
uses: andresz1/size-limit-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
Advertisement