React Compiler(구 React Forget)가 React 19 배포와 함께 공식화됐다. 한마디로 말하면, 컴파일러가 자동으로 컴포넌트와 커스텀 훅을 메모이제이션해준다. 개발자가 직접 useMemo, useCallback을 쓰지 않아도 된다는 뜻이다.
React를 오래 써온 사람이라면 이 패턴에 익숙할 거다.
const filteredList = useMemo(() => {
return items.filter((item) => item.active);
}, [items]);
const handleClick = useCallback(() => {
doSomething(id);
}, [id]);
의존성 배열 관리가 늘 실수 포인트였다. 배열에 넣어야 할 게 빠지면 오래된 값을 참조하는 버그가 생기고, 반대로 너무 많이 넣으면 리렌더링이 오히려 늘어난다. ESLint의 exhaustive-deps 규칙이 도움은 되지만 완벽하지 않다.
React Compiler는 이 문제를 빌드 타임에 처리한다. 컴포넌트 코드를 분석해서 어떤 값이 언제 바뀌는지 파악하고, 자동으로 최적화된 코드를 생성한다.
실제로 켜보니
Next.js 15에서는 next.config.ts에 한 줄 추가하면 된다.
// next.config.ts
const nextConfig = {
experimental: {
reactCompiler: true,
},
};
Babel 플러그인으로도 쓸 수 있다.
npm install babel-plugin-react-compiler
// babel.config.json
{
"plugins": ["babel-plugin-react-compiler"]
}
설정하고 나면 기존 코드 그대로 두고 빌드만 돌리면 된다. 눈에 보이는 변화는 없는데, React DevTools에서 컴포넌트를 보면 메모이제이션 정보가 표시된다.
모든 코드에 적용되는 건 아니다
Compiler가 자동 최적화하려면 React 규칙을 지키는 코드여야 한다. 가장 흔한 경우는 props나 state를 직접 변경(mutation)하는 코드다.
// 이런 코드는 컴파일러가 최적화 못 한다
function Component({ items }) {
items.push(newItem); // 직접 mutation
return <List items={items} />;
}
컴파일러는 규칙을 어기는 컴포넌트는 건너뛰고 나머지만 최적화한다. 강제로 적용하지 않으니까 점진적으로 마이그레이션할 수 있다.
use no memo 디렉티브로 특정 컴포넌트를 명시적으로 제외할 수도 있다.
function ManualMemoComponent() {
"use no memo";
// 이 컴포넌트는 컴파일러가 건드리지 않는다
}
기존 코드베이스 점검
React 팀에서 제공하는 react-compiler-healthcheck 도구로 기존 코드베이스가 얼마나 호환되는지 먼저 확인할 수 있다.
npx react-compiler-healthcheck@latest
결과를 보면 컴파일러가 최적화할 수 있는 컴포넌트 비율과 문제가 되는 패턴이 나온다. 규칙 위반이 많은 코드베이스일수록 효과가 제한적이다.
useMemo를 당장 다 지워야 하나
아직은 아니다. 기존 useMemo/useCallback은 컴파일러가 있어도 동작한다. 중복 적용되는 거라 성능 손해는 없다. 새로 작성하는 코드부터 컴파일러에 맡기고, 기존 코드는 필요할 때 조금씩 정리하는 게 현실적인 방향이다.
2026년 현재 React 19 프로젝트에서 컴파일러는 기본으로 켜두는 게 맞다. 수동 메모이제이션을 전부 걷어내기는 아직 이르지만, 새 컴포넌트 작성할 때 useMemo/useCallback을 고민하는 시간이 많이 줄었다. 그것만으로도 충분히 쓸 이유가 있다.