---
id: wiki-2026-0508-code-splitting
title: Code Splitting
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [Bundle Splitting, Dynamic Import]
duplicate_of: none
source_trust_level: A
confidence_score: 0.9
verification_status: applied
tags: [bundling, performance, webpack, vite, frontend]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
  language: TypeScript
  framework: Vite / Webpack / Next.js
---

# Code Splitting

## 매 한 줄
> **"매 bundle 의 chunk 의 분리, on-demand load 의 first-paint 단축"**. Code Splitting 은 dynamic `import()` + bundler chunking 의 결합 의 매 monolithic JS bundle 의 분해. 2026 Vite/Rspack/Turbopack 시대 에 route-level + component-level + vendor split 의 매 표준.

## 매 핵심

### 매 splitting 의 axis
- **Route-based**: 각 page 의 separate chunk — 매 SPA / Next.js 의 default
- **Component-based**: heavy component 의 lazy (Modal, Chart, Editor)
- **Vendor**: `node_modules` 의 separate chunk — long-term cache
- **Dynamic feature**: locale, A/B variant 의 conditional load

### 매 mechanism
- **Dynamic `import()`**: ECMAScript spec — Promise 의 return
- **Bundler 의 split point detection**: `import()` 호출 의 chunk boundary
- **Manifest**: hashed filename 의 mapping
- **Preload / prefetch**: `<link rel="preload/prefetch">` hint

### 매 cost trade-off
- **Pro**: initial bundle 의 작아짐 → faster TTI
- **Con**: extra HTTP request, waterfall 위험 — preload 의 mitigate

### 매 응용
1. Route-level lazy (React Router, Next.js).
2. Modal / dialog (open 시 load).
3. Heavy editor (Monaco, CodeMirror).
4. Chart library (Recharts, ECharts).
5. i18n locale chunk.

## 💻 패턴

### React.lazy + Suspense
```tsx
import { lazy, Suspense } from 'react';

const Settings = lazy(() => import('./Settings'));

export default function App() {
  return (
    <Suspense fallback={<Spinner />}>
      <Settings />
    </Suspense>
  );
}
```

### Next.js dynamic
```tsx
import dynamic from 'next/dynamic';

const Chart = dynamic(() => import('@/components/Chart'), {
  loading: () => <p>Loading chart...</p>,
  ssr: false, // browser-only
});

export default function Dashboard() {
  return <Chart data={data} />;
}
```

### Route-level (React Router v6)
```tsx
import { lazy } from 'react';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';

const router = createBrowserRouter([
  { path: '/', lazy: () => import('./routes/Home') },
  { path: '/about', lazy: () => import('./routes/About') },
  { path: '/admin/*', lazy: () => import('./routes/Admin') },
]);

export default function App() {
  return <RouterProvider router={router} />;
}
```

### Vite의 manualChunks
```ts
// vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          react: ['react', 'react-dom'],
          ui: ['@radix-ui/react-dialog', '@radix-ui/react-tabs'],
          charts: ['recharts', 'd3'],
        },
      },
    },
  },
});
```

### Webpack magic comment
```ts
const Editor = () => import(
  /* webpackChunkName: "editor" */
  /* webpackPrefetch: true */
  './Editor'
);
```

### Conditional import (locale)
```ts
async function loadLocale(lang: string) {
  const messages = await import(`./locales/${lang}.json`);
  return messages.default;
}
```

### Preload critical chunk
```html
<link rel="modulepreload" href="/assets/Settings-abc123.js">
```

```tsx
// React: preload on hover
<Link
  to="/settings"
  onMouseEnter={() => import('./Settings')}
>
  Settings
</Link>
```

### Module Federation (micro-frontend)
```ts
// host webpack.config.js
new ModuleFederationPlugin({
  remotes: {
    checkout: 'checkout@https://cdn/checkout/remoteEntry.js',
  },
});

// usage
const Checkout = lazy(() => import('checkout/Cart'));
```

## 매 결정 기준
| 상황 | Approach |
|---|---|
| SPA, multi-page | Route-based split |
| Heavy modal / editor | `lazy()` + Suspense |
| Vendor lib stable | manualChunks vendor split |
| SSR + browser-only | `dynamic({ ssr: false })` |
| Independent deploy | Module Federation |
| Hover-triggered | preload on intent |

**기본값**: route-based + heavy-component lazy + vendor chunk.

## 🔗 Graph
- 부모: [[Bundling]] · [[Web Performance]]
- 변형: [[Tree Shaking]] · [[Module Federation]]
- 응용: [[Lazy Loading]] · [[Route-based Splitting]]
- Adjacent: [[Vite]] · [[Webpack]] · [[Next.js]] · [[Suspense]]

## 🤖 LLM 활용
**언제**: route lazy, heavy component 의 split, vendor chunking 코드 generation.
**언제 X**: 매 small app (10kb 의 split 의 overhead 가 더 큼).

## ❌ 안티패턴
- **너무 fine-grained split**: 100개 chunk → request flood — 매 ~10-30 chunk 의 sweet spot.
- **Waterfall**: A → B → C 의 sequential — 매 parallel preload.
- **Vendor chunk 의 무한 growth**: dependency 추가 시 cache invalidation — 매 stable lib 만 vendor.
- **Suspense 의 boundary 누락**: error / blank screen.
- **`ssr: false` 의 abuse**: SEO 손실.

## 🧪 검증 / 중복
- Verified (Vite docs, Webpack docs, Next.js docs, React 19).
- 신뢰도 A.

## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — splitting strategies + bundler config + MF |
