React는 등장 이후로 개발자들이 사용자 인터페이스를 구축하는 방식을 혁신해왔으며, 매번 혁신적이고 강력한 방법을 제공하여 동적이고 컴포넌트 기반의 애플리케이션을 만들 수 있게 했습니다. React 컴파일러는 이러한 혁신의 최신 예로, 성능 최적화를 위한 강력한 도구입니다.
많은 장점에도 불구하고, React는 전통적으로 Vue와 Svelte 같은 프레임워크와 비교했을 때 전용 컴파일러가 부족했습니다. 이는 개발자들이 성능을 최적화하고 리렌더링을 관리하기 위해 useMemo
와 useCallback
같은 훅을 사용하도록 강요했습니다. 최신 버전인 React 19는 새로운 React 컴파일러를 도입하여 이러한 성능 최적화 필요성을 해결합니다! 이 혁신은 수동 메모이제이션과 최적화의 필요성을 제거하여 React로 프론트엔드 개발을 간소화할 것을 약속합니다.
이 기사에서는 React 컴파일러가 무엇인지, 어떻게 작동하는지, 프론트엔드 개발에 어떤 이점을 가져오는지 살펴보겠습니다.
(사이트 https://blog.logrocket.com/exploring-react-compiler-detailed-introduction/ 현재 공부하면서 번역하고 따라한 내용입니다. 번역에 오류가 있을 수 있으니 참고하세요.)
컴파일러란 무엇인가?
일부 개발자는 프론트엔드 개발 여정에서 React만을 사용했을 수 있기 때문에 “컴파일러”라는 용어가 생소할 수 있습니다. 따라서 컴파일러에 대한 간략한 소개로 시작하는 것이 도움이 될 수 있습니다. React 컴파일러는 JavaScript 코드의 성능을 극대화하기 위해 특별히 설계되었습니다.
React 개발자들이 컴파일러에 대한 지식이 부족하다고 말하는 것이 아니라, 전통적인 프로그래밍 언어에서 사용되는 컴파일러와 웹 프레임워크에서 사용되는 컴파일러를 구분하는 것이 중요합니다. 각 컴파일러 유형의 구체적인 기능을 탐구해보겠습니다.
전통적인 컴파일러
전통적인 컴파일러는 C, Java 또는 Rust와 같은 고급 프로그래밍 언어를 컴퓨터의 하드웨어에서 실행될 수 있는 저급 기계어로 번역하도록 설계되었습니다.
컴파일러는 분석, 최적화 및 코드 생성과 같은 여러 단계를 거칩니다. 그런 다음, 라이브러리 및 다른 모듈과 기계어 코드를 링크하여 특정 플랫폼에서 실행할 수 있는 최종 실행 파일을 생성합니다.
웹 프레임워크의 컴파일러
웹 프레임워크 컴파일러는 선언적 컴포넌트 기반 코드를 최적화된 JavaScript, HTML 및 CSS로 변환하도록 설계되었습니다. 각 프레임워크 컴파일러는 컴파일 과정을 다르게 처리하지만, 일반적으로 다음과 같은 단계를 거칩니다:
- 템플릿 파싱: 컴포넌트의 템플릿 부분(HTML과 유사한 문법)을 구문 트리로 파싱
- 스크립트 파싱: 스크립트 부분(JavaScript)을 파싱하여 컴포넌트 로직을 이해
- 스타일 파싱: 스타일 부분(CSS)을 파싱하고 컴포넌트에 범위 지정
- 코드 변환: 각 프레임워크의 컴파일러는 코드 변환 단계를 다르게 처리함. 예를 들어, Vue는 템플릿을 렌더 함수로 변환하고, Svelte는 템플릿과 스크립트를 고도로 최적화된 JavaScript로 직접 컴파일
- 최적화: 정적 분석, 트리 쉐이킹, 코드 분할 등의 최적화 수행
- 코드 생성: 브라우저에서 실행될 최종 JavaScript 코드를 생성
- 출력: 출력은 일반적으로 웹 애플리케이션에 포함되어 브라우저에서 실행될 준비가 된 JavaScript 코드(HTML 및 CSS 포함)
React에서 컴파일러의 필요성
이제 React에 컴파일러가 없다면 어떻게 위에서 논의된 개념을 처리하는지 궁금할 수 있습니다. React 컴파일러의 필요성을 이해하기 위해 React의 핵심 정신 모델을 먼저 이해해야 합니다.
React는 선언적 프로그래밍 모델을 사용합니다. 이 모델은 현재 애플리케이션 상태를 기반으로 UI가 어떻게 보일지를 설명할 수 있게 합니다. DOM을 조작하고 UI를 업데이트하는 단계를 상세히 설명하는 대신(명령형 프로그래밍), 원하는 결과를 지정하면 React가 해당 결과에 맞게 DOM을 업데이트합니다.
다음 컴포넌트를 예로 들어보겠습니다:
function ReactComponent() {
return (
<div>
<h1>Hello, World!</h1>
</div>
);
}
이 코드는 초기 렌더링 시 ReactComponent가 렌더링될 때 “Hello, World!”라는 텍스트가 있는 h1 요소를 포함하는 div를 생성해야 함을 선언합니다. React 컴파일러는 이 과정을 자동화하여 성능을 최적화합니다.
이제 이 컴포넌트에 상태와 부모 컴포넌트로부터 props를 받는다고 가정해보겠습니다:
function ReactComponent(props) {
const [state, setState] = useState(null);
return (
<div>
<h1>Hello, World!</h1>
</div>
);
}
이 컴포넌트의 상태나 props가 변경될 경우, React는 컴포넌트를 다시 렌더링하는 반응적 프로세스를 거칩니다. 이를 통해 선언이 현재 상태와 항상 일치하도록 보장합니다. React 컴파일러는 이러한 리렌더링 과정을 최적화하여 불필요한 성능 저하를 방지합니다.
이 과정은 React의 조정(reconciliation)을 통해 처리되며, 새로운 상태에 맞게 UI를 업데이트하기 위해 필요한 최소한의 변경 사항을 결정합니다.
React의 리렌더링 문제
앞서 설명한 것처럼 React는 리렌더링을 위해 설계되었습니다. 그러나 이는 문제가 될 수 있습니다. React 컴파일러는 이러한 리렌더링 문제를 해결하는 데 중요한 역할을 합니다.
React는 상태가 변경될 때 컴포넌트를 리렌더링할 뿐만 아니라, 해당 컴포넌트 내부의 모든 컴포넌트와 해당 컴포넌트 내부의 컴포넌트까지도 리렌더링합니다.
다음 예제에서 버튼을 클릭하고 message
상태가 “Hello, React!”로 업데이트되면 childComponent
와 AnotherChildComponent
컴포넌트가 모두 리렌더링됩니다. 이는 AnotherChildComponent
컴포넌트가 message
props를 사용하지 않더라도 발생합니다:
import React, { useState } from 'react';
function AnotherChildComponent() {
return <p>Another child component</p>;
}
function ChildComponent({ message }) {
return <p>{message}</p>;
}
export function ParentComponent() {
const [message, setMessage] = useState('Hello, World!');
const updateMessage = () => {
setMessage('Hello, React!'); // This will cause ChildComponent and AnotherChildComponent to re-render.
};
return (
<div>
<AnotherChildComponent />
<ChildComponent message={message} />
<button onClick={updateMessage}>Update Message</button>
</div>
);
}
이것이 반드시 나쁜 것은 아니지만, 너무 자주 발생하거나 다운스트림의 컴포넌트 중 하나가 복잡한 계산을 포함하는 경우 애플리케이션의 성능에 심각한 영향을 미칠 수 있습니다. React 컴파일러는 이러한 성능 문제를 해결합니다.
다음 예제를 고려해보겠습니다:
import React, { useState } from 'react';
// A function to simulate an expensive computation
function expensiveComputation(num) {
console.log('Running expensive computation...');
let result = 0;
for (let i = 0; i < 1000000000; i++) {
result += num * Math.random();
}
return result;
}
function HeavyComponent({ number }) {
// Performing the expensive computation directly in the render
const result = expensiveComputation(number);
return (
<div>
<p>Expensive Computation Result: {result}</p>
</div>
);
}
export function ParentComponent() {
const [number, setNumber] = useState(1);
const [count, setCount] = useState(0);
const incrementCount = () => {
setCount(count + 1);
};
return (
<div>
<HeavyComponent number={number} />
<button onClick={incrementCount}>Increment Count: {count}</button>
</div>
);
}
여기서 count
상태가 변경될 때마다 HeavyComponent
가 리렌더링되고 expensiveComputation
함수가 호출되어 성능에 큰 영향을 미칩니다.
이러한 리렌더링 체인을 방지하고 렌더링을 최적화하기 위해 개발자들은 수동으로 컴
포넌트를 메모이제이션해야 했습니다. 메모이제이션은 React 16에서 처음 도입된 최적화 기술로, 값비싼 함수 호출의 결과를 캐싱하여 동일한 입력이 다시 발생할 때 결과를 재사용하고 불필요한 리렌더링을 방지합니다. React 컴파일러는 이 과정을 자동화합니다.
React는 메모이제이션을 위한 몇 가지 도구를 제공합니다: React.memo
, useMemo
, useCallback
. 이러한 훅은 일반적으로 컴포넌트와 props를 래핑하여 부모 컴포넌트와 독립적으로 작동하도록 React에 알리는 데 사용됩니다. 부모 컴포넌트가 리렌더링될 때, 래핑된 컴포넌트는 리렌더링되지 않습니다. React 컴파일러는 이러한 작업을 자동으로 수행하여 성능을 극대화합니다.
이를 바탕으로 이전 예제를 최적화해보겠습니다:
import React, { useState, useMemo } from 'react';
// A function to simulate an expensive computation
function expensiveComputation(num) {
console.log('Running expensive computation...');
let result = 0;
for (let i = 0; i < 1000000000; i++) {
result += num * Math.random();
}
return result;
}
const HeavyComponent = React.memo(({ number }) => {
// Memoize the result of the expensive computation
const result = useMemo(() => expensiveComputation(number), [number]);
return (
<div>
<p>Expensive Computation Result: {result}</p>
</div>
);
});
export function ParentComponent() {
const [number, setNumber] = useState(1);
const [count, setCount] = useState(0);
const incrementCount = () => {
setCount(count + 1);
};
return (
<div>
<HeavyComponent number={number} />
<button onClick={incrementCount}>Increment Count: {count}</button>
</div>
);
}
여기서는 useMemo
훅을 사용하여 HeavyComponent
컴포넌트에서 expensiveComputation
함수의 결과를 메모이제이션하므로, number
prop이 변경될 때만 계산이 다시 수행됩니다. 또한, React.memo
를 사용하여 HeavyComponent
를 래핑하여 속성이 변경되지 않으면 리렌더링되지 않도록 합니다. React 컴파일러는 이러한 최적화 작업을 자동화하여 개발자가 코드에 더 집중할 수 있게 합니다.
이러한 메모이제이션 훅은 강력하고 잘 작동하지만, 올바르게 사용하는 것은 어렵습니다. 언제 어떻게 사용해야 할지 아는 것은 어려워 개발자들이 성능을 향상시키기 위해 많은 함수와 컴포넌트를 useCallback
과 useMemo
로 래핑하는 코드로 가득 차게 됩니다. React 컴파일러는 이러한 과정을 자동화하여 개발자의 부담을 덜어줍니다.
React 컴파일러란 무엇인가?
React 컴파일러는 원래 React Forget으로 알려졌으며 2021년 React Conf에서 처음 소개되었습니다. 이는 애플리케이션의 코드를 자동으로 가져와서 컴포넌트, 속성 및 훅 종속성을 자동으로 최적화된 코드로 변환하는 저수준 컴파일러입니다. React 컴파일러는 메모이제이션과 유사한 작업을 수행하여 리렌더링 비용을 최소화하는 역할을 합니다.
컴파일러는 처음 도입된 이후로 진화해왔습니다. 최근 아키텍처는 컴포넌트의 메모이제이션뿐만 아니라 로컬 변형과 참조 의미론과 같은 고급 코드 패턴을 최적화하는 복잡한 검사도 수행합니다. Meta의 앱(예: Instagram)에서는 이미 이 컴파일러를 사용해 왔습니다.
React 컴파일러의 작동 방식
컴파일 과정에서 React 컴파일러는 코드를 리팩터링하고 _c
라는 훅(이전에는 useMemoCache
로 알려짐)을 사용하여 캐시 가능한 요소의 배열을 만듭니다. React 컴파일러는 이러한 캐시를 통해 불필요한 리렌더링을 방지합니다.
이것은 각 컴포넌트의 일부를 배열의 슬롯에 저장하고, 다음에 컴포넌트가 호출될 때 배열의 요소 중 하나가 변경되었는지 확인하는 if 문으로 구성된 메모이제이션 블록을 생성합니다. 변경 사항이 없으면 캐시된(원래) 요소를 반환합니다. React 컴파일러는 이러한 과정을 자동화하여 성능을 극대화합니다.
예를 들어, 다음과 같은 간단한 컴포넌트가 있다고 가정해보겠습니다:
function SimpleComponent({ message }) {
return (
<div>
<p>{message}</p>
</div>
);
}
컴파일된 출력은 다음과 같습니다:
function SimpleComponent(t0) {
const $ = _c(2);
const { message } = t0;
let t1;
if ($[0] !== message) {
t1 = (
<div>
<p>{message}</p>
</div>
);
$[0] = message;
$[1] = t1;
} else {
t1 = $[1];
}
return t1;
}
이 코드에서 무슨 일이 일어나고 있는지 살펴보겠습니다. 먼저 컴파일러는 _c
훅을 사용하여 컴포넌트 상태를 캐시하기 위해 두 개의 슬롯이 있는 배열을 초기화합니다. 그런 다음 t0
prop 객체에서 message
prop을 구조 분해하여 암시적 t1
변수를 만들어 JSX 요소를 저장합니다:
const $ = _c(2);
const { message } = t0; // Extract message prop from props object (t0)
let t1; // Variable to hold the JSX element
그 후, 컴파일러는 message
prop이 변경되었는지 확인하기 위해 메모이제이션 블록을 생성하여, 변경된 경우 새로운 JSX를 만들고 이를 t1
변수에 할당하며 배열을 새로운 message
와 JSX 요소로 업데이트합니다. message
가 변경되지 않은 경우 캐시된 JSX 요소를 사용합니다:
if ($[0] !== message) { // Checks if `message` prop has changed
t1 = ( // Creates JSX element if `message` changed
<div>
<p>{message}</p>
</div>
);
$[0] = message; // Update cache with new message
$[1] = t1; // Update cache with new JSX element
} else {
t1 = $[1]; // Reuse JSX element from cache if message hasn't changed
}
React 컴파일러를 사용해 보고 싶다면, React 컴파일러 플레이그라운드를 사용하거나 프로젝트에 통합할 수 있습니다. 다음 섹션에서 이를 수행하는 방법을 다루겠습니다. 또한 React 컴파일러에 대한 더 깊은 이해와 복잡한 개요를 원한다면, React Conf에서 Sathya Gunasekaran의 “React Compiler deep dive” 강연을 시청해보세요.
최적화를 위한 코드 준수
코드를 효율적으로 컴파일하고 성능을 최적화하려면 React 컴파일러가 코드를 이해해야 합니다. 이를 위해 JavaScript와 React의 규칙에 대한 지식을 사용합니다. 이러한 규칙은 예측 가능하고 효율적인 React 애플리케이션을 작성하고 버그를 줄이기 위해 개발자를 돕기 위한 미세한 지침입니다.
React의 규칙 중 일부는 다음과 같습니다:
- 훅은 최상위 레벨에서만 호출되어야 함: 훅은 함수형 컴포넌트나 커스텀 훅의 최상위 레벨에서만 호출되어야 합니다. 루프, 조건문 또는 중첩된 함수 내에서 호출해서는 안 됩니다. 이는 컴포넌트가 렌더링될 때 훅이 항상 동일한 순서로 호출되도록 보장합니다.
- React 코드에서만 훅을 호출: 훅은 함수형 컴포넌트나 커스텀 훅에서만 호출되어야 합니다. 일반 JavaScript 함수, 클래스 컴포넌트 또는 비-React 코드에서는 사용하지 말아야 합니다.
- 부작용은 렌더링 단계 외부에서 실행되어야 함: 데이터 가져오기, 구독, 수동 DOM 조작과 같은 부작용은 직접 렌더링 단계에서 발생해서는 안 됩니다. 대신,
useEffect
훅 또는 유사한 훅을 사용하여 관리해야 합니다. - Props와 상태는 불변: Props와 상태는 불변으로 취급되어야 합니다. 이를 직접 수정하는 대신, 상태 설정자(e.g.,
setState
또는useState
와 같은 훅이 제공하는 함수)를 사용해야 합니다.
컴파일 과정에서 컴파일러가 이러한 규칙이
위반되고 있음을 감지하면, 해당 구성 요소나 훅을 건너뛰고 다른 코드로 안전하게 넘어갑니다. 마찬가지로 코드가 이미 잘 최적화되어 있는 경우, React 컴파일러를 켜도 큰 개선이 없을 수 있습니다.
또한, 컴파일러가 메모이제이션된 구성 요소에서 최적화를 유지할 수 없다고 판단되면, 해당 구성 요소를 건너뛰고 수동 메모이제이션이 대신 작업을 수행하도록 합니다. React 컴파일러는 이러한 유연성을 통해 최적의 성능을 보장합니다.
다음 코드를 예로 들어보겠습니다:
import React, { useState, useMemo } from 'react';
function ItemList({ items }) {
const [filter, setFilter] = useState('');
const [sortOrder, setSortOrder] = useState('asc');
const filteredAndSortedItems = useMemo(() => {
const filteredItems = items.filter(item => item.includes(filter));
const sortedItems = filteredItems.sort((a, b) => {
if (sortOrder === 'asc') return a.localeCompare(b);
return b.localeCompare(a);
});
return sortedItems;
}, [filter, sortOrder, items]);
return (
<div>
<input
type="text"
value={filter}
onChange={e => setFilter(e.target.value)}
placeholder="Filter items"
/>
<select value={sortOrder} onChange={e => setSortOrder(e.target.value)}>
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</select>
<ul>
{filteredAndSortedItems.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
export default ItemList;
React 컴파일러는 메모이제이션된 값이 컴포넌트나 애플리케이션의 다른 곳에서 변형될 수 있으므로, 메모이제이션이 무효화되어 버그와 성능 문제를 초래할 수 있기 때문에 이 코드를 건너뜁니다.
React 컴파일러 플레이그라운드에서 코드를 시도하면 다음과 같은 오류가 발생합니다:
React 컴파일러 시도하기
React 컴파일러는 여전히 실험 단계에 있으며, 프로덕션 환경에서는 사용하지 않는 것이 좋습니다. 그러나 소규모 프로젝트에서 시도하거나 기존 프로젝트에 통합하려면 이 섹션에서 React 컴파일러를 시작하는 방법을 안내합니다.
컴파일러를 설치하기 전에 몇 가지 전제 조건을 해결해야 할 수 있습니다. 여기에는 프로젝트의 컴파일러 호환성 확인 및 ESLint 플러그인 설치가 포함됩니다.
React 컴파일러와의 호환성 확인
코드베이스가 컴파일러와 호환되는지 확인하려면 프로젝트 디렉터리에서 다음 명령을 실행하세요:
npx react-compiler-healthcheck@latest
이 명령은 몇 개의 컴포넌트를 최적화할 수 있는지, strict mode가 활성화되어 있는지, 컴파일러와 호환되지 않을 수 있는 라이브러리를 설치했는지 등을 확인합니다. React 컴파일러의 호환성을 확인하는 첫 단계입니다.
React 컴파일러용 ESLint 플러그인 설치
React 컴파일러에는 React 규칙을 준수하고 문제를 잡아주는 ESLint 플러그인이 포함되어 있습니다. 이 플러그인은 컴파일러와 독립적이므로, 컴파일러 없이도 사용할 수 있습니다. 따라서 사용하려면 설치해야 합니다:
npm install eslint-plugin-react-compiler
그런 다음, ESLint 구성에 추가합니다:
module.exports = {
plugins: [
'eslint-plugin-react-compiler',
],
rules: {
'react-compiler/react-compiler': "error",
},
}
기존 프로젝트에서 React 컴파일러 사용하기
현재 작성 시점에서는 React 컴파일러가 React 19와만 호환됩니다. 컴파일러를 사용하려면 프로젝트를 React와 React DOM의 최신 테스트 버전으로 업그레이드해야 합니다. 이를 위해 프로젝트 디렉터리에서 다음 명령을 실행하세요:
npm install --save-exact react@rc react-dom@rc
터미널 창에서 다음과 같은 내용을 볼 수 있습니다:
다음으로, 빌드 과정에서 컴파일러가 실행되도록 Babel 플러그인 babel-plugin-react-compiler
를 설치합니다:
npm install babel-plugin-react-compiler
설치 후, Babel 구성 파일의 플러그인 배열에 다음을 추가합니다:
// babel.config.js
const ReactCompilerConfig = { /* ... */ };
module.exports = function () {
return {
plugins: [
['babel-plugin-react-compiler', ReactCompilerConfig], // must run first!
// ...
],
};
};
Vite를 사용하는 경우, vite.config
파일에 플러그인을 추가합니다:
// vite.config.js
const ReactCompilerConfig = { /* ... */ };
export default defineConfig(() => {
return {
plugins: [
react({
babel: {
plugins: [
["babel-plugin-react-compiler", ReactCompilerConfig],
],
},
}),
],
// ...
};
});
컴파일러가 빌드 파이프라인에서 첫 번째로 실행되도록 합니다. 즉, 다른 플러그인이 있는 경우, 컴파일러 뒤에 나열해야 합니다. 또한, 오류를 피하기 위해 구성 파일의 최상위 레벨에 ReactCompilerConfig 객체를 추가합니다.
이제 개발 서버를 시작하고 브라우저의 React Developer Tools를 열면 컴파일러에 의해 최적화된 컴포넌트 옆에 Memo 배지가 표시되어야 합니다:
축하합니다! 프로젝트에 React 컴파일러를 성공적으로 통합했습니다.
새로운 프로젝트에서 React 컴파일러 사용하기
새 프로젝트에서 React 컴파일러를 시작하는 가장 좋은 방법은 모든 것이 설정된 Next.js의 카나리아 버전을 설치하는 것입니다. 프로젝트를 시작하려면 다음 명령을 사용하세요:
npm install next@canary babel-plugin-react-compiler
그런 다음, next.config.js
파일에서 experimental 옵션을 사용하여 컴파일러를 켭니다:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
reactCompiler: true,
},
};
module.exports = nextConfig;
experimental 옵션은 다음 환경에서 React 컴파일러가 지원되도록 보장합니다:
- 앱 라우터
- 페이지 라우터
- Webpack
React 컴파일러의 범위 제한 사용
앞서 논의했듯이, React 컴파일러는 여전히 실험적이며 프로덕션 환경에서 사용하지 않는 것이 좋습니다. 이는 JavaScript의 유연한 특성으로 인해 컴파일러가 규칙 위반을 모두 잡아내기 어렵고, 허위 음성으로 컴파일될 수 있기 때문입니다.
이런 이유로, 애플리케이션의 전체 프로젝트가 아니라 특정 부분에만 React 컴파일러의 범위를 제한하고 싶을 수 있습니다. 이렇게 하면 컴파일러를 점진적으로 도입하고 전체 코드베이스에 영향을 미치지 않고 그 이점을 실험할 수 있습니다.
이를 달성하는 두 가지 주요 방법이 있습니다. 두 가지를 간단히 살펴보겠습니다.
특정 디렉터리에서 React 컴파일러 사용
빌드 설정을 구성하여 특정 디렉터리의 파일에만 React 컴파일러를 사용할 수 있습니다. 이를 위해, Babel 또는 Vite 구성 파일에서 ReactCompilerConfig 객체에 다음 코드를 추가합니다:
const ReactCompilerConfig = {
sources: (filename) => {
return filename.indexOf('src/path/to/dir') !== -1;
},
};
그런 다음 ‘src/path/to/dir’을 컴파일러가 작동할 폴더 경로로 바꿉니다.
지시문 선택 옵션 사용
또는 파일 상단에 특수 지시문 주석을 사용하여 파일 단위로 컴파일러를 선택할 수 있습니다. 이 방법은 전체 설정을 변경하지 않고 개별 파일에 대해 컴파일러를 활성화할 수 있습니다. 이를 위해, ReactCompilerConfig 객체에 다음을 추가합니다:
const ReactCompilerConfig = {
compilationMode: "annotation",
};
그런 다음, 개별적으로 최적화할 컴포넌트에 “use memo” 지시문을 추가합니다:
// src/app.jsx
export default function App() {
"use memo";
// ...
}
결론
React 컴파일러는 현재 상태로는 다른 프레임워크의 기능과 비교할 때 많은 것을 제공하지 않을 수 있습니다. 그리고 불행히도, 많은 개발자가 간절히 바라는 useEffect
같은 훅의 종속 배열을 제거할 가능성은 현재로서는 없습니다.
그럼에도 불구하고, 컴파일러는 가까운 미래에 가능한 것들에 대한 통찰을 제공합니다. 예를 들어, 훅의 종속 배열을
없애는 잠재력이 있습니다. 이는 React 컴포넌트의 상태 관리와 부작용을 단순화하고 보일러플레이트 코드를 줄이며 잘못된 종속성과 관련된 버그의 위험을 최소화할 수 있습니다.
그동안 React 컴파일러를 실험하고 피드백을 제공하면 그 발전에 도움이 될 것입니다.!