본문 바로가기
일상이 개발

React 앱 성능 향상 필수 전략! 코드 스플리팅과 Lazy Loading 제대로 적용하기

by 디어노미 2025. 4. 10.
반응형

🚀 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 로딩 가능

📉 성능 개선 지표 확인 방법

코드 스플리팅의 효과는 실제로 숫자로 확인 가능합니다.

  1. Chrome DevTools → Network 탭
    • 초기 로딩에서 JS 청크가 어떻게 분리됐는지 확인 가능
  2. Lighthouse 측정
    • TTI (Time to Interactive), FCP (First Contentful Paint) 개선 여부 확인
  3. Webpack Bundle Analyzer
    • 어떤 모듈이 어떤 청크에 포함되어 있는지 시각화
npm install --save-dev webpack-bundle-analyzer

🚫 주의할 점 & 한계

  1. 너무 작은 컴포넌트까지 lazy 처리 → 오히려 렌더링 병목
  2. Suspense fallback이 자주 보이면 UX 저하
  3. SEO가 필요한 콘텐츠는 SSR 방식과 조합 필요
  4. Lazy 로딩된 컴포넌트에서의 에러 처리 필요 (ErrorBoundary 활용)

✅ 마무리 정리

코드 스플리팅과 Lazy Loading은 성능 최적화를 위한 매우 강력한 도구입니다. 하지만 무조건 분리하는 것이 아니라, 사용자의 UX 흐름과 인터랙션 맥락에 따라 전략적으로 적용하는 것이 중요합니다.

✔ 핵심 체크리스트

  • React.lazy + Suspense 기본 구조 익히기
  • 라우트 기반 코드 스플리팅 필수 적용
  • 대형 라이브러리, 드문 UI 요소에 lazy 적용
  • dynamic import로 일반 모듈 동적 로딩 가능
  • 성능 지표로 효과 측정하고 조절

"필요할 때 필요한 만큼만 로딩하자!" 이것이 진정한 프론트엔드의 미덕입니다 ✨

반응형