React 최적화 기법

코딩로그·2025년 2월 28일

💤 Lazy & Suspense

🚀 등장 배경: CSR에서 발생하는 속도 저하 문제

React는 대표적인 CSR(Client Side Rendering) 방식의 라이브러리, CSR에서 발생하는 속도 저하 문제를 해결하기 위한 방법들이 고안됨

1️⃣ 초기 로딩 속도 지연 (Initial Load Delay)

CSR 방식에서는 초기 로딩 시 모든 JavaScript 파일을 다운로드하고 실행

React 앱 실행 과정:
1. HTML 파일 요청 (<div id="root"></div>만 존재)
2. 번들된 JavaScript 파일 다운로드 (bundle.js)
3. React가 실행되면서 화면을 동적으로 렌더링

💡 문제:
✔️ React가 실행될 때까지 사용자는 빈 화면을 보게 됨
✔️ 앱이 커질수록 JavaScript 번들이 커져서 로딩 속도가 느려짐

2️⃣ 불필요한 JavaScript 다운로드

CSR에서는 사용자가 보지 않는 페이지의 코드도 모두 로드

예를 들어, 홈(Home) 페이지를 방문했을 때:

  • 마이페이지(MyPage), 설정(Settings), 대시보드(Dashboard) 등의 코드도 다운로드됨
  • 그러나 사용자는 해당 페이지를 방문하지 않았으므로 불필요한 리소스가 낭비됨

3️⃣ 렌더링 지연 (JavaScript 실행 비용)

React 앱 실행 과정
1. HTML + JavaScript 다운로드
2. JavaScript 실행 (React 라이브러리 로드)
3. 가상 DOM 생성 및 실제 DOM 업데이트

💡 문제:
✔️ 다운로드한 JavaScript가 많을수록 실행 시간이 증가
✔️ 초기 화면이 표시되기까지 지연 발생

4️⃣ 페이지 전환 속도 문제 (SPA 특성)

React는 SPA(Single Page Application)이므로 페이지 이동 시에도 JavaScript가 실행

  • 한 번 로드된 페이지에서 다른 페이지로 이동하면 서버 요청 없이 클라이언트에서 처리됨
  • 하지만 초기 로딩 시 모든 페이지를 한꺼번에 불러오면 성능 저하 발생

💡 이를 해결하기 위해 React Lazy와 Suspense 등장

Lazy

  • 컴포넌트를 바로 불러오지 않고, 실제로 그 컴포넌트가 필요할 때 동적으로 로드함

Suspense

  • 컴포넌트를 불러오는 동안 사용자에게 보여줄 임시 UI(로딩 화면 등)를 설정할 수 있음

📌 Lazy & Suspense 사용법

1️⃣ Lazy 사용 방법

// 기존 방식 (정적 import)
import Main from "./Main.jsx";
// Lazy를 활용한 코드 스플리팅
const Main = lazy(() => import("./Main.jsx"));

2️⃣ Suspense 사용 방법

<Suspense fallback={<h1>로딩중...</h1>}>
  <Main />
</Suspense>

🏗️ Bundling & Code Splitting

1️⃣ Bundling

여러 개의 JavaScript, CSS 파일을 하나의 파일로 합치는 과정

🔹 효과

  • HTTP 요청 수 감소 → 속도 향상
  • 코드 압축 → 파일 크기 축소
  • 사용되지 않는 코드 제거 → 최적화

🔹 Bundling 도구

  • Vite → Rollup 사용
  • CRA(Create React App) → Webpack 사용

🔹 Bundling 실행 명령어

npm run build

2️⃣ Code Splitting (코드 스플리팅)

애플리케이션의 코드를 여러 개의 작은 부분으로 나누는 기술

🔹 효과

  • 초기 로딩 속도 개선
  • 불필요한 코드 로드 방지
  • 데이터 비용 절감 효과

React의 Lazy & Suspense는 대표적인 코드 스플리팅 기법!

📌 컴포넌트 이외의 코드 스플리팅 방법

📁 vite.config.js 설정 예시

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  build: {
    outDir: "docs",
    rollupOptions: {
      output: {
        manualChunks: (id) => {
          if (id.includes("node_modules")) {
            const module = id.split("node_modules/").pop().split("/")[0];
            return `vendor-${module}`;
          }
        },
      },
    },
  },
});

⏳ Debounce & Throttle

🎯 Debounce (디바운스)

  • 이벤트가 연속적으로 발생해도, 마지막 이벤트만 실행
  • 예시: 검색 자동완성 (타이핑 후 일정 시간 동안 입력이 없으면 실행)
useEffect(() => {
  const debounceTimer = setTimeout(() => {
    const newFilteredData = data.filter((el) => el.name.match(reg));
    setFilteredData(newFilteredData);
  }, 1000);
  return () => clearTimeout(debounceTimer);
}, [param]);

🎯 Throttle (스로틀)

  • 일정 시간 간격마다 함수 실행 (중간 중간 끊기지 않는 인터랙션 필요할 때 사용)
  • 예시: 마우스 이동, 스크롤 이벤트
useEffect(() => {
  const newTime = new Date();
  const throttleTimer = setTimeout(() => {
    const newFilteredData = data.filter((el) => el.name.match(reg));
    setFilteredData(newFilteredData);
    time.current = new Date();
  }, 1000 - (newTime - time.current));
  return () => clearTimeout(throttleTimer);
}, [param]);

결론

  1. CSR에서는 초기 로딩 속도 저하, 불필요한 코드 로드, 렌더링 지연 등의 문제가 발생
  2. React Lazy와 Suspense를 활용하면 코드 스플리팅을 통해 성능을 최적화할 수 있음
  3. Bundling과 Code Splitting을 활용하면 더 효율적인 애플리케이션을 만들 수 있음
  4. Debounce & Throttle을 사용하면 불필요한 이벤트 처리를 줄이고 최적화 가능
profile
hello world!

0개의 댓글