본문 바로가기

Engineering/Frontend

React 19 도입 1년, 이제 useMemo와 useCallback은 놓아줄 때가 됐다

React 19가 정식 릴리즈되고 시간이 꽤 흘렀다. 초창기만 해도 호환성 문제로 도입을 꺼리는 분위기가 있었으나, 이제 Next.js 최신 버전과 맞물려 React Compiler가 표준으로 자리 잡는 모양새다.

지난 몇 년간 리액트 개발자들을 괴롭혔던 건 '불필요한 리렌더링'과의 싸움이었다. 우리는 컴포넌트 성능을 지키기 위해 의존성 배열(dependency array)을 관리하며 useMemo, useCallback을 난사했다. 코드는 지저분해졌고, 의존성 배열 하나만 실수해도 버그가 터졌다.

이제 그 소모적인 논쟁을 끝낼 시점이다. 2026년 현재, 프론트엔드 최적화의 주권은 개발자가 아닌 '컴파일러'에게 넘어갔다.

수동 메모이제이션의 종말

기존 리액트의 멘탈 모델은 "상태가 변하면 UI를 다시 그린다"였다. 이 과정에서 하위 컴포넌트까지 불필요하게 렌더링되는 것을 막기 위해 우리는 수동으로 캐싱을 했다.

Legacy Code (React 18 이하)

JavaScript
 
const filteredList = useMemo(() => {
  return items.filter(item => item.active);
}, [items]);

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

return <ListComponent list={filteredList} onClick={handleClick} />;

이 코드는 두 가지 문제가 있다.

  1. items 배열이 조금만 복잡해져도 의존성 추적이 어렵다.
  2. 비즈니스 로직보다 최적화 로직이 더 많은 비중을 차지한다.

Modern Code (React 19 + Compiler)

JavaScript
 
// 메모이제이션 훅이 필요 없다.
const filteredList = items.filter(item => item.active);

const handleClick = () => {
  console.log('clicked');
};

return <ListComponent list={filteredList} onClick={handleClick} />;

React Compiler는 빌드 타임에 코드를 분석한다. items가 변하지 않았다면 filteredList를 다시 계산하지 않도록, handleClick 함수가 재생성되지 않도록 컴파일러가 자동으로 코드를 변환한다. 즉, 개발자는 로직에만 집중하면 되고, 성능 최적화는 도구의 몫이 되었다.

어떻게 작동하는가

React Compiler는 'Fine-grained Memoization(세밀한 메모이제이션)'을 수행한다. 컴포넌트 전체를 메모이징하는 React.memo와 달리, 컴포넌트 내부의 변수 선언, 함수 정의, JSX 반환값 각각을 독립적으로 캐싱한다.

빌드된 결과물을 뜯어보면, 값의 변경 여부를 판단하는 if 분기문들이 자동으로 삽입되어 있다. 개발자가 런타임 비용을 걱정하며 훅을 걸던 행위를 빌드 타임 연산으로 대체한 것이다.

주의할 점과 한계

물론 은탄환은 아니다. 컴파일러가 의도를 파악하기 힘든 난해한 코드나, 리액트의 규칙(Rules of React)을 위반한 코드에서는 최적화가 건너뛰어질 수 있다.

  1. 엄격한 규칙 준수: 훅을 조건문 안에서 호출하거나, 전역 변수를 컴포넌트 내부에서 변조하는 식의 안티 패턴은 이제 더 엄격하게 걸러진다.
  2. 복잡한 설정: 레거시 프로젝트에 React Compiler를 도입하려면 Babel/SWC 설정 단계에서 꽤 고생할 수 있다. (Next.js를 쓴다면 설정 플래그 하나로 해결되긴 한다.)

결론

지금 시점에서 useMemo와 useCallback을 공부하고 있다면 우선순위를 재조정해야 한다. 이제 프론트엔드 성능 최적화는 '어떻게 렌더링을 막을까'가 아니라, '어떻게 컴파일러가 이해하기 쉬운 코드를 짤까'로 바뀌었다.

기계가 할 수 있는 일은 기계에게 맡기자. 우리는 데이터의 흐름과 사용자 경험에 더 집중해야 한다.