5분만에 해치우는 react query - 꼭 필요한 사전 지식

dante Yoon·2022년 8월 14일
31

react query

목록 보기
1/2
post-thumbnail
post-custom-banner

리엑트 쿼리를 사용하다보면, 다른 상태 관리 도구에서 보지 못하던 stale, cache와 같은 단어들이 보인다.

또한 내가 명시적으로 api 호출을 하지 않았는데 반복되어 ajax 호출이 되는 것을 볼 수 있다.

staleTime과 cacheTime은 또 무엇이 다른 것일까?

이렇게 리엑트 쿼리를 사용하기 전 꼭 선행하여 알고 있어야 하는 점에 대해 단 5분 동안 모두 알아보자.

Stale data는 캐시가 만료된 데이터를 의미한다.

stale time은 리엑트 쿼리가 이전에 호출했던 server state가 더 이상 최근 데이터가 아니며 (not up-to-date) 이 상태에 있는 데이터를 사용하기 원하는 경우 리엑트 쿼리는 다시 server state를 불러온다.

useQuery, useInfiniteQuery로 조회하는 데이터는 모두 특정 시간이 지난 이후에는 만료된 데이터, stale data로 간주도미ㅕ 이걸 바꾸고 싶다면 staleTime 옵션을 지정한다. 옵션 값에 지정된 시간이 지나기 전까지는 데이터를 다시 호출하지 않는다.

그렇다면 stale data는 왜 사용되는 걸까?
리엑트 쿼리는 아무런 데이터도 ui에 표현하지 않는 것보단 최신 server state를 받아올 때까지 stale data를 view에 표현해주는 전략을 취한다.

사용자는 FCP (first contentful paint)이후 유의미한 정보로 보여지는 server state를 업데이트 되기 전까지 볼 수 있으므로, 더 좋은 UX를 제공받는다.

리엑트 쿼리는 다음 상황에서 기존 데이터를 stale되었다고 판단하고 다시 업데이트 한다.(이하 revalidate)

  • 새로운 쿼리 인스턴스
  • 윈도우가 다시 포커스 되었을 때 (탭전환등)
  • 네트워크가 끊겼다가 다시 연결되었을 때
  • refetch interval 설정에 따라 다시 데이터를 호출할 때

이 중 윈도우 포커싱 시 데이터 패칭이 되는 기능은 refetchOnWindowFocus 옵션을 사용해서 끌 수 있다. 이 외에도 refetch 기능들은 아래의 옵션들로 조절할 수 있다.

refetchOnMount
refetchOnWindowFocus
refetchOnReconnect
refetchInterval

useQuery를 사용하는 모든 컴포넌트에서 data가 refetch 되는데..?

function ComponentOne() {
  const { data } = useTodos()

  if (data) {
    // ⚠️ mounts conditionally, only after we already have data
    return <ComponentTwo />
  }
  return <Loading />
}

function ComponentTwo() {
  // ⚠️ will thus trigger a second network request
  const { data } = useTodos()
}

const queryClient = new QueryClient()

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <ComponentOne />
    </QueryClientProvider>
  )
}

코드 출처: https://tkdodo.eu/blog/react-query-as-a-state-manager#letting-react-query-do-its-magic

앱이 stale 데이터를 가지고 있음으로 해당 데이터를 참조하는 모든 컴포넌트에서 이 데이터를 다시 사용하는 것은 너무 멋진 기능이다. 하지만 위의 예시에서 ComponentOne에서 data fetching이 완료된 이후 ComponentTwo를 렌더링되었을 때, 다시 한번 동일한 ajax가 call되는 것을 네트워크 탭을 통해 확인할 수 있다.

stale 데이터는 기본적으로 0초로 설정되어있기 때문에 list가 참조되어질 때마다 항상 백그라운드에서 refetch가 일어난다.

불필요한 ajax 호출을 방지하기 위해

  • child component의 props로 data를 넘기거나
  • context api를 사용하거나
  • refetchOnMount | refetchOnWindowFocus 옵션을 끌 수 있다.

하지만 위 방법은 stale된 데이터를 다시 revalidate 하는데는 도움을 주지 못한다.

그러면 어떻게 해야할까?

staleTime을 조절하기

staleTime을 변경하여 리엑트 쿼리애개 캐시된 데이터를 얼마나 자주 최신화 시켜줘야 하는지 알려줄 수 있다.

stale time에 대한 정확한 모범답안은 없다. tkdodo 블로그에 따르면 (react query maintainer) 최소 20초 정도면 중복 호출 문제를 해결할 수 있다고 말한다.

특정 쿼리 키 값에만 staleTime을 변경하고 싶다면,

10개의 쿼리 중 9개에는 20초, 나머지 한 개에는 5초의 staleTime을 지정하고 싶으면 어떻게 하는게 좋은 방법일까?

queryClient 인스턴스를 생성할 때 기본적으로 staleTime을 쿼리 인스턴스에 전역 설정해줄 수 있지만
v3 부터 지원하는 QueryClient.setQueryDefaults를 사용한다면, 특정 키 값에 대한 독립적인 staleTime을 쉽게 조절할 수 있다.

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      // ✅ globally default to 20 seconds
      staleTime: 1000 * 20,
    },
  },
})

queryClient.setQueryDefaults(todoKeys.all, { staleTime: 1000 * 5 })

cache된 데이터는 어떻게 처리될까?

useQuery, useInfiniteQuery로 만들어져 사용된 인스턴스들은 다른 컴포넌트에서 사용될 때를 대비해 캐시되며, 5분 후에 Garbage Collection (이하 GC)에 의해 사라진다.
이를 변경하기 위해서는 1000 60 5 말고 다른 옵션 값을 cacheTime에 전달하면 된다.

실패되는 쿼리들은 exponential한 딜레이 간격으로 3번 다시 호출되며, 이후에는 에러로 처리된다.

cacheTime, staleTime

staleTime은 data revalidating이 필요한 시점까지 얼마의 기간이 남았는지에 대해 설정하는 옵션이며
cacheTime은 캐시된 데이터가 GC에 의해 메모리 상에서 지워지기 전까지 얼마나 긴 시간동안 들고 있어야 하는지를 말하는 것이다.

profile
성장을 향한 작은 몸부림의 흔적들
post-custom-banner

1개의 댓글

comment-user-thumbnail
2024년 5월 20일

굿-!!

답글 달기