React 19 Compiler와 기존 최적화 패턴 비교

배경

회사 프로젝트를 React 19로 마이그레이션하면서 React Compiler를 도입했다. 기존에 useMemo, useCallback으로 도배되어 있던 코드를 정리할 수 있다는 기대가 있었지만, 실제로는 생각보다 복잡한 부분이 있었다.

React Compiler 적용 전후

기존 코드는 이런 식이었다.

const ExpensiveList = ({ items, filter }) => {
  const filteredItems = useMemo(
    () => items.filter(item => item.category === filter),
    [items, filter]
  );

  const handleClick = useCallback((id) => {
    console.log('clicked', id);
  }, []);

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

Compiler 적용 후에는 이렇게 단순화할 수 있었다.

const ExpensiveList = ({ items, filter }) => {
  const filteredItems = items.filter(item => item.category === filter);
  
  const handleClick = (id) => {
    console.log('clicked', id);
  };

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

여전히 주의가 필요한 케이스

  1. 외부 상태 변경: Zustand나 Jotai 같은 상태관리 라이브러리 사용 시 여전히 명시적 최적화가 필요했다.
  2. 대용량 데이터 처리: 만 건 이상의 리스트 필터링은 여전히 useMemo가 더 예측 가능했다.
  3. 서드파티 컴포넌트: React.memo로 감싸진 외부 라이브러리 컴포넌트에 props 전달 시 useCallback이 필요한 경우가 있었다.

성능 측정 결과

Chrome DevTools Profiler로 측정한 결과, 대부분의 케이스에서 Compiler 적용 전후 성능 차이는 미미했다. 오히려 코드 가독성 향상이 더 큰 이득이었다.

결론

React Compiler는 boilerplate를 크게 줄여주지만, 성능 크리티컬한 부분에서는 여전히 수동 최적화가 필요하다. 마이그레이션 시 점진적으로 접근하고, 프로파일링으로 검증하는 과정이 중요했다.