FoxRain Logo
FoxRain
devlog

Tanstack Query stale, cache time의 차이

Tanstack Query stale, cache time의 차이 image

1인 가구를 타겟으로한 SNS 프로젝트를 개발하면서 tanstack query v4를 사용해 비동기 상태 관리를 하고있다. tanstack query의 장점 중 하나는 Data Caching을 통한 불필요한 네트워크 요청 방지라고 생각한다. 이는 유니크한 키 값으로 API 반환 값을 저장하여 동일한 네트워크 요청 발생 시, 저장된 값을 재사용하는 것이다.

하지만, 프로젝트 개발 진행 중 동일한 queryFn을 가진 useQuery hook을 사용해도 페이지가 변경될 때마다 API 재요청이 일어나는 문제가 발생했다. 이를 해결하기 위해 사용했던 tanstack query의 stale time의 개념과 헷갈렸던 cache time의 개념을 정리해보고자 한다.

useQuery의 API 재요청 문제

SNS 프로젝트의 api 중 /auth-user API는 사용자의 이름, 사용자에게 온 알람, 작성한 게시글의 ID 등 많은 컴포넌트에서 사용되는 사용자 데이터를 반환하는 데이터이다. 그렇기 때문에 해당 API를 사용하는 useQuery hook을 custom hook으로 개발해 사용할 필요가 있었다.

const getUser = async () => {
  const { data } = await snsApiClient.get('/auth-user');

  // ...

  return data;
};

const useUser = () => {
  const queryClient = useQueryClient();

  const { data: user } = useQuery<User>({
    queryKey: userKeys.user,
    queryFn: getUser,
    suspense: true
  });

  if (user !== null) {
    assert(user);
  }

  const initialUser = (user: User, token: string) => {
    // ...
  };

  const clearUser = () => {
    // ...
  };

  return { user, clearUser, initialUser };
};

export default useUser;

하지만 아래와 같이 useUser hook을 사용하는 페이지로 이동할 때마다 auth-user API를 재요청하는 문제가 발생했다.

문제 원인과 해결

tanstack query는 stale time default 값이 0이라고 한다. 즉 api 요청 후 반환 값은 다시 fetching 받아와야할 데이터(stale한 데이터)라고 간주하는데 여러 페이지에서 사용되는 useUser는 동시에 api를 요청하는 것이 아닌 페이지가 변경될 때마다 재요청하기 때문에 발생하는 문제였다.

따라서 auth-user의 반환 값이 stale한 데이터로 상태가 변경되지 않도록 설정하면 이 문제를 해결할 수 있다. stale time을 변경하기 위해선 useQuery의 staleTime 옵션을 사용하면 된다.

const useUser = () => {
  const queryClient = useQueryClient();

  const { data: user } = useQuery<User>({
    queryKey: userKeys.user,
    queryFn: getUser,
+   staleTime: Infinity,
    suspense: true
  });

  // ...

  return { user, clearUser, initialUser };
};

export default useUser;

staleTime을 Infinity로 설정해 영구적으로 fresh한 데이터로 간주하도록 하였고 아래와 같이 페이지를 변경하였을 때 API 재요청 문제를 해결할 수 있었다.

만약 user의 상태가 변경되는 작업(ex: 새로운 글 작성)을 한다면 쿼리 무효화를 사용해 다시 user 상태를 불러오는 과정이 필요하다.

return useMutation({
  mutationFn: { mutationFn },
  onSuccess: async () => {
    await queryClient.invalidateQueries(userKeys.user);
  },
});

stale time과 cache time 이란?

stale time 개념을 사용해 문제를 해결한 후, cache time은 무엇인지 궁금했다. 이름만 생각했을 땐 stale time과 cache time의 역할이 비슷하게 생각되었기 때문이다.

cacheTime과 staleTime, 이 두 옵션은 모두 캐시된 데이터와 관련이 있지만 데이터에 대해 서로 다른 것을 제어한다. 간단히 요약하면 다음과 같다.

stale time

staleTime은 데이터가 fresh한 것(stale하지 않은)으로 간주되는 기간으로, 데이터가 stale한 상태라면 서버에서 다시 fresh한 데이터를 가져온다. 기본값은 0이다.

즉, staleTime은 tanstack query가 데이터를 받은 후 해당 데이터를 유효한(fresh) 데이터로 간주할 것인지에 대한 시간이다. 만약 staleTime이 2초라면 2초 동안 해당 데이터는 유효한 데이터로 간주해서 다시 api 요청을 보내지 않는 것으로 이해할 수 있다.

cache time

cacheTime은 캐시에서 비활성 데이터를 삭제하기 전에 Tanstack Query가 저장하는 기간으로 기본값은 5분이다.
여기서 비활성화 상태(inactive) 는 tanstack query가 캐싱하고 있는 데이터를 사용하지 않는 상태를 의미한다.

렌더링되는 화면에서 tanstack query가 캐싱하고 있는 데이터를 사용하고 있는 상태면 active 상태이지만 캐싱하고 있는 데이터를 사용하지 않는 화면으로 전환하면 inactive 상태로 전환된다.

즉, cacheTime의 역할은 inactive 상태에서 캐싱하고 있는 데이터를 얼마나 유지할 것인지를 담당하며 cacheTime이 지나기 전에 캐시되었지만 stale 상태인 데이터를 사용하는 컴포넌트가 다시 마운트 되면, 데이터를 fetching하는 동안 캐시 데이터를 보여준다.

staleTime Infinity, cacheTime 0?

그럼 만약 staleTime은 Infinity인데 cacheTime은 0이라면 어떻게 될까?

staleTime이 무한이지만 cacheTime은 0으로 inactive 상태로 들어간 캐싱 데이터는 바로 삭제되기 때문에 캐시 데이터가 필요한 페이지로 재진입한다면 캐싱할 데이터가 없어 staleTime이 무한이여도 tanstack query는 데이터를 refetching 해야한다.

참고 문서