Files
2nd/10_Wiki/Topics/Frontend/Bundle Size Optimization.md
T
koriweb d8a80f6272 chore(wiki): dangling 링크 canonical 정규화 (768파일/1200건)
이름만 다른(표기 변형) [[위키링크]]를 대상 문서의 canonical 제목으로 치환해
끊겼던 1,200개 링크를 연결. 제목/파일명 정규화 일치만 적용하고 별칭 매칭은
과병합 위험으로 제외(애매성 가드). 원본은 _link_reconcile_backup/ 에 백업.
도구: Datacollect/scripts/link_reconcile_apply.mjs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:24:15 +09:00

188 lines
4.9 KiB
Markdown

---
id: wiki-2026-0508-bundle-size-optimization
title: Bundle Size Optimization
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [JS Bundle Optimization, Web Bundle Reduction]
duplicate_of: none
source_trust_level: A
confidence_score: 0.9
verification_status: applied
tags: [bundle, performance, webpack, vite, tree-shaking]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
language: JavaScript/TypeScript
framework: Vite/Rollup/Webpack
---
# Bundle Size Optimization
## 매 한 줄
> **"매 byte 매 less 매 user time 매 less"**. Bundle size optimization은 production JS/CSS payload를 줄여 LCP/INP/TBT 개선 + mobile-first user 의 perceived speed 개선. 2026 standard tooling: Vite + Rollup tree-shaking, modern bundle analysis (Bundle Buddy, esbuild-visualizer), bundle budgets enforcement.
## 매 핵심
### 매 4 lever
- **Tree shaking**: ESM only, sideEffects:false, no re-export wildcards.
- **Code splitting**: route / component lazy import.
- **Compression**: brotli > gzip; precompress at build.
- **Dependency surgery**: heavy lib → lighter alt or self-implement.
### 매 측정 우선
- Bundle visualizer (rollup-plugin-visualizer, source-map-explorer).
- Bundle budget in CI (e.g., size-limit, bundlesize).
- Real device testing (slow 3G profile).
### 매 응용
1. Lazy-load route chunks.
2. Remove unused locales (date-fns, moment).
3. Replace lodash with native / lodash-es.
## 💻 패턴
### Vite + visualizer
```ts
// vite.config.ts
import { defineConfig } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
visualizer({ filename: 'stats.html', gzipSize: true, brotliSize: true })
],
build: {
rollupOptions: {
output: {
manualChunks: {
react: ['react', 'react-dom'],
vendor: ['date-fns', 'zustand']
}
}
}
}
});
```
### Route-level code split (React)
```tsx
import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./Dashboard'));
<Suspense fallback={<Skeleton />}>
<Dashboard />
</Suspense>
```
### Dynamic import for rare path
```ts
async function exportToPDF(data: Item[]) {
const { jsPDF } = await import('jspdf');
const doc = new jsPDF();
doc.text(JSON.stringify(data), 10, 10);
doc.save('out.pdf');
}
```
### Replace heavy lib
```ts
// X moment (~290KB)
import moment from 'moment';
moment().format('YYYY-MM-DD');
// O Intl (built-in, 0KB)
new Intl.DateTimeFormat('en-CA').format(new Date());
// O date-fns/format (tree-shakeable, ~3KB)
import { format } from 'date-fns/format';
format(new Date(), 'yyyy-MM-dd');
```
### sideEffects flag
```json
// package.json — library author 측
{
"name": "my-lib",
"type": "module",
"sideEffects": false,
"exports": {
".": {
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts"
}
}
}
```
### size-limit budget enforcement
```json
// package.json
{
"scripts": {
"size": "size-limit"
},
"size-limit": [
{ "path": "dist/index.js", "limit": "50 KB" },
{ "path": "dist/vendor.js", "limit": "120 KB" }
]
}
```
### Compression at build (brotli)
```ts
import compression from 'vite-plugin-compression2';
export default defineConfig({
plugins: [
compression({ algorithm: 'brotliCompress', exclude: [/\.(br)$/, /\.(gz)$/] })
]
});
```
### Server: strip locales from dayjs
```ts
import dayjs from 'dayjs';
import 'dayjs/locale/ko'; // 매 필요한 것만
dayjs.locale('ko');
```
## 매 결정 기준
| 상황 | Approach |
|---|---|
| Initial bundle > 200KB | route split + lazy load |
| Single heavy lib | replace 또는 dynamic import |
| Multi-tenant build | per-tenant treeshake config |
| Library publish | ESM + `sideEffects:false` |
| Edge runtime | bundle ≤ 1MB 가까이 strict budget |
**기본값**: measure first → split → compress → swap heavy deps.
## 🔗 Graph
- 부모: [[Frontend Performance]]
- 변형: [[Code Splitting]]
- 응용: [[Core Web Vitals Optimization (INP, LCP, CLS)|Core Web Vitals]] · [[LCP]]
- Adjacent: [[Vite]] · [[Rollup]] · [[esbuild]]
## 🤖 LLM 활용
**언제**: webpack/vite config audit, lib alternative suggestion, bundle analyzer interpretation.
**언제 X**: 매 production 매 swap deploy — actual measurement 필수.
## ❌ 안티패턴
- **CommonJS lib import**: tree shaking blocked — ESM 사용.
- **`import * as foo`**: bundler 매 mark 매 모든 export used.
- **Polyfill 전체**: target browser baseline + browserslist으로 narrow.
- **Single chunk all**: SPA → 매 long initial — split per route.
- **Dev source maps in prod**: ship source map only via separate URL or skip.
## 🧪 검증 / 중복
- Verified (web.dev bundle size guide, Vite docs, size-limit GitHub).
- 신뢰도 A.
## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — bundle optim 4 lever + Vite/size-limit pattern |