[React] react lazy 지연로딩 예제

react lazy 사용하여 컴포넌트 간의 데이터를 효율적으로 공유하기

Posted by lim.Chuck on November 21, 2024

[React]

  1. [React] vite React설치
  2. Project initial setting
  3. Hooks && Library
    1. [React] Javascript 파일을 Typescript Import해보자
    2. [React] react-router-dom 알아보고 사용하자
    3. [React] child 컴포넌트에서 함수호출하기
    4. [React] useCallback
    5. [React] framer-motion 알아보고 사용하자
    6. [React] Context API
    7. [React] react lazy 지연로딩 예제
    8. [React] react에서 캐싱처리를 위한 react queryd와 indexDB 비교분석
    9. [React] 스타일 라이브러리 styled-components stitches 비교
  4. [React] 프론트 에러추적 도구 Sentry 사용해보기
  5. [React] React 19 주요 변경점
  6. [React] DOM이란 무엇이고? 가상 DOM이란 무엇인가?
  7. [React] preact 알아보기

React의 React.lazy는 코드 스플리팅(Code Splitting)을 통해 애플리케이션의 성능을 최적화하기 위한 기능입니다. 컴포넌트를 미리 로드하지 않고 필요할 때 동적으로 불러올 수 있도록 도와줍니다. 이를 통해 초기 로딩 속도를 개선하고 사용자가 특정 페이지나 기능을 요���할 때만 관련 코드를 로드하게 됩니다.


기본 문법

1
const HeavyComponent = React.lazy(() => import("./components/HeavyComponent"));

React.lazy는 동적 import()를 사용해 비동기적으로 컴포넌트를 로드합니다. 이는 JavaScript의 코드 스플리팅을 구현하는 데 중요한 역할을 합니다.


사용 방법

React.lazy를 사용할 때, Suspense 컴포넌트를 함께 사용해야 합니다. Suspense는 lazy 컴포넌트가 로드될 때까지 로딩 상태를 표시할 수 있는 fallback UI를 제공합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import React, { useState, Suspense } from "react";
import { motion } from "framer-motion";

// 지연 로딩할 컴포넌트를 lazy로 import
const HeavyComponent = React.lazy(() => import("./components/HeavyComponent"));

function App() {
  const [showComponent, setShowComponent] = useState(false);

  return (
    <div
      style={{
        display: "flex",
        flexDirection: "column",
        justifyContent: "center",
        alignItems: "center",
        height: "100vh",
        gap: "20px",
      }}
    >
      <motion.button
        whileHover={{ scale: 1.1 }}
        whileTap={{ scale: 0.9 }}
        onClick={() => setShowComponent(!showComponent)}
        style={{
          padding: "10px 20px",
          backgroundColor: "blue",
          color: "white",
          border: "none",
          borderRadius: "5px",
          cursor: "pointer",
        }}
      >
        컴포넌트 {showComponent ? "숨기기" : "보이기"}
      </motion.button>

      <Suspense fallback={<div>로딩중...</div>}>
        {showComponent && <HeavyComponent />}
      </Suspense>
    </div>
  );
}

export default App;

react lazy 금방 로딩이 되어 크롬에서 네트워크속도를 낮추어 실행해보았음

  • React.lazy(): 컴포넌트를 동적으로 가져오기 위한 함수.
  • Suspense: lazy로 불러온 컴포넌트가 로드될 때 보여줄 fallback UI를 지정.

장점

  1. 초기 로딩 속도 개선
    필요한 코드만 로드하기 때문에 초기 번들 크기가 작아지고 로딩 시간이 줄어듭니다.

  2. 코드 스플리팅 자동화
    Webpack과 같은 번들러가 import()를 감지하여 자동으로 코드 스플리팅을 수행합니다.

  3. 사용자 경험 향상
    로딩 화면을 제공함으로써 사용자가 애플리케이션이 멈췄다고 느끼는 것을 방지합니다.

단점

  1. 추가 네트워크 요청
    각 lazy 컴포넌트마다 별도 요청 발생 너무 많은 lazy 사용시 HTTP 요청 증가

