C.W.K.
Stream
Lesson 02 of 07 · published

Code Splitting — lazy 라우트, lazy 컴포넌트

~11 min · code-splitting, lazy, performance

Level 0React 입문자
0 XP0/54 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
사용자가 다운로드하는 JS 의 모든 바이트는 그들이 돈 낸 바이트. 절대 방문 안 하는 바이트는 돈 낼 가치 없음. Code splitting 이 각 페이지에 필요한 것만 출하하는 방법.

메커니즘

const Heavy = React.lazy(() => import('./Heavy')). 동적 import() 가 Vite/Rollup 한테 '이건 별도 chunk' 라고 말함. 빌드 시 Heavy 가 자기 JS 파일로. 런타임에 <Heavy /> 렌더될 때만 chunk 로드. <Suspense> 와 결합해서 로딩 state 선언적.

분할할 세 자리

  1. 라우트별 — 모든 페이지가 별도 chunk. 사용자가 홈 페이지 비용 지불; 깊은 링크 클릭이 그 페이지 번들만 로드.
  2. 무거운 컴포넌트별 — 차팅 라이브러리, Markdown 렌더러, 한두 자리에서만 쓰는 50KB 넘는 모든 것.
  3. 모달 / 다이얼로그별 — settings 모달이 form 라이브러리 끌어옴; 사용자가 열 때 lazy-load.

비용

각 chunk 가 네트워크 round trip. 너무 공격적으로 분할 = 작은 요청 waterfall, 큰 번들 하나보다 느릴 수 있음. 30-200KB gzipped chunk 목표; 그보다 작으면 round-trip 비용 정당화 안 됨.

Preloading

사용자가 라우트 네비게이트 직전이라고 알면 그 chunk preload 가능. HTML 의 <link rel="modulepreload" href="...">, 또는 lazy 컴포넌트 그냥 touching 으로 프로그래매틱. Vite 가 entry chunk 의 modulepreload 태그 자동 emit.

사용자 인식 가능 경계에서 분할. 라우트는 명확. 다이얼로그는 명확. 개별 유틸리티 함수 분할은 과설계. 올바른 granularity = '사용자가 별도 경험으로 생각하는 것'.

Code

React Router 와 라우트 레벨 code splitting·tsx
import { lazy, Suspense } from "react";
import { Routes, Route } from "react-router-dom";

// 각 lazy() 호출이 프로덕션 빌드에 별도 chunk 생산.
const Home = lazy(() => import("./pages/Home"));
const Settings = lazy(() => import("./pages/Settings"));
const Admin = lazy(() => import("./pages/Admin"));

export function App() {
  return (
    <Suspense fallback={<p>Loading page…</p>}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/settings" element={<Settings />} />
        <Route path="/admin" element={<Admin />} />
      </Routes>
    </Suspense>
  );
}

// 초기 로드 = 메인 번들 + Home chunk. /settings 네비게이트 시 Settings chunk
// on-demand fetch. Admin 은 admin 방문 전까지 절대 로드 안 됨.
Lazy 모달 — 열 때만 로드·tsx
import { lazy, Suspense, useState } from "react";

const HeavySettingsModal = lazy(() => import("./HeavySettingsModal"));

function Header() {
  const [open, setOpen] = useState(false);
  return (
    <header>
      <button onClick={() => setOpen(true)}>Settings</button>
      {open && (
        <Suspense fallback={<p>Loading settings…</p>}>
          <HeavySettingsModal onClose={() => setOpen(false)} />
        </Suspense>
      )}
    </header>
  );
}

// 사용자가 Settings 클릭 전엔 모달의 번들 다운로드 안 됨.

External links

Exercise

Bootstrap 프로젝트 라우트를 eager import 에서 React.lazy + Suspense 로 변환. npm run build 빌드 + dist/assets/ 에 라우트당 chunk 하나 보이는지 확인. 돌아다니며 Network 패널 봐 — 각 새 라우트가 자기 chunk on-demand fetch. 보너스: 버튼이 트리거하는 lazy 모달 추가 + 첫 열림에만 모달 chunk 로드되는지 확인.
Hint
라우트가 여전히 메인 번들에 출하되면 어디선가 const Home = await import('./Home') 썼을 듯 — 동적 import 좌절시킴. 정확히 lazy(() => import('./Home')) 만 써.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.