🚀 React 앱에서 코드 스플리팅과 Lazy Loading 제대로 적용하기
프론트엔드 개발에서 사용자 경험(UX)을 높이기 위해 성능 최적화는 필수입니다. React 앱이 커질수록 초기 로딩 속도가 느려지는 현상이 발생하는데, 이 문제를 해결하기 위한 대표적인 방법이 바로 **코드 스플리팅(Code Splitting)**과 **지연 로딩(Lazy Loading)**입니다.
이번 글에서는 React 중급 개발자를 위한 코드 스플리팅의 개념부터 실전 적용 방법, 그리고 주의할 점까지 구체적으로 다뤄보겠습니다.
🎯 왜 코드 스플리팅이 필요한가?
React는 SPA(Single Page Application) 구조이기 때문에 한 번에 모든 코드를 브라우저로 전달하는 구조입니다.
즉, 사용자가 단 하나의 페이지를 보더라도 전체 번들 파일을 받아야 하기 때문에 불필요하게 큰 JS 파일을 다운로드하고 실행하게 됩니다.
이로 인해 다음과 같은 문제가 생깁니다:
- 초기 로딩 속도 저하
- 사용자 체감 성능 악화
- SEO 지표 하락 가능성
- 모바일 환경에서 데이터 낭비
이 문제를 해결하는 것이 코드 스플리팅의 핵심 목표입니다.
📦 코드 스플리팅이란?
코드 스플리팅은 앱의 JavaScript 번들을 여러 개의 청크(chunk)로 나누는 전략입니다.
이렇게 나누면 사용자가 필요한 시점에 필요한 코드만 로드하게 할 수 있습니다.
React에서는 이 기능을 다음과 같은 방식으로 제공합니다:
- React.lazy + Suspense 조합 (클라이언트 렌더링)
- Dynamic import
- Webpack의 코드 스플리팅
- 라우팅 기반 코드 스플리팅 (React Router, Next.js 등)
🧩 React.lazy와 Suspense 기본 사용법
React는 React.lazy() 함수를 통해 동적으로 컴포넌트를 import할 수 있습니다. 이를 사용할 때 Suspense 컴포넌트와 함께 로딩 UI를 지정해줘야 합니다.
import React, { Suspense } from 'react';
const LazyComponent = React.lazy(() => import('./components/LazyComponent'));
function App() {
return (
<div>
<Suspense fallback={<div>로딩 중...</div>}>
<LazyComponent />
</Suspense>
</div>
);
}
✅ 특징
- 초기 렌더링 시 LazyComponent는 청크로 분리되어 필요할 때 로딩됨
- fallback으로 로딩 상태 UI를 지정할 수 있음
🗂 라우팅 기반 코드 스플리팅
사용자 대부분은 특정 페이지에서만 필요한 컴포넌트들을 사용합니다. 이럴 경우 라우터 단위로 lazy loading을 적용하면 효과적입니다.
예: React Router v6 기준
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { Suspense, lazy } from 'react';
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
💡 팁: Suspense는 라우트 레벨에서 감싸는 것이 일반적입니다.
⚙️ dynamic import로 유틸, 모듈 동적 로딩하기
React.lazy는 컴포넌트 로딩에만 쓸 수 있습니다.
유틸 함수나 라이브러리를 동적으로 import하려면 dynamic import 문법을 사용해야 합니다.
async function loadEditor() {
const { Editor } = await import('./components/Editor');
// 동적으로 불러온 모듈 사용
Editor.init();
}
📌 주의: React.lazy는 기본 export만 지원하므로 named export가 필요한 경우는 dynamic import를 사용해야 합니다.
🧠 실전 전략: Lazy Load를 언제 적용할까?
케이스 코드 스플리팅 여부
메인 페이지 구성 요소 | ❌ (초기 렌더링에 포함되어야 함) |
드물게 사용하는 Modal, Tooltip 등 | ✅ lazy load 추천 |
코드 에디터, 차트, WYSIWYG 등 대형 라이브러리 | ✅ 강력 추천 |
페이지 단위 | ✅ 기본 적용 |
Form 내 일부 조건부 필드 | ✅ 조건부로 Lazy 로딩 가능 |
📉 성능 개선 지표 확인 방법
코드 스플리팅의 효과는 실제로 숫자로 확인 가능합니다.
- Chrome DevTools → Network 탭
- 초기 로딩에서 JS 청크가 어떻게 분리됐는지 확인 가능
- Lighthouse 측정
- TTI (Time to Interactive), FCP (First Contentful Paint) 개선 여부 확인
- Webpack Bundle Analyzer
- 어떤 모듈이 어떤 청크에 포함되어 있는지 시각화
npm install --save-dev webpack-bundle-analyzer
🚫 주의할 점 & 한계
- 너무 작은 컴포넌트까지 lazy 처리 → 오히려 렌더링 병목
- Suspense fallback이 자주 보이면 UX 저하
- SEO가 필요한 콘텐츠는 SSR 방식과 조합 필요
- Lazy 로딩된 컴포넌트에서의 에러 처리 필요 (ErrorBoundary 활용)
✅ 마무리 정리
코드 스플리팅과 Lazy Loading은 성능 최적화를 위한 매우 강력한 도구입니다. 하지만 무조건 분리하는 것이 아니라, 사용자의 UX 흐름과 인터랙션 맥락에 따라 전략적으로 적용하는 것이 중요합니다.
✔ 핵심 체크리스트
- React.lazy + Suspense 기본 구조 익히기
- 라우트 기반 코드 스플리팅 필수 적용
- 대형 라이브러리, 드문 UI 요소에 lazy 적용
- dynamic import로 일반 모듈 동적 로딩 가능
- 성능 지표로 효과 측정하고 조절
"필요할 때 필요한 만큼만 로딩하자!" 이것이 진정한 프론트엔드의 미덕입니다 ✨
'일상이 개발' 카테고리의 다른 글
React 앱에서 성능 병목 찾기: DevTools, why-did-you-render, Profiler 실전 분석 (0) | 2025.04.11 |
---|---|
React 앱 성능 병목 실전 분석: DevTools부터 Profiler까지 완전 정복 (0) | 2025.04.11 |
Next.js App Router 환경에서 서버 상태와 URL 라우팅 최적화하는 실전 전략 (0) | 2025.04.10 |
React 상태 동기화 이슈 완전 정복: Form ↔ 전역 상태 ↔ URL 쿼리 흐름 정리 (0) | 2025.04.09 |
Next.js + React Query로 서버 상태 완벽 최적화하기: 실전 적용 가이드 (0) | 2025.04.09 |