주의사항

  1. 서버 사이드 렌더링(SSR)
    React.lazy는 클라이언트 사이드 렌더링(CSR)에서만 동작합니다. 서버 사이드 렌더링 환경에서는 대체 로직이 필요합니다.

  2. Error Boundary 필요
    lazy로 로드하는 컴포넌트에서 오류가 발생할 경우 애플리케이션이 중단될 수 있으므로 Error Boundary를 설정하는 것이 좋습니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
    import React, { Suspense } from "react";
    
    const LazyComponent = React.lazy(() => import("./LazyComponent"));
    
    class ErrorBoundary extends React.Component {
      constructor(props) {
        super(props);
        this.state = { hasError: false };
      }
    
      static getDerivedStateFromError(error) {
        return { hasError: true };
      }
    
      render() {
        if (this.state.hasError) {
          return <h1>Something went wrong.</h1>;
        }
        return this.props.children;
      }
    }
    
    function App() {
      return (
        <ErrorBoundary>
          <Suspense fallback={<div>Loading...</div>}>
            <LazyComponent />
          </Suspense>
        </ErrorBoundary>
      );
    }
    
  3. Fallback UI
    fallback UI를 지정하지 않으면 사용자에게 로딩 중이라는 피드백이 제공되지 않아 불편을 줄 수 있습니다.


언제 사용하면 좋을까?

  • 라우팅이 많은 애플리케이션: 페이지 단위로 코드를 분리할 때 적합합니다.
  • 대형 컴포넌트: 자주 사용되지 않거나 초기 화면에 필요하지 않은 컴포넌트는 lazy 로드하는 것이 좋습니다.
  • 모듈화된 라이브러리: 특정 시점에만 필요한 외부 라이브러리의 로드에도 유용합니다.

마무리 정리🧹

lazy를 사용하면 각 컴포넌트가 별도의 JavaScript 파일(청크)로 분리되어 빌드되고, 이 파일들은 필요할 때마다 별도의 HTTP 요청으로 다운로드됩니다.

예를 들어보겠습니다:

1. lazy 없이 단일 번들로 빌드할 때

1
2
3
4
5
6
7
// 모든 컴포넌트가 하나의 번들로 합쳐짐
import Dashboard from "./Dashboard";
import Settings from "./Settings";
import Profile from "./Profile";

// 빌드 결과: main.js (3MB)
// HTTP 요청 1번으로 모든 것을 다운로드

2. lazy를 과도하게 사용할 때

1
2
3
4
5
6
7
8
9
10
11
// 각 컴포넌트가 별도의 청크로 분리됨
const Dashboard = lazy(() => import("./Dashboard")); // chunk1.js (1MB)
const Settings = lazy(() => import("./Settings")); // chunk2.js (1MB)
const Profile = lazy(() => import("./Profile")); // chunk3.js (1MB)

// HTTP 요청이 컴포넌트마다 발생
// - 메인 페이지 접속: main.js 다운로드 (요청 1번)
// - 대시보드 클릭: chunk1.js 다운로드 (요청 1번)
// - 설정 클릭: chunk2.js 다운로드 (요청 1번)
// - 프로필 클릭: chunk3.js 다운로드 (요청 1번)
// 총 HTTP 요청 4번!

문제점:

  1. 많은 HTTP 요청

    • 각 요청마다 네트워크 오버헤드 발생
    • 서버 부하 증가
    • 특히 모바일 환경에서 성능 저하 가능성
  2. 캐싱 비효율

1
2
3
4
5
6
7
// 너무 작은 단위로 분리한 경우
const Button = lazy(() => import("./Button")); // chunk4.js (10KB)
const Card = lazy(() => import("./Card")); // chunk5.js (15KB)
const Input = lazy(() => import("./Input")); // chunk6.js (12KB)

// 많은 작은 파일들 → 캐시 관리 복잡
// 오히려 성능 저하 가능성

좋은 예시:

1
2
3
4
5
// 관련된 컴포넌트들을 함께 그룹화
const AdminFeatures = lazy(() => import("./features/admin")); // 관련 컴포넌트들을 하나의 청크로
const UserDashboard = lazy(() => import("./features/dashboard")); // 의미 있는 단위로 분리

// HTTP 요청이 줄어들고, 더 효율적인 캐싱 가능

React.lazy는 애플리케이션의 성능 최적화와 사용자 경험 개선에 강력한 도구입니다. 필요에 따라 활용해 보세요! 😊