99

최근 30분간 동시 방문자 수를 표시합니다. (~)

최고 동시 방문자 수 -
어제: 0명 / 오늘: 0명

(초안) React Compiler 핵심 정리

(초안) React Compiler 핵심 정리

info

이 글은 React Compiler v1.0.0 버전을 기준으로 작성되었습니다.

React Compiler는 빌드 타임 최적화 도구로, 컴포넌트의 리렌더링과 메모이제이션을 자동으로 처리해 복잡한 수동 최적화 없이도 최적의 성능을 얻을 수 있습니다.
더 이상 useMemouseCallback 훅이나 memo 함수를 사용할 필요가 없습니다.
React v19부터 공식 지원되며, 추가 구성을 통해 React v17, v18에서도 사용할 수 있습니다.

# 컴파일러 사용 전

React Compiler를 사용하지 않는 기존에는 리렌더링을 최적화하기 위해 컴포넌트, 함수 그리고 값을 React.memo, useCallback, useMemo를 사용해 수동으로 메모이제이션해야 했습니다.
이를 통해 불필요한 리렌더링이나 추가 연산을 방지할 수 있지만, 코드 가독성을 해치고 더 복잡한 코드를 작성해야 합니다.

/src/components/Counter.tsx
TSX
content_copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { memo, useMemo } from 'react' function Counter({ count, onIncrease }: { count: number onIncrease: () => void }) { console.log('Counter 컴포넌트 렌더링!') // useMemo로 계산된 값 메모이제이션 const double = useMemo(() => { console.log('double 값 계산!') return count * 2 }, [count]) return ( <div> <p>Count: {count}</p> <p>Double: {double}</p> <button onClick={onIncrease}>증가!</button> </div> ) } // memo로 컴포넌트 메모이제이션 export default memo(Counter)
/src/App.tsx
TSX
content_copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { useState, useCallback } from 'react' import Counter from '@/components/Counter' export default function App() { const [text, setText] = useState('') const [count, setCount] = useState(0) // useCallback으로 함수 메모이제이션 const increase = useCallback(() => { console.log('increase 함수 호출!') setCount(c => c + 1) }, []) return ( <> <input placeholder="아무 값이나 입력해보세요!" value={text} onChange={e => setText(e.target.value)} /> <Counter count={count} onIncrease={increase} /> </> ) }

# 컴파일러 사용 후

React Compiler를 사용하면, 수동 메모이제이션을 직접 작성한 것 이상으로 최적화되어 동작합니다.
상태가 변경되면 필요한 부분만 리렌더링하거나 연산하며, 이를 세밀한 반응성(fine-grained reactivity)이라고도 부릅니다.

/src/components/Counter.tsx
TSX
content_copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function getDouble(count: number) { console.log('double 값 계산!') return count * 2 } export default function Counter({ count, onIncrease }: { count: number onIncrease: () => void }) { console.log('Counter 컴포넌트 렌더링!') const double = getDouble(count) return ( <> <p>Count: {count}</p> <p>Double: {double}</p> <button onClick={onIncrease}>증가!</button> </> ) }
/src/App.tsx
TSX
content_copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { useState } from 'react' import Counter from '@/components/Counter' export default function App() { const [text, setText] = useState('') const [count, setCount] = useState(0) const increase = () => { console.log('increase 함수 호출!') setCount(c => c + 1) } return ( <> <input placeholder="아무 값이나 입력해보세요!" value={text} onChange={e => setText(e.target.value)} /> <Counter count={count} onIncrease={increase} /> </> ) }

# 새로운 프로젝트에서 사용

Vite 빌드 도구나 Next.js 프로젝트를 통해 새로운 프로젝트를 만드는 경우, 아주 간단하게 React Compiler를 사용할 수 있습니다.

Vite 구성

BASH
content_copy
1
2
3
4
5
npm create vite@latest . ◇ Select a framework: React ◇ Select a variant: TypeScript + React Compiler # ...

Next.js 구성

BASH
content_copy
1
2
3
4
5
6
7
npx create-next-app@latest . ✔ Would you like to use the recommended Next.js defaults? › No, customize settings ✔ Would you like to use TypeScript? … No / Yes ✔ Which linter would you like to use? › ESLint ✔ Would you like to use React Compiler? … No / Yes # ...

# 기존 프로젝트에서 사용

기존 프로젝트에서 React Compiler를 사용하기 위해 필요한 단계를 살펴봅시다.
점진적 도입으로 작은 부분부터 테스트하고 안정성을 확보한 후에 전체로 확장하는 것이 좋습니다.
우선 필수 패키지로 다음과 같이 babel-plugin-react-compiler를 설치합니다.

BASH
content_copy
1
npm i -D babel-plugin-react-compiler

Vite 구성

vite.config.ts 구성 파일의 React 옵션에서 Babel 플러그인으로 babel-plugin-react-compiler를 추가합니다.
플러그인 이름을 명시해 단순히 기본값으로 React Compiler를 사용하거나, 점진적 도입을 위한 컴파일러 옵션을 추가할 수도 있습니다.

/vite.config.ts
TS
content_copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [ react({ babel: { plugins: [ // 'babel-plugin-react-compiler' // 혹은 ['babel-plugin-react-compiler', { // 컴파일러 옵션.. }] ] } }) ] })

Next.js 구성

Next.js v15.3.1 이상에서는 간단하게 React Compiler를 사용할 수 있습니다.
next.config.ts 구성 파일에서 reactCompiler 옵션을 사용하여 SWC 기반의 React Compiler를 활성화합니다.

/next.config.ts
TS
content_copy
1
2
3
4
5
6
7
8
9
10
import type { NextConfig } from 'next' const nextConfig: NextConfig = { // reactCompiler: true // 혹은 reactCompiler: { // 컴파일러 옵션.. } } export default nextConfig

# React 17/18 지원

추가 구성을 통해 React v17 혹은 v18에서도 React Compiler를 사용할 수 있습니다.
react-compiler-runtime 패키지를 설치하고 target 컴파일러 옵션을 통해 사용하는 React 버전을 명시합니다.
이를 통해 React 버전에 맞게 올바른 API 구현을 사용하거나 누락된 API를 폴리필합니다.

BASH
content_copy
1
npm i react-compiler-runtime
패키지 설치
TS
content_copy
1
2
3
4
5
6
7
{ plugins: [ ['babel-plugin-react-compiler', { target: '17' }] ] }
컴파일러 옵션 추가 / Vite
TS
content_copy
1
2
3
4
5
{ reactCompiler: { target: '17' } }
컴파일러 옵션 추가 / Next.js

# ESLint 통합

React Compiler를 통해 최적화할 수 없는 코드를 식별하는 데 도움이 되는 ESLint 규칙을 사용할 수 있습니다.
eslint-plugin-react-hooks ESLint 플러그인의 추천 규칙 프리셋을 구성합니다.

warning

기존 프로젝트에서 이미 eslint-plugin-react-hooks ESLint 플러그인을 사용하고 있을 수 있습니다.
설치 여부와 버전을 확인하고, 필요한 경우 v7 이상으로 업데이트해야 합니다.

BASH
content_copy
1
npm i -D eslint-plugin-react-hooks@^7
패키지 설치
/eslint.config.js
JS
content_copy
1
2
3
4
5
6
7
8
9
import reactHooks from 'eslint-plugin-react-hooks' export default defineConfig([ { extends: [ reactHooks.configs.flat.recommended ] } ])
추천 프리셋 구성

# 디렉토리 기반

Babel의 overrides 옵션을 사용하면 코드베이스의 다른 부분에 다른 플러그인을 적용할 수 있습니다.
특정 디렉토리부터 시작해 점진적으로 확장할 수 있습니다.

Vite 구성

/vite.config.ts
TS
content_copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react({ babel: { overrides: [ { test: [/src\/features\/.*\.(js|jsx|ts|tsx)$/], plugins: [ ['babel-plugin-react-compiler', { target: '17' }] ] } ] } })] })

# 옵트아웃 기반

React Compiler에서는 React 이름 규칙을 준수하는 컴포넌트나 훅을 자동으로 식별해 컴파일합니다.
테스트를 위해 일부 컴포넌트나 훅을 컴파일하지 않고 싶다면, 'use no memo' 지시어(Directive)를 사용해 제외할 수 있으며, 이를 옵트아웃(Opting Out)이라고 합니다.
지시어는 컴포넌트나 훅의 최상위 함수 레벨에서 사용해야 합니다.

/src/components/Counter.tsx
TSX
content_copy
1
2
3
4
5
export default function Counter() { 'use no memo' // ... }

# 옵트인 기반

테스트하려는 일부 컴포넌트나 훅 외에는 컴파일하지 않고 싶다면, 'use memo' 지시어를 사용할 수 있습니다.
다만 'use no memo' 지시어와는 다르게, compilationMode: 'annotation'을 옵션을 사용해야 지시어가 명시된 경우에만 컴파일할 수 있습니다.
이를 옵트인(Opting In)이라고 합니다.

TS
content_copy
1
2
3
4
5
6
7
{ plugins: [ ['babel-plugin-react-compiler', { compilationMode: 'annotation' }] ] }
컴파일러 옵션 추가

컴파일하려는 컴포넌트나 훅에서 'use memo' 지시어를 추가합니다.

/src/components/Counter.tsx
TSX
content_copy
1
2
3
4
5
export default function Counter() { 'use memo' // ... }