React Compiler는 React의 규칙들을 따르는 코드를 처리하도록 설계되었어요. 이 규칙을 위반할 수 있는 코드를 만나면, 앱의 동작을 바꿀 위험을 감수하는 대신 안전하게 최적화를 건너뛰어요.
(즉, 컴파일러는 "확실하지 않으면 건드리지 않는다"는 보수적인 접근 방식을 취해요. 이게 안전하긴 하지만, 때때로 예상치 못한 동작으로 이어질 수 있어요.)
컴파일러 에러는 빌드 타임에 발생하고 코드가 컴파일되는 것을 막아요. 컴파일러가 문제가 있는 코드를 실패하는 대신 건너뛰도록 설계되어 있기 때문에 이런 에러는 드물어요.
런타임 이슈는 컴파일된 코드가 예상과 다르게 동작할 때 발생해요. 대부분의 경우, React Compiler와 관련된 문제를 만나면 그건 런타임 이슈예요. 이건 보통 여러분의 코드가 컴파일러가 감지하지 못한 미묘한 방식으로 React의 규칙을 위반하고, 컴파일러가 건너뛰었어야 할 컴포넌트를 실수로 컴파일했을 때 발생해요.
런타임 이슈를 디버깅할 때는, ESLint 규칙이 감지하지 못한 React 규칙 위반을 영향을 받은 컴포넌트에서 찾는 데 노력을 집중하세요. 컴파일러는 여러분의 코드가 이 규칙들을 따른다고 가정하고, 감지할 수 없는 방식으로 규칙이 깨졌을 때 런타임 문제가 발생하는 거예요.
React Compiler가 앱을 깨뜨릴 수 있는 주요 방법 중 하나는 여러분의 코드가 정확성을 위해 메모이제이션에 의존하도록 작성된 경우예요. 이건 앱이 제대로 작동하기 위해 특정 값이 메모이제이션되어야 한다는 뜻이에요. 컴파일러가 여러분의 수동 접근 방식과 다르게 메모이제이션할 수 있기 때문에, effect가 과도하게 실행되거나, 무한 루프가 발생하거나, 업데이트가 누락되는 것 같은 예상치 못한 동작으로 이어질 수 있어요.
(쉽게 말해서, "이 값은 반드시 같은 참조를 유지해야 해"라고 가정하고 코드를 작성했는데, 컴파일러가 다르게 최적화하면 문제가 생기는 거예요.)
이런 상황이 발생하는 일반적인 시나리오들:
(예를 들어, useEffect의 의존성 배열에 객체를 넣었는데, 그 객체가 매 렌더링마다 새로 생성된다면 effect가 계속 실행될 거예요. 수동으로 useMemo를 써서 이걸 방지했는데, 컴파일러가 다르게 최적화하면 문제가 생길 수 있어요.)
문제가 발생하면 다음 단계를 따르세요:
빌드를 예상치 못하게 깨뜨리는 컴파일러 에러를 만나면, 이건 컴파일러의 버그일 가능성이 높아요. 다음 정보와 함께 facebook/react 저장소에 보고해주세요:
런타임 동작 이슈의 경우:
이슈가 컴파일러와 관련된 것인지 분리하기 위해 "use no memo"를 사용하세요:
function ProblematicComponent() {
"use no memo"; // 이 컴포넌트의 컴파일을 건너뛰어요
// ... 컴포넌트의 나머지 부분
}
이슈가 사라지면, React 규칙 위반과 관련된 것일 가능성이 높아요.
문제가 있는 컴포넌트에서 수동 메모이제이션(useMemo, useCallback, memo)을 제거해서 어떤 메모이제이션도 없이 앱이 올바르게 작동하는지 확인해볼 수도 있어요. 모든 메모이제이션을 제거했는데도 버그가 여전히 발생하면, 수정해야 할 React 규칙 위반이 있는 거예요.
(이 테스트가 중요한 이유는, 만약 메모이제이션을 모두 제거해도 버그가 있다면 그건 컴파일러 문제가 아니라 원래 코드에 있는 문제라는 뜻이기 때문이에요.)
"use no memo"를 제거하세요(✨ 배지는 해당 컴포넌트가 React Compiler에 의해 성공적으로 최적화되었다는 표시예요. 이 배지가 보이면 컴파일러가 제대로 작동하고 있는 거예요.)
컴파일러 버그를 발견했다고 생각되면:
(최소한의 재현 예제를 만드는 건 정말 중요해요. 전체 앱 코드를 올리는 것보다 문제가 되는 부분만 분리해서 보여주면 React 팀이 훨씬 빠르게 문제를 파악하고 해결할 수 있어요.)