TypeScript

[React] React.lazy와 Suspense

코샵 2024. 12. 26. 10:48
반응형

소개

웹 애플리케이션이 커질수록 초기 로딩 시간도 늘어납니다. React.lazy와 Suspense를 사용하면 필요한 시점에 코드를 로드하여 초기 로딩 시간을 줄일 수 있습니다. 쉽게 말해, 처음부터 모든 것을 로드하지 않고 필요할 때 필요한 부분만 가져오는 것입니다.

React.lazy란?

React.lazy는 컴포넌트를 동적으로 불러올 수 있게 해주는 기능입니다. 쉽게 말해, 필요한 순간에 컴포넌트를 로드하는 것입니다.

기본 사용법

// 기존 방식
import HeavyComponent from './HeavyComponent';

// React.lazy 사용
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));

Suspense란?

Suspense는 컴포넌트가 로드되는 동안 로딩 화면을 보여주는 기능입니다.

기본 사용법

import React, { Suspense } from 'react';

function App() {
  return (
    <Suspense fallback={<div>로딩중...</div>}>
      <HeavyComponent />
    </Suspense>
  );
}

실제 사용 예시

1. 탭 메뉴 구현

// 각 탭 컴포넌트를 lazy로 불러오기
const HomeTab = React.lazy(() => import('./HomeTab'));
const ProfileTab = React.lazy(() => import('./ProfileTab'));
const SettingsTab = React.lazy(() => import('./SettingsTab'));

function TabMenu() {
  const [activeTab, setActiveTab] = useState('home');

  return (
    <div>
      {/* 탭 버튼들 */}
      <div>
        <button onClick={() => setActiveTab('home')}>홈</button>
        <button onClick={() => setActiveTab('profile')}>프로필</button>
        <button onClick={() => setActiveTab('settings')}>설정</button>
      </div>

      {/* 탭 내용 */}
      <Suspense fallback={<div>탭 로딩중...</div>}>
        {activeTab === 'home' && <HomeTab />}
        {activeTab === 'profile' && <ProfileTab />}
        {activeTab === 'settings' && <SettingsTab />}
      </Suspense>
    </div>
  );
}

2. 모달 구현

const DetailModal = React.lazy(() => import('./DetailModal'));

function ProductList() {
  const [showModal, setShowModal] = useState(false);

  return (
    <div>
      <button onClick={() => setShowModal(true)}>
        상세 정보 보기
      </button>

      {showModal && (
        <Suspense fallback={<div>모달 로딩중...</div>}>
          <DetailModal 
            onClose={() => setShowModal(false)} 
          />
        </Suspense>
      )}
    </div>
  );
}

3. 페이지 라우팅

import { BrowserRouter, Routes, Route } from 'react-router-dom';

const Home = React.lazy(() => import('./pages/Home'));
const Products = React.lazy(() => import('./pages/Products'));
const About = React.lazy(() => import('./pages/About'));

function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<div>페이지 로딩중...</div>}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/products" element={<Products />} />
          <Route path="/about" element={<About />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

사용자 경험 개선하기

1. 로딩 화면 꾸미기

function LoadingSpinner() {
  return (
    <div className="loading-spinner">
      <div className="spinner"></div>
      <p>잠시만 기다려주세요...</p>
    </div>
  );
}

function App() {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <MyComponent />
    </Suspense>
  );
}

2. 지연 로딩 추가하기

function DelayedSuspense({ children, delay = 500 }) {
  const [show, setShow] = useState(false);

  useEffect(() => {
    const timer = setTimeout(() => setShow(true), delay);
    return () => clearTimeout(timer);
  }, [delay]);

  return (
    <Suspense 
      fallback={
        show ? <LoadingSpinner /> : null
      }
    >
      {children}
    </Suspense>
  );
}

주의사항

1. 너무 작은 컴포넌트는 분할하지 않기

// ❌ 너무 작은 컴포넌트
const Button = React.lazy(() => import('./Button'));

// ✅ 의미 있는 크기의 컴포넌트
const ComplexDashboard = React.lazy(() => import('./ComplexDashboard'));

2. 적절한 분할 포인트 선택하기

// ✅ 좋은 분할 포인트 예시
const AdminDashboard = React.lazy(() => import('./AdminDashboard'));
const UserProfile = React.lazy(() => import('./UserProfile'));
const Analytics = React.lazy(() => import('./Analytics'));

성능 향상 확인하기

  1. 개발자 도구의 Network 탭 확인
  2. Lighthouse 성능 점수 비교
  3. 실제 사용자 피드백 수집

결론

React.lazy와 Suspense는 큰 규모의 애플리케이션에서 초기 로딩 시간을 줄이는 데 매우 효과적입니다. 하지만 모든 것을 분할하는 것이 아니라, 적절한 위치에 적용하는 것이 중요합니다.