반응형

리팩토링은 어려워


얼마 전 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 규칙


  1. 최상위에서만 호출
    1. 컴포넌트 혹은 커스텀 훅의 최상위 스코프에서만 훅을 호출
    2. 조건문, 반복문, 중첩된 함수 안에서 훅을 호출하면 안됨
  2. 렌더링마다 동일한 순서로 호출
    1. 컴포넌트가 매번 렌더링될 때 훅이 호출되는 순서와 개수가 변하면 안됨
    2. 호출 순서가 바뀌면 이전 렌더에서 저장해둔 “훅의 큐” 를 찾아올 수 없어 오류가 발생

왜 동일한 순서여야 할까?


리액트의 훅 동작 순서를 파악해보자.

  • 컴포넌트가 렌더링 될 때 마다, 리액트는 훅 호출 순서를 따라 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 개발자 —

반응형

+ Recent posts