[Frontend] React Compiler 1.0: 자동 memoization 의 새 표준
정의
React Compiler 는 React 컴포넌트와 hook 의 불필요한 재계산 을 빌드 타임에 자동으로 잡아주는 optimizing compiler. 손으로 useMemo / useCallback / React.memo 를 박는 대신, 컴파일러가 값과 함수의 변경 여부 를 분석해 자동으로 캐싱한다.
핵심 약속 4가지:
- 자동 memoization: 손으로 쓰지 않아도 정확하게 박힌다.
- 조건부 memoization:
if/return이후의 값 까지 memoize 가능 (수동 hook 으로는 불가능했던 영역). - Rules of React 강제: ESLint 와 함께 위반을 조용히 잡아낸다.
- 점진 도입:
'use memo'디렉티브 하나로 컴포넌트 단위 부터 켤 수 있다.
v1.0 은 2025년 10월 7일 출시. 거의 10년에 걸친 컴파일러 실험 의 첫 안정 결과물이다.
왜 컴파일러인가?
React 의 멘탈 모델은 “매 렌더마다 새로 계산”. 깔끔하지만, 비싼 계산 이나 자식의 reference equality 가 필요한 곳에서는 다음 같은 코드가 불가피했다:
const filtered = useMemo(
() => items.filter((it) => it.tag === activeTag),
[items, activeTag],
);
const handleClick = useCallback(
(id: string) => onSelect(id),
[onSelect],
);
const Child = memo(function Child({ data }) {
// ...
});
문제는 3가지:
- 디펜던시 배열을 사람이 관리: 빠뜨리면 stale, 너무 많이 넣으면 무의미.
- memoize 한 함수도 변하면 자식이 다시 렌더: 한 군데서 끊기면 사슬 전체가 무너진다.
useMemo자체가 비용: dependency 비교 + 캐시 슬롯 관리. 오히려 무겁다.
자동화는 비용 자체를 줄이는 것이 아니라 사람의 일관성을 보장 하기 위한 것. 컴파일러는 조건부 early-return 이후 의 값까지 memoize 한다.
컴파일러는 어떻게 동작하는가
Babel 이 만든 Abstract Syntax Tree (AST) 를 받아서, 자체 IR (HIR, High-Level Intermediate Representation) 로 lowering. HIR 은 Control Flow Graph (CFG) 기반이라 정확한 data-flow analysis 와 mutability inference 가 가능하다.
Source (JSX/TS)
↓ Babel parse
AST
↓ React Compiler lowering
HIR (CFG-based)
↓ multi-pass
├ data-flow analysis
├ mutability inference
├ scope formation (memoization 단위)
└ rules-of-react validation
↓ codegen
Optimized output (AST)
↓ Babel print
JS
이 분리 덕에 다음이 가능:
- 조건부 분기 이후 memoize:
useMemo의 최상위 위치 제약이 사라진다. - 세분화 (granular) scope: 거대한
useMemo하나가 아니라 필요한 값마다 따로. - lint 가 같은 분석을 재활용:
eslint-plugin-react-hooks의 새 규칙들이 compiler 의 HIR 분석 결과를 그대로 쓴다.
공식 예시 (조건부 return 후의 theme 값까지 memoize):
import { use } from 'react';
export default function ThemeProvider(props) {
if (!props.children) {
return null;
}
// 컴파일러는 conditional return 이후의 값도 memoize 한다
const theme = mergeTheme(props.theme, use(ThemeContext));
return (
<ThemeContext value={theme}>
{props.children}
</ThemeContext>
);
}
수동 useMemo 로는 불가능한 위치. 컴파일러만의 강점.
컴파일 모드와 디렉티브
| 모드 | 동작 | 용도 |
|---|---|---|
infer (기본) | 컴포넌트 / hook 으로 추론 되면 자동 컴파일 | 새 코드, 전체 도입 |
annotation | 'use memo' 가 명시된 함수만 컴파일 | 점진 도입 초기 |
all | 모든 함수를 컴파일 시도 | 보통 권장하지 않음 |
syntax | parse 만 하고 실제 변환은 하지 않음 | debugging |
함수 단위 디렉티브 (모드와 무관하게 우선):
function StableComponent() {
'use memo'; // 이 컴포넌트는 *반드시* 컴파일
// ...
}
function ProblematicComponent() {
'use no memo'; // 이 컴포넌트는 *반드시* 컴파일에서 제외
// 보통 Rules of React 위반을 발견한 임시 escape
}
권장 도입 흐름:
compilationMode: 'annotation'으로 켜고, 잘 검증된 컴포넌트부터'use memo'추가.- ESLint 새 규칙으로 위반 코드 식별 + 수정.
compilationMode: 'infer'로 전환.- 문제 발견된 컴포넌트만
'use no memo'로 임시 격리 + 원인 수정 후 제거.
설정 (babel.config.js)
// babel.config.js
module.exports = {
plugins: [
[
'babel-plugin-react-compiler',
{
compilationMode: 'annotation',
target: '18', // '17' | '18' | '19' (기본 '19')
panicThreshold: 'none', // 'all_errors' | 'critical_errors' | 'none'
logger: {
logEvent(filename, event) {
if (event.kind === 'CompileSuccess') {
console.log('Compiled:', filename);
}
},
},
gating: {
source: 'my-feature-flags', // 모듈 경로
importSpecifierName: 'isCompilerEnabled', // 토글 함수
},
},
],
],
};
주요 옵션:
target: '17' | '18': React 19 미만이면react-compiler-runtime을 의존성에 추가하고 target 을 명시. v1.0 의 놀라운 약속.panicThreshold: 일반적으로'none'권장. 컴파일에 실패한 함수는 그대로 두고 넘어감.gating: A/B 테스트나 feature flag 로 환경에 따라 켜고 끄기. Meta 가 내부적으로 쓰던 방식.
빌드 도구 매트릭스
| 빌드 도구 | 지원 상태 (2026-06) |
|---|---|
| Babel | 1차 (babel-plugin-react-compiler) |
| Vite | @vitejs/plugin-react 의 Babel 옵션 경유, 또는 vite-plugin-react-compiler |
| Next.js | next.config.js 의 reactCompiler: true. 15.3.1+ 에서 swc plugin 으로 빌드 속도 크게 개선 |
| swc | experimental. @kdy1dev 가 작업. Next.js 가 먼저 활용 |
| oxc | 작업 중 (oxc-project/oxc#10048) |
| Rsbuild | 1차 지원 |
| Expo | SDK 54 부터 기본 enable |
새 프로젝트는
create-vite,create-next-app,create-expo-app모두 컴파일러 enable 옵션 을 제공한다. 새 코드면 거의 무조건 켜는 것이 권장.
ESLint 통합
v1.0 부터 별도 eslint-plugin-react-compiler 는 폐지. eslint-plugin-react-hooks 의 recommended preset 안으로 통합되었다.
// eslint.config.js (Flat Config)
import reactHooks from 'eslint-plugin-react-hooks';
import { defineConfig } from 'eslint/config';
export default defineConfig([
reactHooks.configs.flat.recommended,
]);
새 규칙 일부:
set-state-in-render: 렌더 중setState호출로 인한 무한 루프 패턴.set-state-in-effect: Effect 안에서 불필요한 state 동기화 를 잡음.refs: 렌더 중 ref 접근 같은 unsafe 사용을 잡음.
ESLint 만 켜는 것도 유의미: 컴파일러를 실제로 도입하지 않아도, Rules of React 위반을 컴파일러의 IR 수준 분석으로 잡아낼 수 있다.
useMemo / useCallback / React.memo 는 이제 필요 없나?
대부분의 경우 그렇다. 컴파일러는 손으로 쓴 것보다 더 정밀하게 memoize 한다.
다만 다음 세 경우는 escape hatch 로 그대로 둘 가치가 있다:
useEffect의 dependency 가 정확히 memoize 된 값이어야 할 때: 컴파일러가 언제 reference 가 바뀌는지 의 의도를 표현할 곳이 코드에 명시되어 있어야 안전.- 외부 라이브러리에 stable reference 를 넘겨야 할 때: 라이브러리의 hook 내부가 dep array 를 보는 경우.
- 이미 깔린 기존
useMemo를 제거: 컴파일 결과가 미세하게 달라질 수 있어, 코드를 건드리지 않고 두는 편이 안전. 새 코드는 안 써도 OK.
프로덕션 결과
Meta 가 자체 앱들에 수년간 적용한 결과 (공식 글):
- Meta Quest Store: 초기 로드와 페이지 이동 최대 12% 개선. 일부 인터랙션은 2.5배 이상 빨라짐.
- 메모리 사용량은 중립: memoize 추가 비용이 그만한 가치 가 있도록 균형.
오픈소스 사례:
- Sanity Studio: 거대한 content editor 의 복잡한 트리에서 도입 회고 (discussion #33).
- Wakelet: 측정된 수치 와 함께 도입 회고 (discussion #52).
v1.0 까지의 타임라인
| 시점 | 이벤트 |
|---|---|
| 2017 | Meta 의 Prepack 실험. 종료되었지만 Hooks 설계의 토대 가 됨 |
| 2021 | Xuan Huang 의 첫 prototype 데모 |
| 2024-02 | React Labs 글, 본격적인 Compiler 작업 상황 공개 |
| 2024-05 | React Conf 2024 에서 experimental 공개 |
| 2024-10 | Beta 출시. Meta 사내 사용량 확대 |
| 2025-10-07 | v1.0 stable, ESLint 통합, 새 프로젝트 기본 enable |
| 2025-10-16 | React Conf 2025 에서 리뷰 |
다음
v1.0 은 시작점. 공식 글이 명시하는 방향:
- 더 정밀한 memoization (현재보다 세분화 + 예측 가능성).
- 더 많은 Rules of React 를 정적으로 검증.
- swc / oxc 와의 통합 강화 → 빌드 속도.
- Library precompilation: 라이브러리 저작자가 미리 컴파일된 결과 를 패키지에 동봉.
관련 위키
- React (React 19/19.x 핵심 기능)
- SPA Architecture (memoization 의 역사적 맥락)
- JS Closure (memoize 가 의존하는 어휘 환경)
참고
이 글의 용어 (3개)
- [Frontend] SPA Architecture: CSR, SSR, SSG, hydrationjavascript
- 정의 SPA (Single Page Application) 는 첫 로딩 시 받은 HTML / JS 번들 하나로 모든 화면 전환을 클라이언트에서 처리하는 웹 앱 구조. URL 변경…
- [Javascript] 클로저javascript
- 정의 클로저 (Closure): 함수가 자신이 정의된 시점의 외부 변수들을 계속 참조할 수 있는 성질. 함수가 일급 객체 () 인 언어의 자연스러운 결과. 메커니즘은 + 함수 객…
- [Voice AI] Agent 패턴: Supervisor, Handoff, ReAct, HITLai
- 정의 복잡한 음성 에이전트 = 여러 sub-agent + 조정 패턴. 단일 LLM 으로 처리하기 어려운 흐름. [!IMPORTANT] 각 sub-agent 내부는 VAD → ST…
💬 댓글