JavaScript

React Compiler의 한계 — 뭘 최적화하고 뭘 못 하는가

React Compiler가 자동 메모이제이션으로 해결하는 것과 해결하지 못하는 것. 컴파일러 기반 UI 프레임워크의 능력 경계를 정리했다.

6 min read
ReactReact Compiler성능메모이제이션프레임워크
React Compiler의 한계 — 뭘 최적화하고 뭘 못 하는가

React Compiler는 React를 대체하지 않는다

React Compiler가 나왔을 때 많은 사람이 기대한 건 React의 렌더링 모델 자체가 바뀌는 것이었다. useMemouseCallback을 수동으로 쓸 필요가 없어진다는 건 맞다. 그런데 "그게 Signal 기반 프레임워크처럼 세밀한 업데이트를 한다는 뜻인가"라고 묻는다면, 아니다.

컴파일러는 React의 기존 계약 안에서 동작한다. 규칙을 바꾸는 게 아니라, 그 규칙을 지키면서 불필요한 재실행을 줄인다.


실제로 하는 일

React Compiler는 세 가지를 한다.

1. 자동 의존성 추론

src/components/UserCard.tsx
// 컴파일 전: 수동 메모이제이션
function UserCard({ user, onClick }) {
  const fullName = useMemo(
    () => `${user.firstName} ${user.lastName}`,
    [user.firstName, user.lastName]
  )
  const handleClick = useCallback(() => onClick(user.id), [onClick, user.id])
  return <div onClick={handleClick}>{fullName}</div>
}
 
// 컴파일 후: 컴파일러가 처리
function UserCard({ user, onClick }) {
  const fullName = `${user.firstName} ${user.lastName}`
  const handleClick = () => onClick(user.id)
  return <div onClick={handleClick}>{fullName}</div>
}

코드에서 useMemouseCallback을 지워도 된다. 컴파일러가 어떤 값이 언제 바뀌는지 추론해서 캐싱을 삽입한다.

[💡 잠깐! 이 용어는?] 메모이제이션(Memoization): 동일한 입력에 대해 이전 결과를 재사용하는 최적화. React에서는 불필요한 컴포넌트 재렌더링과 함수 재생성을 막는 데 쓴다.

2. JSX 서브트리 재사용

동일한 props가 들어오면 이미 계산한 JSX 결과를 재사용한다. 이전에는 React.memo()로 컴포넌트를 직접 감싸야 했던 것을 컴파일러가 자동화한다.

3. 성능 바닥을 높인다

최악의 케이스(모든 게 매번 새로 계산되는 상황)를 없애준다. 처음부터 잘 짠 코드보다는 못하더라도, 최적화를 빠뜨린 코드의 성능 하한선이 올라간다.


못 하는 것들

기대실제
Signal 기반 세밀한 DOM 업데이트컴포넌트/서브트리 단위 업데이트 유지
Hook 실행 순서 변경Fiber/Hook 계약 그대로 유지
스케줄링 방식 변경Concurrent 스케줄링 모델 변경 없음
아키텍처 문제 해결구조적 최적화는 여전히 개발자 몫

컴파일러는 "재실행을 줄이는" 것이지 "다른 방식으로 실행"하는 게 아니다. React의 기본 동작인 "컴포넌트를 리렌더링하고 이전 결과와 비교"하는 흐름은 바뀌지 않는다.

[💡 잠깐! 이 용어는?] Fiber 스케줄러: React 16부터 도입된 렌더링 엔진. 작업을 잘게 쪼개 우선순위에 따라 처리하는 구조. Concurrent Mode의 기반이 되며, Compiler는 이 계약 밖에서 동작하지 않는다.


Compiler-First 프레임워크와 비교

Svelte, SolidJS 같은 컴파일러 우선 프레임워크는 런타임 자체가 다르다.

차원React + CompilerCompiler-First (Svelte/Solid)
업데이트 단위컴포넌트/서브트리소스 레벨 의존성 전파
메커니즘자동 메모이제이션빌드타임 의존성 그래프
마이그레이션 비용낮음-중간중간-높음
런타임 번들 크기일반적으로 큼작음

비유하면 React Compiler는 기존 고속도로에 자동 크루즈 컨트롤을 추가한 것이다. Svelte는 아예 다른 방식으로 설계된 도로다. 더 빠를 수 있지만 진입로가 다르다.

포인트: "두 접근법은 서로 다른 비용을 최적화한다. 모더니티 경쟁이 아니라 목적 함수 선택이다."


언제 React Compiler가 효과적인가

  • 기존 React 코드베이스를 그대로 유지하면서 성능을 높이고 싶을 때
  • useMemo/useCallback 남발로 코드가 복잡해진 팀
  • 컴포넌트 트리가 깊고 불필요한 리렌더링이 프로파일러에서 확인될 때

반대로 아키텍처 자체가 문제인 경우 — N+1 렌더링, 너무 큰 Context 범위, 깊은 prop drilling — 은 컴파일러로 해결되지 않는다. 구조는 여전히 개발자가 잡아야 한다.


마무리

React Compiler는 "React 프로그래밍 모델을 유지하면서 성능 바닥을 높이는 도구"다. 모든 프레임워크를 대체하는 만능 최적화가 아니다. 정확한 기대치를 갖고 쓰면 실무에서 유용하다 — 특히 수동 메모이제이션 코드를 정리하고 싶은 팀에게는 즉각적인 코드 정리 효과가 있다.


참고: