React Compiler 2026: 자동 메모이제이션의 원리와 기술 면접 완벽 가이드

React Compiler v1.0의 자동 메모이제이션 내부 구조, 컴파일 파이프라인, 수동 최적화가 필요한 시나리오를 상세히 분석합니다. 2026년 React 기술 면접에서 자주 출제되는 핵심 질문을 포괄적으로 다룹니다.

React Compiler 2026 automatic memoization interview questions

React Compiler는 2025년 10월에 안정 버전 v1.0에 도달하며, React 애플리케이션의 성능 최적화 방식을 근본적으로 바꾸었습니다. 2026년 기술 면접에서는 컴파일러의 내부 구조, 메모이제이션 전략, 그리고 수동 최적화가 여전히 필요한 시나리오에 대한 이해가 필수적입니다.

면접 핵심 포인트

React Compiler는 정적 분석을 통해 컴포넌트와 훅을 자동으로 메모이제이션하는 빌드 타임 도구입니다. 대부분의 경우 useMemo, useCallback, React.memo의 수동 작성이 불필요해지지만, 모든 경우에 해당하는 것은 아닙니다. 면접관은 컴파일러가 처리할 수 있는 영역과 처리할 수 없는 영역 모두를 명확히 설명할 수 있는 능력을 기대합니다.

React Compiler란 무엇이며, 코드를 어떻게 최적화하는가

React Compiler는 React 컴포넌트와 훅을 분석하여 유효한 위치에 세밀한 메모이제이션을 삽입하는 빌드 타임 최적화 컴파일러입니다. useMemouseCallback을 사용한 수동 메모이제이션과 달리, 컴파일러는 조건부로 그리고 개발자가 직접 유지하기 어려운 수준의 세밀한 단위로 메모이제이션을 수행할 수 있습니다.

컴파일러는 Babel 플러그인으로 동작합니다(Next.js 15.3.1 이상에서는 SWC 지원이 확대되고 있습니다). 각 컴포넌트를 독립적으로 처리하며, Babel AST를 커스텀 고수준 중간 표현(HIR)으로 변환한 후 여러 최적화 패스를 실행합니다.

Meta의 프로덕션 환경 결과가 그 효과를 입증합니다. Quest Store에서는 초기 로딩이 최대 12% 빨라졌고, 인터랙션은 2.5배 이상 빨라졌습니다. Sanity Studio에서는 1,231개의 컴파일된 컴포넌트에서 렌더링 시간이 20-30% 감소했다고 보고되었습니다.

면접 답변 구성: 컴파일러를 정의하고, 런타임이 아닌 빌드 타임에 동작한다는 것을 설명하며, HIR 파이프라인을 언급하고, 실제 성능 지표를 인용합니다.

컴파일 파이프라인: 소스 코드에서 최적화된 코드까지

이 질문은 깊은 이해를 평가합니다. React Compiler 파이프라인은 7개의 명확한 단계로 구성됩니다.

  1. 소스에서 AST로 — Babel이 JavaScript/TypeScript를 추상 구문 트리(AST)로 파싱합니다
  2. AST에서 HIR로 — 컴파일러가 AST를 React의 컴포넌트 모델 전용으로 설계된 고수준 중간 표현으로 변환합니다
  3. SSA 변환 — 정적 단일 할당 형식을 통해 각 변수가 정확히 한 번만 할당되도록 보장하여 정밀한 데이터 흐름 추적을 가능하게 합니다
  4. 타입 추론과 이펙트 분석 — 컴파일러가 연산을 영향도에 따라 분류합니다: read, store, capture, mutate, freeze
  5. 리액티브 분석 — 모든 표현식이 static(상수, 임포트) 또는 reactive(props, state, context에서 파생)으로 분류됩니다
  6. 스코프 발견 — 컴파일러가 독립적으로 캐시하고 무효화할 수 있는 개별 메모이제이션 스코프를 식별합니다
  7. 코드 생성 — 자동 메모이제이션이 포함된 최적화된 JavaScript가 출력됩니다
javascript
// 컴파일 전
function UserProfile({ user, theme }) {
  const fullName = user.firstName + ' ' + user.lastName;
  const style = { color: theme.primary, fontSize: 16 };
  return <div style={style}>{fullName}</div>;
}

