React Compiler는 빌드 타임에 컴포넌트와 훅을 분석해 메모이제이션(Memoization) 코드를 자동으로 넣어 주는 컴파일러다. 개발자가 손으로 붙이던 useMemo·useCallback·memo를 대신한다. 2025년 10월 1.0이 나왔다.
동작 원리
컴포넌트마다 캐시 슬롯 배열을 만들고, 입력이 Object.is로 같으면 이전 결과를 꺼내 쓴다(개념 코드).
function Cart({ items }) {
const $ = _c(2) // 슬롯 2개
let total
if ($[0] !== items) { total = items.reduce(sum, 0); $[0] = items; $[1] = total }
else total = $[1]
return <Summary total={total} />
}- 의존성을 자동으로 추론하니 deps 배열을 빠뜨릴 일이 없다
useMemo처럼 덩어리 단위가 아니라 식 단위로 잘게 메모한다- JSX(JavaScript XML)도 메모한다. props가 같으면 같은 엘리먼트 객체를 돌려줘서 React가 자식 렌더를 건너뛴다. 그래서
memo()와 비슷한 효과를 낸다 - 주로 Babel 플러그인으로 동작하고, 컴포넌트(대문자, JSX 반환)와 훅(
use…)만 대상이다
전제와 bailout
컴파일러는 코드가 React의 규칙과 렌더 순수성를 지킨다고 가정한다. 렌더 중 ref.current 읽기, props·state 변경, 호환되지 않는 라이브러리(값을 변경 가능한 객체로 주는 폼 라이브러리 등)를 감지하면 그 컴포넌트를 건드리지 않고 원본대로 둔다(bailout). 에러가 아니라 보수적인 보호 장치라 동작은 그대로이고, 자동 메모 혜택만 못 받는다. eslint-plugin-react-hooks가 이런 곳을 알려 준다.
실무 포인트
- 새 코드에는 성능용 수동 메모를 쓰지 않는다. bailout된 컴포넌트, Effect 의존성으로 쓰이는 값 정도만 수동으로 남긴다
- 이미 가벼운 렌더는 더 빨라지지 않는다. 무거운 리렌더를 줄이는 도구다
- 컴포넌트마다 캐시 코드가 들어가 번들이 커진다. 효과와 비용을 재 보고 판단한다(번들 크기 줄이기)
- 탈출구: 함수 첫 줄에
'use no memo'. 점진 도입은compilationMode: 'annotation'+'use memo'
컴파일러 기반 린트 규칙
eslint-plugin-react-hooks 7부터 기존 두 규칙(rules-of-hooks, exhaustive-deps)에 컴파일러 진단을 쓰는 규칙들이 더해졌고, recommended 프리셋에 들어 있다(2026-10 기준 7.1). 컴파일러를 켜지 않은 앱에서도 쓸 수 있어서 도입 전 준비 단계로 좋다. 자주 걸리는 것들은 다음과 같다.
set-state-in-effect: Effect 본문에서 곧바로setState. 렌더를 한 번 더 돌리니 렌더 중 계산으로 바꾼다(Effect가 필요 없는 경우). ref로 잰 DOM 크기를 state에 넣는 경우는 예외다immutability: props·state 등 추적 중인 값을 직접 변경(items.push(x); setItems(items))refs: 렌더 중ref.current읽기·쓰기purity: 렌더 중Date.now()·Math.random()같은 비순수 호출preserve-manual-memoization: 기존useMemo·useCallback의 deps가 빠져 컴파일러가 그 메모를 보존할 수 없는 경우incompatible-library: 컴파일러가 건너뛰는 API 사용(내부 가변성과 메모이제이션). 기본은 경고- 그 밖에
set-state-in-render,static-components,globals,use-memo,error-boundaries등이 있고, 실험 규칙까지 받으려면recommended-latest를 쓴다
기존 코드베이스에 처음 켜면 위반이 많이 나온다. 한꺼번에 고칠 필요는 없고, 규칙별로 끄거나 경고로 낮춘 뒤 하나씩 켜 가면 된다. 위반한 컴포넌트는 컴파일러가 건너뛸 뿐이라 점진적으로 최적화 범위가 넓어진다.
도입 판단 축
- 런타임 이득: 리렌더가 이미 가볍거나 서버 렌더·정적화 비중이 크면 체감 이득이 거의 없을 수 있다. 프로파일러로 실제 느린 인터랙션이 있는지 먼저 본다
- 번들 비용: 메모 코드가 늘어 번들이 커진다. 모바일 비중이 높으면 압축 후 크기를 직접 재 본다
- 코드 단순화: 수동
useMemo·useCallback과 deps 실수를 줄이고, 새 코드에서 "메모할까" 고민이 사라진다. 성능보다 이쪽이 주된 이득일 때가 많다 - 위험과 전환 비용: 비호환 라이브러리, 렌더 중 ref 접근 등 건너뛰거나 동작이 달라질 수 있는 곳을 핵심 경로(결제 등)부터 확인해야 한다. 수동 메모를 지운 뒤 되돌리면 다시 써야 한다
- 중간 선택지:
compilationMode: 'annotation'으로 무거운 컴포넌트에만'use memo'를 붙여 비용을 국소화한다
수동 도구의 원리는 memo·useMemo·useCallback, 순수성 규칙은 부수 효과와 Effect.
출처: React - eslint-plugin-react-hooks · React - set-state-in-effect · React - preserve-manual-memoization