Post

Tanstack Query 의 동작 원리

이전 포스팅에서 Tanstack Query 를 사용하는 간단한 예시를 살펴보았다. useQuery() 를 통해 Read, useMutation() 을 이용해 Create 동작을 구현해보았다. 이후, invalidateQueries() 로 캐시 데이터에 변화가 생기면 이를 반영하도록 만들었다. 이것이 어떤 원리로 동작하는 걸까?

SWR 이란?

Stale-While-Revalidate(SWR)은 최신 데이터가 도착하기 전까지 기존 캐시 데이터를 사용하는 전략이다.

*Stale: 오래된, 냄새가 나는

SWR은 stale time 이내의 짧은 시간에 동일한 요청이 반복될 시, 기존의 캐시 데이터를 사용한다. 그 보다 간격이 길다면 기존 캐시 데이터를 사용하다가 새로운 데이터가 도착하면 캐시 데이터에 새로 반영하는 방법이 되겠다.

캐시 데이터 저장소

그렇다면, 캐시 데이터를 전역적으로 사용하게 해주는 원리는 무엇일까?

Tanstack-Query 는 React Context API를 사용해 구현이 되어있다. 그래서, 전역적으로 캐시 데이터를 사용할 수 있는 것이다!

tanstack-query SWR 원리

v4 버전 기준이지만, 좋은 자료인 것 같아 가져왔다.

Stale Time 이란??

캐시 데이터가 최신 상태(fresh)인지, 이전 상태(stale)인지 구분하는 경과 시간이 되겠다.

만약, staleTime 이 0 초라면 데이터를 fetch 한 직후 stale 로 바뀔 것이다.

하지만 stale 이라고 해서 데이터를 지우지 않고 가지고 있는다. 이후 refetch 를 통해 업데이트하는 것이다.

이때, 현재 페이지에서 사용하지 않는 경우(컴포넌트 unmount 등)에는 inactive 상태로 존재한다.

계속해서 사용하지 않는 경우, 정해진 cacheTime(gcTime)까지 지난 경우 삭제하게 된다.

Tanstack Query 의 생명주기

상태설명
fresh데이터를 새로 패칭할 필요가 없는 상태. staleTime 이 지나지 않아 그대로 사용 가능
stalestaleTime 이 지나 새로운 데이터를 가져와야하는 상태
active현재 컴포넌트에서 사용하고 있는 쿼리 상태
inactive더 이상 사용하지 않는 쿼리 상태, 컴포넌트 unmount 시 해당
deleted캐시에서 제거한 쿼리 상태, gcTime 이 지나면 이에 해당
fetching데이터를 서버에서 가져오고 있는 상태, isFetching === true 로 설정됨

Tanstack Query 에서 기본 설정 값

기본 설정의미
staleTime: 0useQuery 또는 useInfiniteQuery 에 등록한 queryFn 을 통해 fetch 한 데이터는 즉시 stale
refetchOnMount: true컴포넌트 mount 시 stale data refetch
refetchOnWindowsFocus: true실행중인 브라우저 화면을 focus 할 때 마다 stale data refetch
refetchOnReconnect: trueNetwork 가 끊겼다가 재연결되었을 때 stale data refetch
gcTime(cacheTime): 5min컴포넌트 unmount 시 쿼리 상태가 있다면 inactive query가 됨. 이후 5min 이상 상태가 유지되면 가비지 컬렉터가 삭제 처리
retry: 3queryFn 이 데이터를 정상적으로 가져오지 못할 경우 최대 3번까지 시도

옵션 설정 방법

만약, 공통적으로 모든 query 에 대해 설정을 적용하려면 new 연산자를 사용해 생성한 곳에서 이렇게 설정해주면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      // staleTime 5초, 5000ms
      staleTime: 5000,
      // 컴포넌트 Mount 시 refetch
      refetchOnMount: true,
      // 네트워크 연결이 될 시 refetch
      refetchOnReconnect: true,
      // 탭이 비활성화되었다가 활성화 될 시 refetch
      refetchOnWindowFocus: true
      // API 호출 시도 횟수
      retry: 2
    }
  }
});

그렇지 않고, query 요청을 보내는 컴포넌트 마다 다르게 설정하고 싶다면 물론 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const {
  data: todos,
  isLoading,
  isError
} = useQuery({
  // queryKey 를 한 번 가져오고 난 뒤 변경이 생기기 전까지는 캐싱한다는 말??
  queryKey: ["todos"],
  // queryFn: () => {},
  queryFn: fetchTodos,
  // staleTime 5초, 5000ms
  staleTime: 5000,
  // 컴포넌트 Mount 시 refetch
  refetchOnMount: true,
  // 네트워크 연결이 될 시 refetch
  refetchOnReconnect: true,
  // 탭이 비활성화되었다가 활성화 될 시 refetch
  refetchOnWindowFocus: true
  // API 호출 시도 횟수
  retry: 2
});

useQuery() 에서 데이터를 가져올 때 설정해주면 된다.

Tanstack Query Devtools

Tanstack Query 는 개발자도구를 지원한다!

1
yarn add @tanstack/react-query-devtools

이후, queryClinet 를 작성한 파일에서 아래와 같이 추가해주면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import "./index.css";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
// ✨ 추가한 부분
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";

const queryClient = new QueryClient();

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      {/* ✨ 추가한 부분 */}
      <ReactQueryDevtools initialIsOpen={false} />
      <App />
    </QueryClientProvider>
  </React.StrictMode>
);
This post is licensed under CC BY 4.0 by the author.