// 컴파일 후 (간소화된 표현)
function UserProfile({ user, theme }) {
  const $ = useMemoCache(4);
  let fullName;
  if ($[0] !== user.firstName || $[1] !== user.lastName) {
    fullName = user.firstName + ' ' + user.lastName;
    $[0] = user.firstName;
    $[1] = user.lastName;
    $[2] = fullName;
  } else {
    fullName = $[2];
  }
  // style과 JSX도 동일하게 메모이제이션...
}

면접에서 핵심적으로 이해해야 할 점은 컴파일러가 컴포넌트 수준이 아닌 값 수준에서 메모이제이션을 수행한다는 것입니다. 각 계산된 값은 고유한 캐시 슬롯을 가지며, 해당 값의 특정 의존성이 변경될 때만 무효화됩니다. 이 세밀한 단위는 실제 코드에서 useMemo가 달성하는 수준을 넘어섭니다.

React의 규칙: 컴파일러가 전제하는 것들

React Compiler는 컴포넌트가 React의 규칙을 따른다는 전제하에 정적 분석을 수행합니다. 이러한 규칙을 위반하는 코드에 대해 컴파일러는 해당 컴포넌트의 최적화를 조용히 건너뜁니다. 이는 면접에서 자주 다뤄지는 주제입니다.

세 가지 핵심 규칙:

  • 컴포넌트와 훅은 순수해야 합니다 — 동일한 입력에 대해 렌더가 동일한 출력을 생성해야 합니다. 렌더 중에 외부의 가변 상태를 읽거나 쓰면 안 됩니다.
  • 훅은 호출 규칙을 따라야 합니다 — 훅을 조건문 내부, 반복문 내부, 또는 중첩된 함수 내부에서 호출할 수 없습니다.
  • Props와 State는 렌더 내에서 불변입니다 — props나 state를 직접 변경해서는 안 되며, 항상 setter 함수를 사용해야 합니다.
javascript
// 규칙 위반: 렌더 중 뮤테이션
function BadCounter({ items }) {
  // 컴파일러가 이 컴포넌트를 건너뜁니다
  items.sort(); // props 배열을 뮤테이트하고 있습니다!
  return <ul>{items.map(i => <li key={i}>{i}</li>)}</ul>;
}

// 올바른 구현: 새로운 정렬된 배열 생성
function GoodCounter({ items }) {
  const sorted = [...items].sort();
  return <ul>{sorted.map(i => <li key={i}>{i}</li>)}</ul>;
}
면접에서 흔한 함정

컴파일러가 컴포넌트를 건너뛰어도 에러가 발생하지 않습니다. 애플리케이션은 정상적으로 동작하지만, 해당 컴포넌트는 최적화되지 않습니다. ESLint 플러그인(eslint-plugin-react-hooksrecommended 프리셋)이 프로덕션에 도달하기 전에 이러한 위반을 감지하는 주요 수단입니다.

ESLint 통합: 빌드 전에 위반 사항 감지하기

독립 패키지인 eslint-plugin-react-compiler는 2025년 후반에 지원이 종료되었습니다. 컴파일러 관련 모든 린트 규칙은 eslint-plugin-react-hooks@latest에 통합되었습니다.

ESLint 플랫 설정 방법:

eslint.config.jsjavascript
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 — 이펙트 내의 고비용 연산이 연쇄적인 리렌더를 유발하는 경우
  • refs — 렌더 중에 ref의 .current에 접근하는 경우 (ref는 가변적인 탈출구이며, 렌더에 안전하지 않습니다)

면접에서는 권장 도입 전략을 설명할 수 있어야 합니다. 먼저 ESLint 규칙을 활성화하고, 모든 위반 사항을 수정한 다음, 컴파일러를 활성화합니다. 이 단계적 접근 방식은 예기치 않은 문제를 최소화합니다.

수동 최적화가 여전히 필요한 경우

컴파일러가 모든 성능 관련 작업을 대체하는 것은 아닙니다. 몇 가지 시나리오에서는 수동 개입이 필요합니다.

1. 외부 스토어 구독

