[React] 최적화 전략 2탄 (lazy, Suspense와 코드 스플리팅, Debounce, Throttle)


01

lazy와 Suspense

1. lazy

lazy컴포넌트를 필요할 때만 로딩하는 기능으로, 초기 로딩을 최적화할 수 있습니다.

이는 대개 큰 애플리케이션에서 유용하게 쓰입니다.

 

✅ 사용법

import React, { Suspense, lazy } from 'react';

const MyComponent = lazy(() => import('./MyComponent'));

function App() {
  return (
    <div>
      <h1>Hello, world!</h1>
      <Suspense fallback={<div>Loading...</div>}>
        <MyComponent />
      </Suspense>
    </div>
  );
}

export default App;
  • React.lazy는 동적으로 컴포넌트를 불러오는 함수입니다.
  • import() 구문을 사용하여 컴포넌트를 동적으로 불러옵니다.

2. Suspense

Suspense lazy로 불러온 컴포넌트를 로딩하는 동안 사용자에게 보여줄 fallback UI (로딩)를 설정하는 기능입니다.

 

✅ 사용법

import React, { Suspense, lazy } from 'react';

const MyComponent = lazy(() => import('./MyComponent'));

function App() {
  return (
    <div>
      <h1>Hello, world!</h1>
      <Suspense fallback={<div>Loading...</div>}>
        <MyComponent />
      </Suspense>
    </div>
  );
}

export default App;
  • Suspense는 반드시 lazy와 함께 사용되어야 하며, 로딩 중일 때 보여줄 내용을 fallback(로딩)으로 설정합니다.

3. 부작용 및 단점

부작용

: 모든 컴포넌트들이 로딩될 때까지 기다려야 하므로, 사용자가 요청하지 않은 페이지까지도 불필요하게 로딩되며 네트워크 비용이 발생할 수 있습니다.

 

단점

: 사용자가 처음 페이지에 들어갔을 때 로딩 화면을 봐야 할 수 있습니다. 특히 작은 페이지들이 많은 앱에서 불편할 수 있습니다.

 

✅ 사용 시기

: 대규모 프로젝트에서 사용하면 유리하며, 로딩이 필요 없는 페이지를 선택적으로 적용할 수 있습니다.


02

번들링과 코드 스플리팅

1. 번들링 (Bundling)

번들링은 여러 개의 파일을 하나로 합쳐 HTTP 요청 수를 줄이고, 코드를 압축해 파일 크기를 줄이는 과정입니다.

 

✅ 사용법

# Webpack 설정 예시
module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
  },
  // 추가 설정들...
};
  • Webpack이나 Vite 등의 모듈 번들러를 사용하여 번들링을 설정할 수 있습니다.

2. 코드 스플리팅 (Code Splitting)

코드 스플리팅은 애플리케이션의 코드 전체를 하나의 덩어리로 로딩하는 대신,

여러 개의 작은 부분으로 나누어 불러오는 기술입니다.

 

✅ 사용법

//vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vite.dev/config/
export default defineConfig({
  plugins: [react()],
  rollupOptions:{
    output:{
      manualChunks: (id) =>{
        if(id.indexOf("node_modules") !== -1){
          const module = id.split("node_modules/").pop().split("/")[0];
          return `vendor-${module}`;
        }
      }
    }
  }
})
  • React.lazy와 Suspense 외에도, Webpack이나 Vite에서 설정을 통해 코드 스플리팅을 할 수 있습니다.

03

Debounce와 Throttle

Debounce와 Throttle은 연속적으로 발생하는 이벤트나 함수를 처리할 때 성능 최적화를 위해 사용됩니다.

1. Debounce 

Debounce는 연속적으로 발생하는 이벤트를 일정 시간이 지난 후 마지막 이벤트만 처리하는 방식입니다.

예를 들어, 검색어 자동완성 기능에 유용합니다.

 

✅ 사용법

import { useState } from 'react';

function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);

  return debouncedValue;
}

 

2. Throttle

Throttle은 이벤트가 연속적으로 발생할 때 일정 시간 간격으로만 처리하도록 하는 방식입니다.

예를 들어, 스크롤 이벤트에서 유용합니다.

 

✅ 사용법

function useThrottle(value, limit) {
  const [throttledValue, setThrottledValue] = useState(value);

  useEffect(() => {
    const handler = setInterval(() => {
      setThrottledValue(value);
    }, limit);

    return () => {
      clearInterval(handler);
    };
  }, [value, limit]);

  return throttledValue;
}

 

3. Throttle 시간 간격 설정은 왜 중요한가?

Throttle은 이벤트(예: 스크롤, 마우스 이동 등)가 너무 자주 발생하는 걸 방지하는 방법인데,

그 "자주 발생하는" 것을 얼마나 자주 처리할지를 결정하는 게 바로 간격입니다.

  • 간격을 짧게 설정하면, 이벤트를 자주 처리하게 되지만 성능에 부담이 될 수 있어요.
  • 간격을 길게 설정하면 성능은 좋아지지만, 사용자에게 반응이 느리다고 느껴질 수 있어요.

 

어떻게 간격을 정해야 할까?

  • 일반적인 추천 간격: 보통 100ms ~ 200ms 사이의 간격이 적당합니다.
    이 정도면 사용자는 큰 불편함 없이 반응을 보고, 성능에도 부담을 주지 않습니다.
  • 어떤 이벤트냐에 따라 다르다:
    📌 스크롤이나 마우스 이동과 같은 자주 발생하는 이벤트는 100ms ~ 200ms 간격이 적당합니다.
    📌 창 크기 변경처럼 드물게 발생하는 이벤트는 200ms ~ 500ms 정도의 간격이 적당할 수 있습니다.
          이때는 반응 속도보다는 성능이 더 중요하니까요.

 

간격 설정 예시 코드

import { useState, useEffect } from 'react';

function useThrottle(value, delay) {
  const [throttledValue, setThrottledValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setThrottledValue(value);
    }, delay);  // delay가 바로 Throttle 간격

    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);

  return throttledValue;
}

// 사용 예시
function ExampleComponent() {
  const [scrollPosition, setScrollPosition] = useState(0);
  const throttledPosition = useThrottle(scrollPosition, 200); // 200ms 간격으로 처리

  useEffect(() => {
    const handleScroll = () => {
      setScrollPosition(window.scrollY); // 스크롤 위치 저장
    };

    window.addEventListener('scroll', handleScroll);
    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
  }, []);

  return (
    <div>
      <p>Throttled Scroll Position: {throttledPosition}</p>
    </div>
  );
}

이 예제에서 200ms라는 간격을 설정했습니다.

100ms ~ 200ms 사이로 설정하는 게 일반적으로 가장 적당하고, 성능도 좋고 반응도 빠릅니다.


04

마무리

위에서 설명한 최적화 기법들은 애플리케이션의 성능을 크게 향상시킬 수 있습니다.

각 기법은 특정 상황에서 유용하게 사용될 수 있으므로, 프로젝트의 요구사항에 맞게 적절히 적용하는 것이 중요합니다.

 

최적화 기법 주요 효과 적용 시기
lazy와 Suspense 대규모 애플리케이션에서 컴포넌트 로딩 지연, 초기 로딩 시간 단축, 네트워크 리소스 효율화 대규모 애플리케이션에서 유리
Debounce와 Throttle 이벤트 연속 발생 시 불필요한 계산 방지, 성능 최적화, 반응 속도 개선 사용자 인터랙션이 빈번한 경우 유용
번들링과 코드 스플리팅 코드 크기 축소, 필요한 코드만 로드, 초기 로딩 속도 개선, 네트워크 트래픽 줄이기 초기 로딩 시간 단축 및 네트워크 효율화가 중요한 경우

 

이렇게 각 기법의 주요 효과와 적용 시기를 비교할 수 있습니다.

 

 

👉  관련 내용 블로그 다른 글 보러가기

 

[React] 최적화 함수 1탄 (useMemo, React.memo, useCallback)

01최적화(Optimization)React는 상태(state)나 props가 변경될 때마다 컴포넌트를 다시 렌더링 합니다.이 과정이 반복되면 성능이 저하될 수 있기 때문에 최적화(Optimization)가 필요합니다. React에서 사

dev-watnu.tistory.com