본문 바로가기

카테고리 없음

[ React] React의 고급 렌더링 최적화 기법

반응형

1. React.memo를 사용한 컴포넌트 메모이제이션

React.memo는 함수형 컴포넌트의 결과를 메모이징(기억)하여, 불필요한 리렌더링을 방지합니다.

작동 원리:

  • 컴포넌트의 props를 얕은 비교(shallow compare)합니다.
  • props가 변경되지 않았다면, 이전에 렌더링된 결과를 재사용합니다.

사용 예시:

import React from 'react';

const ExpensiveComponent = React.memo(({ data, onItemClick }) => {
  console.log('ExpensiveComponent rendered');
  return (
    <ul>
      {data.map(item => (
        <li key={item.id} onClick={() => onItemClick(item.id)}>
          {item.name}
        </li>
      ))}
    </ul>
  );
});

// 사용
function ParentComponent() {
  const [count, setCount] = useState(0);
  const data = useMemo(() => [
    { id: 1, name: 'Item 1' },
    { id: 2, name: 'Item 2' }
  ], []);
  const handleItemClick = useCallback((id) => {
    console.log('Item clicked:', id);
  }, []);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment: {count}</button>
      <ExpensiveComponent data={data} onItemClick={handleItemClick} />
    </div>
  );
}

이 예시에서 ParentComponentcount 상태가 변경되어도 ExpensiveComponent는 리렌더링되지 않습니다. dataonItemClick이 메모이제이션되어 있기 때문입니다.

주의사항:

  • 모든 컴포넌트를 React.memo로 감싸는 것은 오히려 성능을 저하시킬 수 있습니다.
  • 복잡한 객체나 함수를 props로 전달할 때는 useMemouseCallback과 함께 사용해야 효과적입니다.

2. useMemo 훅을 이용한 계산 결과 메모이제이션

useMemo는 계산 비용이 높은 연산의 결과를 저장하고 재사용합니다.

작동 원리:

  • 첫 번째 인자로 전달된 함수의 결과를 메모이징합니다.
  • 두 번째 인자로 전달된 의존성 배열의 값이 변경될 때만 함수를 재실행합니다.

사용 예시:

import React, { useMemo, useState } from 'react';

function ExpensiveCalculation({ data }) {
  const [filter, setFilter] = useState('');

  const filteredData = useMemo(() => {
    console.log('Calculating filtered data...');
    return data.filter(item => item.name.toLowerCase().includes(filter.toLowerCase()));
  }, [data, filter]); // data 또는 filter가 변경될 때만 재계산

  return (
    <div>
      <input
        type="text"
        value={filter}
        onChange={(e) => setFilter(e.target.value)}
        placeholder="Filter items"
      />
      <ul>
        {filteredData.map(item => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}

이 예시에서 filteredDatadata 또는 filter가 변경될 때만 재계산됩니다. 다른 상태 변경으로 인한 리렌더링에서는 이전에 계산된 결과를 재사용합니다.

주의사항:

  • 모든 계산에 useMemo를 사용하는 것은 불필요합니다. 계산 비용이 높거나 자주 리렌더링되는 컴포넌트에서만 사용하세요.
  • 의존성 배열을 올바르게 설정하지 않으면 오래된 데이터를 사용할 risk가 있습니다.

3. useCallback을 이용한 함수 메모이제이션

useCallback은 콜백 함수를 메모이징하여, 불필요한 리렌더링을 방지합니다.

작동 원리:

  • 첫 번째 인자로 전달된 콜백 함수를 메모이징합니다.
  • 두 번째 인자로 전달된 의존성 배열의 값이 변경될 때만 새로운 함수를 생성합니다.

사용 예시:

import React, { useState, useCallback } from 'react';

function ParentComponent() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    console.log('Button clicked');
    setCount(prevCount => prevCount + 1);
  }, []); // 의존성 배열이 비어있으므로 컴포넌트가 마운트될 때만 함수가 생성됨

  return (
    <div>
      <ChildComponent onClick={handleClick} />
      <p>Count: {count}</p>
    </div>
  );
}

const ChildComponent = React.memo(({ onClick }) => {
  console.log('ChildComponent rendered');
  return <button onClick={onClick}>Click me</button>;
});

이 예시에서 handleClick 함수는 컴포넌트가 처음 렌더링될 때만 생성되며, 이후 리렌더링에서는 동일한 함수 참조를 유지합니다. 이로 인해 ChildComponent는 불필요하게 리렌더링되지 않습니다.

주의사항:

  • useCallback은 함수 생성 자체를 최적화하는 것이 아니라, 함수의 참조가 변경되는 것을 방지합니다.
  • 의존성 배열을 신중히 설정해야 합니다. 빈 배열을 사용하면 함수가 오래된 클로저를 참조할 수 있습니다.

이러한 기법들을 적절히 조합하여 사용하면 React 애플리케이션의 성능을 크게 향상시킬 수 있습니다. 하지만 항상 성능 측정을 통해 실제로 최적화가 필요한 부분에 적용하는 것이 중요합니다.

반응형