TypeScript

[React 최적화] 메모이제이션 완벽 가이드 - useMemo, useCallback, React.memo 실전 활용법

코샵 2024. 11. 17. 10:43
반응형

소개

React 애플리케이션의 성능을 최적화하는 방법 중 가장 중요한 것이 메모이제이션(Memoization)입니다. 메모이제이션은 이전에 계산한 값을 재사용하여 불필요한 렌더링과 계산을 방지하는 기술입니다. 이번 글에서는 React의 메모이제이션 도구들을 자세히 살펴보겠습니다.

React.memo

React.memo는 컴포넌트 자체를 메모이제이션하는 고차 컴포넌트(HOC)입니다.

// 기본 사용법
const MemoizedComponent = React.memo(function MyComponent(props) {
  return (
    <div>
      <h2>{props.name}</h2>
      <p>{props.description}</p>
    </div>
  );
});

// 커스텀 비교 함수 사용
const MemoizedWithCustomCompare = React.memo(MyComponent, (prevProps, nextProps) => {
  return prevProps.name === nextProps.name;
  // description이 변경되어도 리렌더링하지 않음
});

useMemo

useMemo는 계산 비용이 많이 드는 값을 메모이제이션합니다.

// 고비용 계산의 결과를 메모이제이션
const memoizedValue = useMemo(() => {
  return expensiveCalculation(count * 2);
}, [count]); // count가 변경될 때만 재계산

// 객체 메모이제이션
const memoizedObject = useMemo(() => ({
  id: props.id,
  name: props.name
}), [props.id, props.name]);

useCallback

useCallback은 함수를 메모이제이션하여 불필요한 재생성을 방지합니다.

// 이벤트 핸들러 메모이제이션
const handleClick = useCallback(() => {
  console.log('Button clicked:', count);
}, [count]); // count가 변경될 때만 함수 재생성

// 자식 컴포넌트에 전달하는 콜백 메모이제이션
const handleSearch = useCallback((searchTerm: string) => {
  setResults(items.filter(item => item.includes(searchTerm)));
}, [items]);

실제 사용 예제

실제 애플리케이션에서 메모이제이션을 활용하는 예제를 살펴보겠습니다.

function ExpensiveList({ items, onItemClick }) {
  // 아이템 필터링 결과 메모이제이션
  const filteredItems = useMemo(() => {
    return items.filter(item => item.active);
  }, [items]);

  // 클릭 핸들러 메모이제이션
  const handleClick = useCallback((id: string) => {
    onItemClick(id);
  }, [onItemClick]);

  return (
    <ul>
      {filteredItems.map(item => (
        <ListItem
          key={item.id}
          item={item}
          onClick={() => handleClick(item.id)}
        />
      ))}
    </ul>
  );
}

// 리스트 아이템 컴포넌트 메모이제이션
const ListItem = React.memo(({ item, onClick }) => (
  <li onClick={onClick}>
    {item.name}
  </li>
));

메모이제이션 사용 시 주의사항

  1. 과도한 메모이제이션 피하기
    // 불필요한 메모이제이션 (단순 값)
    const simpleValue = useMemo(() => count + 1, [count]);
    // 대신 이렇게 사용
    const simpleValue = count + 1;
  2. 의존성 배열 관리
// 잘못된 의존성 배열
const memoizedValue = useMemo(() => {
  return data.filter(item => item.id === selectedId);
}, []); // selectedId가 변경되어도 업데이트되지 않음

// 올바른 의존성 배열
const memoizedValue = useMemo(() => {
  return data.filter(item => item.id === selectedId);
}, [data, selectedId]);

 

성능 측정

메모이제이션 적용 전후의 성능을 측정하는 방법입니다.

// React DevTools Profiler 사용
function ParentComponent() {
  const [renderCount, setRenderCount] = useState(0);

  console.time('render');
  useEffect(() => {
    console.timeEnd('render');
  });

  return (
    <MemoizedComponent 
      data={complexData}
      onUpdate={() => setRenderCount(prev => prev + 1)}
    />
  );
}

결론

메모이제이션은 React 애플리케이션의 성능을 최적화하는 강력한 도구입니다. React.memo, useMemo, useCallback을 적절히 활용하면 불필요한 렌더링을 방지하고 애플리케이션의 반응성을 향상시킬 수 있습니다. 하지만 모든 것을 메모이제이션하는 것은 오히려 성능을 저하시킬 수 있으므로, 실제 성능 측정을 통해 필요한 곳에만 적용하는 것이 중요합니다.