컴파일러는 React의 state, props, context만 추적합니다. 외부 스토어(Redux, Zustand, MobX)에는 렌더 최적화를 위해 useSyncExternalStore 또는 스토어 자체의 셀렉터 메커니즘이 필요합니다.

2. 대규모 데이터셋에 대한 고비용 연산

컴파일러가 반환 값을 메모이제이션하더라도, 실행 비용을 프로파일링하지는 않습니다. 100,000개의 항목을 필터링하는 함수는 의존성이 변경될 때마다 실행됩니다. 진정으로 고비용인 연산에는 명시적 의존성을 가진 useMemo가 여전히 유효합니다(컴파일러가 메모이제이션을 중복시키지는 않습니다).

3. Ref 기반 명령형 로직

Ref는 컴파일러의 리액티브 모델에서 의도적으로 제외되어 있습니다. 애니메이션, canvas 렌더링, 서드파티 DOM 라이브러리 통합에는 수동 제어가 필요합니다.

4. 컴포넌트 간 메모이제이션

컴파일러는 단일 컴포넌트 또는 훅 내에서 메모이제이션을 수행합니다. 컴포넌트 간에 계산된 값을 공유하려면 state 끌어올리기, context, 또는 외부 캐시 계층이 필요합니다.

javascript
// 컴파일러가 자동으로 처리하는 경우
function ProductCard({ product, currency }) {
  const formattedPrice = formatCurrency(product.price, currency);
  const discount = product.originalPrice - product.price;
  return (
    <div>
      <span>{formattedPrice}</span>
      {discount > 0 && <Badge>Save {discount}</Badge>}
    </div>
  );
}

// 수동 최적화가 필요한 경우: 고비용 외부 연산
function DataGrid({ rows }) {
  const processedRows = useMemo(
    () => rows.map(row => heavyTransform(row)), // O(n) with large n
    [rows]
  );
  return <VirtualList items={processedRows} />;
}

React / Next.js 면접 준비가 되셨나요?

인터랙티브 시뮬레이터, flashcards, 기술 테스트로 연습하세요.

프레임워크별 React Compiler 활성화 방법

면접에서는 도구 체인 설정에 대한 실무적 지식도 평가됩니다.

Next.js (15.3.1 이상): SWC 네이티브 지원. next.config.js에서 활성화합니다:

next.config.jsjavascript
const nextConfig = {
  experimental: {
    reactCompiler: true,
  },
};
export default nextConfig;

Vite 8 + @vitejs/plugin-react v6: v6에서 내부 Babel을 폐기하고 oxc로 전환했기 때문에 컴파일러에는 @rolldown/plugin-babel이 필요합니다:

vite.config.jsjavascript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import babel from '@rolldown/plugin-babel';
import { reactCompilerPreset } from 'babel-plugin-react-compiler';

export default defineConfig({
  plugins: [
    react(),
    babel({ presets: [reactCompilerPreset] }),
  ],
});

Expo SDK 54 이상: 기본적으로 활성화되어 있습니다. 별도의 설정이 필요하지 않습니다.

버전 호환성

React Compiler v1.0은 React 17, 18, 19에서 동작합니다. 그러나 완전한 최적화 기능은 React 19에서만 사용할 수 있으며, 런타임이 useMemoCache를 통한 세밀한 캐시 무효화를 지원합니다.

React Compiler vs 수동 메모이제이션: 면접용 비교

| 항목 | React Compiler | 수동 (useMemo/useCallback) | |------|---------------|----------------------------------| | 세밀도 | 값 수준, 표현식별 | 블록 수준, 개발자가 정의 | | 조건부 메모이제이션 | 지원 | 불가능 | | 유지보수 비용 | 제로 — 자동 | 높음 — 의존성 배열, 래퍼 컴포넌트 | | 컴포넌트 간 | 단일 컴포넌트 범위 | 단일 컴포넌트 범위 | | 외부 스토어 | 추적하지 않음 | 추적하지 않음 (useSyncExternalStore 사용) | | 클래스 컴포넌트 | 지원하지 않음 | N/A (React.memo 사용) | | 빌드 요구 사항 | Babel 플러그인 또는 SWC | 없음 | | 성능 상한 | 리렌더 20-60% 감소 (일반적) | 개발자 역량에 따라 다름 |

