리팩토링은 어려워
얼마 전 react-query 로 데이터 호출 프로세스를 변경했다. 이 때만해도 데이터가 술술 잘 불러와지더라니 낌새를 챘어야 했다. 컴포넌트를 하나 둘 추가하다 보니 Mypage 로 이동할 때마다 에러를 마주치게 되었다.
Should have a queue ~~
아니 이건 또 처음 보는 에런데,, 당황의 연속,,
구글링을 열심히 해보니 React Hook 의 규칙 중 하나인 “Hook 은 항상 동일한 순서로 호출되어야 한다” 는 규칙을 위반했을 때 발생한다고 한다. 그런데 이전까지는 잘되다가 갑자기 안되는 이유는 무엇?
범인은 바로 너
지피티가 알려준 나의 코드의 문제 부분을 함께 찾아보게 되었다. 예전의 나는 응애였기에 몰라 하란대로 한 부분이었는데 지금 와서 보니 너무 얼척이 없는 문제를 일으키고 있었다.. 공부의 중요성 ,,,,,,,
*// 문제가 있는 코드*
const purchaseUserIds = referralPurchases
.map(*purchase* => purchase?.user_id)
.filter(*id* => !!id);
*// 반복문 안에서 Hook을 호출하는 잘못된 패턴*
const userInfoQueries = purchaseUserIds.map(*userId* => useUserByUserId(userId));
문제 원인
useQuery 로 불러온 데이터를 순회하면서 또 useQuery 써버리기
정확히 말하면 반복문 내에서 Hook 을 호출하면 렌더링마다 Hook의 호출 순서가 달라질 수 있기 때문에 동적 Hook 호출이 되고 이는 리액트 규칙 위반이다.
React Hook 규칙
- 최상위에서만 호출
- 컴포넌트 혹은 커스텀 훅의 최상위 스코프에서만 훅을 호출
- 조건문, 반복문, 중첩된 함수 안에서 훅을 호출하면 안됨
- 렌더링마다 동일한 순서로 호출
- 컴포넌트가 매번 렌더링될 때 훅이 호출되는 순서와 개수가 변하면 안됨
- 호출 순서가 바뀌면 이전 렌더에서 저장해둔 “훅의 큐” 를 찾아올 수 없어 오류가 발생
왜 동일한 순서여야 할까?
리액트의 훅 동작 순서를 파악해보자.
- 컴포넌트가 렌더링 될 때 마다, 리액트는 훅 호출 순서를 따라
hook1
,hook2
, … 순으로 큐를 꺼내 상태를 연결 - 만약 어떤 렌더에서
hook2
가 호출되지 않거나,hook4
가 먼저 호출 되면 리액트는 이전에 저장된 큐가 없다고 판단하며에러
를 던짐
리액트가 훅 순서를 기억하는 이유 → 복작한 트리 구조를 따로 추적하지 않아도됨 → 단일 연결 리스트 형태로 훅 상태를 관리
해결 방법 (useQueries)
useQueries
는 훅 순서를 고정해 동적으로 쿼리를 생성할 수 있게 도와준다. 이 훅이 내가 원하는 기능을 구현하기에 안성맞춤이다고 느끼게 되었다.
import { useQueries } from '@tanstack/react-query';
*// 해결된 코드*
const userInfoQueries = useQueries({
queries: purchaseUserIds.map(userId => ({
queryKey: ['user', userId],
queryFn: () => useUserByUserId(userId),
enabled: Boolean(userId),
})),
});
오늘의 교훈
“Hook은 마법 같은 편의 기능이지만, 그 뒤에 숨은 내부 메커니즘을 이해해야 비로소 제대로 쓸 수 있다.”
— 익명의 React 개발자 —
'Error' 카테고리의 다른 글
[Blockchain] NFT metadata가 잘못올라가다. (0) | 2025.06.08 |
---|---|
[Blockchain] RPC url 을 구매하다. (0) | 2025.06.01 |
[Blockchain] RPC server down (0) | 2025.05.18 |
[WebView] How to Open in Chrome Browser (0) | 2025.04.27 |
vue3-kakao-map 오픈소스 기여해보기 [실패] (1) | 2024.11.25 |