[React] React Compiler 가볍게 살펴보기
React Compiler
- 컴포넌트와 훅을 자동으로 최적화(메모이제이션 Memoization)
useMemo,useCallback,memo사용에 대해 더이상 신경쓰지 않아도 됨.- 선택적으로 컴파일이 가능. 참조 : react compiler의 코드 변환 결과물을 보고싶다면 playground 에서 테스트 가능.
준비
react compiler를 사용하기 위해 react 버전 확인, eslint 플러그인, babel 플러그인 설정이 필요.
최소 지원 버전
- react compiler는 17버전부터 지원하며, 19버전에서 가장 잘 동작.
eslint-plugin-react-hooks
- react의 기본적인 rule 위반 사항을 체크하는 lint
- react compiler는 규칙을 위반한 컴포넌트는 컴파일 대상에서 제외.
- npm 링크
eslint-plugin-react-compiler
- react-compiler 기준으로 rule 위반사항을 체크하는 lint (기본 react rule보다 더 강하게 체크)
- 마찬가지로 react compiler는 규칙을 위반한 컴포넌트는 컴파일 대상에서 제외.
eslint-disable구문이 있는 컴포넌트도 제외.- npm 링크

babel-plugin-react-compiler
- react-compiler가 “컴포넌트나 훅이 어떻게 렌더되고 언제 재렌더되는가”를 정적 분석하기 위한 플러그인.
- npm 링크
예시
매우 무거운 연산을 실행하는 컴포넌트
export default function SlowComponent({ value }: { value: number }) {
console.log("SlowComponent rendered with", value);
// 매우 느린 계산 시뮬레이션
let sum = 0;
for (let i = 0; i < 100000000; i++) {
sum += i * value;
}
return <div>Computed: {sum}</div>;
}
SlowComponent 를 렌더링하는 부모 컴포넌트
import { useState } from "react";
import SlowComponent from "./SlowComponent";
export default function SlowComponentWrapperWithCompiler() {
const [count, setCount] = useState(0);
const [value, setValue] = useState(1);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount((c) => c + 1)}>
Increment count (SlowComponent에 영향을 주지 않는 상태 변화)
</button>
<h1>Value: {value}</h1>
<button onClick={() => setValue((v) => v + 1)}>
Increment value (SlowComponent에 영향을 주는 상태 변화(props 전달)
</button>
<SlowComponent value={value} />
</div>
);
}
SlowComponent 를 렌더링 하지만, "use no memo" 지시자를 사용하여 컴파일에서 제외된 부모 컴포넌트
"use no memo"; // 컴포넌트가 React 컴파일러에 의해 컴파일되지 않도록 제외.
import { useState } from "react";
import SlowComponent from "./SlowComponent";
export default function NoCompilerSlowComponentWrapper() {
const [count, setCount] = useState(0);
const [value, setValue] = useState(1);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount((c) => c + 1)}>
Increment count (SlowComponent에 영향을 주지 않는 상태 변화)
</button>
<h1>Value: {value}</h1>
<button onClick={() => setValue((v) => v + 1)}>
Increment value (SlowComponent에 영향을 주는 상태 변화(props 전달)
</button>
<SlowComponent value={value} />
</div>
);
}
컴파일 결과
- 컴파일을 통해 자동 최적화에 성공한 컴포넌트는 react dev tool에 “Memo ✨” 아이콘 표시

진짜 잘 메모이제이션 되었는가?
최적화된 컴포넌트의 상태변화


const [count, setCount] = useState(0);의 count가 0 → 1로 변경되는 과정에서 자식 컴포넌트 (SlowComponent) 는 리렌더링 되지 않았음. (최적화됨)
최적화안된 컴포넌트의 상태변화



- 부모가 가진 상태
const [count, setCount] = useState(0);가 0 → 1로 변화하면서 자식 컴포넌트가 리렌더링 되는것을 볼 수 있다.
지시자
use memo: 해당 지시자가 있는 함수, 컴포넌트는 일단 컴파일 시도use no memo: 해당 지시자가 있는 함수, 컴포넌트는 어떤 컴파일 모드에서든 컴파일 시도 X- 최적화시 의도하지 않은 문제가 예상되는 컴포넌트에 사용.
- 상세 설명 : https://react.dev/reference/react-compiler/directives
컴파일모드
- react-compiler는 여러 모드를 지원.
- 문서 : compilationmode
타입
'**infer**' | 'syntax' | 'annotation' | 'all'
기본값
'infer'
옵션별 설명
'infer'(기본값)- 컴파일러가 스마트한 휴리스틱(heuristics)을 이용해 컴포넌트나 훅을 식별.
"use memo"지시자가 붙은 함수들.- PascalCase(첫 글자 대문자) 형태 이름 + JSX를 반환하거나 훅(Hooks)을 호출하는 함수.
- 컴파일러가 스마트한 휴리스틱(heuristics)을 이용해 컴포넌트나 훅을 식별.
'annotation'"use memo"지시자가 명시적으로 붙은 함수만 컴파일.- 점진적(migration)으로 적용할 때 적합.
'syntax'- Flow 문법을 사용하는 코드베이스일 때만 컴파일.
- 예컨대 Flow의
component/hook구문을 사용한 경우.
'all'- 사용을 권장하지 않음.
- 최상위(top-level) 함수 전부를 컴파일 대상에 포함.
- 주의: 유틸 함수 같은 일반 함수까지 컴파일되어 성능에 부정적 영향을 줄 수 있음.
- 컴파일 옵션 적용 예시
//vite.config.ts export default defineConfig({ plugins: [ react({ babel: { plugins: [ [ "babel-plugin-react-compiler", // 다른 플러그인보다 먼저 와야함. { compilationMode: "infer", }, ], ], }, }), ], });
성능이 개선되는가?
- 그럴수도 있고 아닐수도 있음.
- 이미 메모이제이션이 잘 적용된 프로젝트는 성능 개선이 미비함.
- 메모이제이션이 잘 되지 않은 프로젝트에서는 성능 개선 효과를 볼 수 있음.
반드시 사용해야하나?
-
react 공식 문서에도 점진적인 도입을 권장
자바스크립트의 유연한 특성으로 인해 컴파일러가 가능한 모든 위반 사항을 잡아내지는 못하며, 가끔 거짓 음성False Negatives으로 컴파일할 수 있습니다. 즉, 컴파일러는 React의 규칙을 위반하는 컴포넌트나 Hook을 실수로 컴파일할 수 있어 정의되지 않은 동작으로 이어질 수 있습니다. 따라서 기존 프로젝트에서 컴파일러를 성공적으로 도입하려면, 먼저 제품 코드의 작은 디렉토리에서 실행해 보는 것이 좋습니다. 이를 위해 컴파일러를 특정 디렉토리 집합Set에서만 실행하도록 구성할 수 있습니다. - 지금은 아니더라도 점진적으로 도입은 해야한다고 생각.
- 당장 준비가 어렵다면 eslint 플러그인으로 규칙 위반부터 하나씩 수정해나가는것도 방법
참고한문서
Leave a comment