면접 요약: 컴파일러는 개발자가 이전에 수동으로 처리하던 메모이제이션의 80-90%를 더 세밀한 단위와 제로 유지보수 오버헤드로 자동화합니다. 나머지 10-20%는 외부 통합, 고비용 연산, 명령형 DOM 작업과 관련된 부분입니다.

자주 나오는 면접 질문과 간결한 답변

Q: React Compiler는 빌드 타임에 동작합니까, 런타임에 동작합니까? 빌드 타임에 동작합니다. 컴파일러는 컴파일 과정에서 소스 코드를 변환하는 Babel 플러그인입니다. 최적화된 출력은 캐시된 값을 저장하기 위해 런타임 헬퍼(useMemoCache)를 사용하지만, 모든 분석은 배포 전에 수행됩니다.

Q: 기존 useMemouseCallback 호출을 제거해야 합니까? React 팀은 기존 수동 메모이제이션을 그대로 유지할 것을 권장합니다. 제거하면 컴파일 결과가 미묘하게 달라질 수 있습니다. 새로운 코드에서는 컴파일러에 의존하고, 프로파일링을 통해 구체적인 필요성이 확인된 경우에만 수동 메모이제이션을 추가합니다.

Q: 컴파일러가 최적화할 수 없는 코드를 만나면 어떻게 됩니까? 해당 컴포넌트나 훅을 조용히 건너뜁니다. 애플리케이션은 정상적으로 작동하지만, 해당 부분은 자동 메모이제이션 없이 실행됩니다. ESLint 규칙이 개발 중에 이러한 경우를 감지해 줍니다.

Q: 컴파일러는 Server Components와 어떻게 상호 작용합니까? Server Components는 서버에서 실행되며 직렬화된 출력을 생성합니다. 클라이언트에서 리렌더되지 않습니다. 컴파일러는 리렌더가 주요 성능 문제인 클라이언트 컴포넌트를 대상으로 합니다. 이 두 가지 최적화는 상호 보완적입니다. Server Components는 번들 크기를 줄이고, 컴파일러는 클라이언트 리렌더 비용을 줄입니다.

Q: 컴파일러는 커스텀 훅을 최적화할 수 있습니까? 네, 가능합니다. 컴파일러는 컴포넌트와 훅 모두를 분석합니다. 파생 상태를 계산하는 커스텀 훅의 반환 값은 자동으로 메모이제이션되며, 해당 훅을 호출하는 모든 컴포넌트에 혜택을 제공합니다.

결론

  • React Compiler v1.0은 7단계 컴파일 파이프라인(AST, HIR, SSA, 타입 추론, 이펙트 분석, 리액티브 분석, 코드 생성)을 통해 세밀한 값 수준 메모이제이션을 삽입하는 빌드 타임 도구입니다
  • 컴파일러는 컴포넌트가 React의 규칙을 따르는 것을 전제로 하며, 위반 시 조용히 최적화를 건너뜁니다 (eslint-plugin-react-hooksrecommended 프리셋으로 감지 가능)
  • 외부 스토어 구독, 대규모 데이터셋의 고비용 연산, Ref 기반 명령형 로직, 컴포넌트 간 캐싱에는 수동 최적화가 여전히 필요합니다
  • 프레임워크별 설정: Next.js에서는 SWC 네이티브, Vite 8에서는 @rolldown/plugin-babel, Expo SDK 54 이상에서는 기본 활성화
  • 면접에서는 컴파일러가 자동화하는 영역(메모이제이션의 80-90%)과 자동화하지 않는 영역(외부 state, 고비용 컴포넌트 간 연산, 클래스 컴포넌트)을 모두 명확히 설명할 수 있어야 합니다
  • 권장 도입 경로는 먼저 ESLint 규칙을 활성화한 후 컴파일러를 활성화하는 단계적 전략이며, 면접관은 이 접근 방식에 대해 질문할 것으로 예상됩니다

연습을 시작하세요!

면접 시뮬레이터와 기술 테스트로 지식을 테스트하세요.

태그

#react
#react-compiler
#memoization
#performance
#interview

공유

관련 기사