TanStack-Query staleTime & invalidQueries를 이용한 data 상태관리

박민우·2023년 12월 5일
2

🚨 TanStack-Query

목록 보기
1/1
post-thumbnail

TanStack-Query에서는 useQuery를 이용해 서버의 data를 받아올 수 있고, data의 staleTime과 gcTime을 설정해줌으로써 서버 data의 상태를 관리할 수 있다. 이러한 staleTime, gcTime과 invalidQueries를 활용해 어떻게 서버 data의 상태 관리를 효율적으로 할 수 있는지 알아보자.


📌 staleTime

  • 캐시된 data가 신선한 상태(fresh)로 남아있는 시간을 말한다.

  • 특정 data에 대해 설정해준 stale time이 지나게되면, 그 data는 신선하지 않은 상태(stale)로 간주된다.

  • 따로 stale time의 값을 설정해주지 않으면 기본값은 0이다.

    => data를 fetch 해오자마자 data를 신선하지 않다고 간주하는 것 !

  • 특정 쿼리 키에 대한 data를 다시 fetch 해와야 하는 상황일 때,

    • 해당하는 data가 fresh한 상태라면, API 호출 없이 캐싱된 data가 다시 사용된다.
    • 해당하는 data가 stale한 상태라면, API 호출을 통해 신선한 data를 다시 fetch해오고, 이 data를 cache에 저장한다.

=> 즉, 서버로부터 새로 data를 받아오지 않고 캐시된 data를 최신의 data로 간주하고 사용하기 위해 설정해주는 조건이다.


📌 gcTime

  • 메모리에 저장된 캐싱 데이터가 유효한 시간(캐시 메모리에 남아있는 시간)을 의미한다.

  • 쿼리를 사용하는 모든 컴포넌트가 언마운트되었을 때, 쿼리는 비활성화(inactive 상태)되고 비활성화된 데이터는 gcTime이 지난 후, 캐시에서 삭제된다.

    쿼리를 사용하는 컴포넌트가 언마운트 되지 않고 계속 마운트된 상태로 화면에 계속 보여진다면, cache data는 계속 남아있다.

    => 쿼리를 사용하는 모든 컴포넌트가 언마운트 된 이후부터 gcTime이 지나야 캐시 데이터가 삭제되기 때문이다.

  • 캐싱 데이터가 없다면, 특정 query key에 해당하는 쿼리 호출 시, 다시 API 요청을 통해 data를 받아온다.

  • 따로 gcTime의 값을 설정해주지 않으면 기본값은 5분이다.


📌 staleTime, gcTime에 따른 data fetch

그렇다면 staleTime과 gcTime가 어떻게 설정되어있는지에 따라 data fetch가 어떻게 달라지는지 각 상황을 통해 알아보자.

특정 data를 fetch 할 때

  1. staleTime 아직 안 지남, gcTime 아직 안 지남

    => API 요청 X, 캐시 데이터 사용

  2. staleTime 아직 안 지남, gcTime 지남

    => staleTime은 아직 지나지 않아서 cache data를 최신의 data로 간주하고 사용하고 싶은데 gcTime이 지나서 cache에 data가 없는 상황

    => API 요청으로 새로운 data 받아와야 함

    사실 이런 경우는 해당 쿼리가 포함된 페이지나 컴포넌트가 새로 다시 mount되는 경우일 것이다.

    => 만약 unmount 되지 않고 계속 해당 쿼리가 활성화 상태라면 cache에 data가 계속 있는 상태였을 것이므로

    => unmount 되었다가 다시 mount되어 다시 data fetch 하는 경우일 듯

    EX)

    export const useGetContentList = (type: string, title: string, page: number) => {
      const { data } = useQuery(
        [{ scope: "ContentList", type, title, page }],
        () => {
          console.log(`fetch Data: ${type},${title}, ${page}`);
          return getContentList(type, title, page);
        },
        {
          staleTime: 1000000,
          gcTime: 100,
          select: (data) => {
            return data?.contentList;
          },
        }
      );
    
      return {
        contentListData: data,
      };
    };

    위와 같이 gcTime을 엄청 짧게 설정해, 쿼리가 inactive된 후 바로 삭제되도록 했을 때, 다시 해당 쿼리가 불렸을 때 다시 data를 fetch해오고 이를 cache에 추가하는 것을 확인할 수 있었다.

  3. staleTime 지남, gcTime 아직 안 지남

    => API 호출로 새로운 신선한 data를 받아오는데, 받아오기 전까지는 cache data를 보여줌

    여기서 해당 useQuery에 대한 isLoading 값은 계속 false이기 때문에 사용자는 loading 화면을 보고 있지 않아도 된다.

  4. staleTime 지남, gcTime 지남

    => API 호출로 새로운 신선한 data를 받아오고, 받아오기 전까지는 보여줄 data 없음


위의 각 상황들을 정리해보면 !

fetch나 refetch를 통해 data를 불러올 때

  • staleTime 안 지나면 cache data 사용
    • gcTime 지나서 cache data 없으면 => refetch
  • staleTime 지나면 무조건 새로운 data 받아오는 것
    • 새로운 data 받아오는 동안, cache data가 있으면 이를 보여주다가 새로운 data로 교체해주는 것

📌 invalidQueries

tanstack query는 특정 쿼리를 무효화해주는 invalidQueries 메서드를 제공한다.

// 캐시의 모든 쿼리를 무효화함
queryClient.invalidateQueries();


// `todos`로 시작하는 쿼리키를 가지는 모든 쿼리를 무효화함
queryClient.invalidateQueries({ queryKey: ['todos'] });

쿼리를 무효화한다는 것은 무슨 뜻일까 ? invalidateQueries 메서드를 통해 특정 쿼리를 무효화하면, 다음과 같은 두 가지 현상이 발생한다.

  • 해당 쿼리의 상태를 stale로 바꾼다. stale된 상태는 useQuery 또는 관련 훅에서 사용 중인 모든 staleTime 구성에 오버라이드한다.
  • 만약 useQuery 나 관련 훅을 통해 쿼리가 렌더링되고 있다면 백그라운드에서도 refetch한다.

=> 그래서 invalidQueries 메서드를 이용해 특정 쿼리를 무효화하면, 해당 쿼리의 data를 신선하지 않은 상태로 간주해 이후 data를 fetch 할 때 캐시의 data가 아닌 서버로부터 새로운 data를 받아올 수 있도록 하는 것이다.


📌 queryKeys

queryKeys 란?

  1. React Query에서는 query keys를 기반으로 해서 쿼리 캐싱을 관리한다.
  2. query key는 문자열, 문자열의 배열 혹은 중첩된 객체(nested object)로 지정 가능하다.
  3. query keys는 query data에 고유하고 직렬화하여 사용해야한다.

queryKey에 queryFunction의 변수 포함하기

공식 문서에서는 다음과 같이 쿼리 키를 지정하도록 권장한다.

쿼리 키는 쿼리 function에 대한 dependencies의 역할을 한다. 쿼리 function이 의존하고 있는 변수들을 쿼리 키 배열에 넣음으로써, 각 쿼리 키에 해당하는 쿼리들이 독립적으로 cache되고, 변수가 변할 때마다 각 쿼리들이 자동적으로 refetch 되도록 할 수 있다.


📌 쿼리가 refetch 되는 조건

  • 새로운 Query Instance가 마운트 될 때 ( 페이지를 이동했다가 왔을 경우 )
  • 브라우저 화면을 이탈했다가 다시 Focus 할 때
  • 네트워크가 다시 연결될 때
  • 설정한 refetch interval에 의한 경우
  • queryKey에 특정 state가 포함되고, state값이 변경될 경우

staleTime && refetch

staleTime이 0으로 설정되어있다면 data를 fetch 하는 즉시 stale한 상태로 변하게 되고, refetch가 일어나는 조건과 일치하게 되면 데이터가 패치됩니다.

즉, staleTime이 지나 data가 stale 한 상태가 되더라도, 바로 refetch가 일어나는 건 아니다.

만약 그렇다면 기본 staleTime은 0인데 페이지에 그대로 있어도 data가 무한으로 계속 refetch 될 것이다.

=> staleTime이 지나고 && refetch가 일어나는 조건에 해당될 때 refetch가 일어나는 것


refetch의 전제조건은 데이터가 stale한 상태여야 한다.

⇒ 다른 페이지 갔다가 와서 새로 mount 되더라도, data가 fresh 하다면 refetch 하지 않는다. (ex) 작성하고 다시 홈페이지가면 업데이트 x)

=> 대신 새로고침할 때는 무조건 refetch 된다. ( refetch라고 부르는 것이 올바르지 않은 듯)


📌 staleTime, InvalidQueries를 이용한 data 상태 관리

위에서 살펴본 queryKeys, staleTime, InvalidQueries의 개념을 서로 연결해 data의 상태를 어떻게 효율적으로 관리할 수 있는지 알아보자 !

특정 data가 자주 변경되지 않고, 특정 상황에서만 변경이 된다면 다음과 같은 방법으로 data를 관리할 수 있다.

  1. 해당 query의 staleTimeinfinity로 설정
  2. data가 변경되는 상황일 때, 해당 data에 해당하는 쿼리 키 기반으로 쿼리 무효화 invalidQueries 실행
  3. data가 stale 해졌으므로, 서버에서 data를 새로 받아온다.

이를 통해 , 특정 페이지에 접근할 때마다 무조건 서버로부터 data를 받아오는 것이 아니라, data가 변경이 되었을 때만 서버에서 새로운 data를 받아옴으로써 불필요한 API 호출을 줄일 수 있다.


실제 나의 프로젝트에서 적용했던 예시를 살펴보면,

  1. 내 계획을 불러오는 get 요청에 대한 useQuery의 staleTimeinfinity로 설정해주고
export const useGetMyPlansQuery = () => {
  const { data } = useSuspenseQuery({
    queryKey: [QUERY_KEY.MY_PLANS],
    queryFn: getMyPlans,
    staleTime: Infinity,
  });
    
  return { myPlans: data! };
};
  1. 계획을 생성했을 때, 즉 계획을 생성하는 mutation 함수가 성공했을 때, 계획을 불러오는 query를 무효화 시켜줬다.
export const usePostNewPlanMutation = () => {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: postNewPlan,
    onSuccess: () => {
      queryClient.invalidateQueries({
        queryKey: [QUERY_KEY.MY_PLANS],
      });
    },
  });
};

여기서 쿼리키를 기반으로 특정 쿼리만 무효화시켜주었음을 확인할 수 있다.

queryClient.invalidateQueries({
     queryKey: [QUERY_KEY.MY_PLANS],
   });
  1. 이를 통해 내 계획 data를 페이지에 접근할 때마다 서버로부터 받아오는 것이 아니라 내 계획 data가 바뀌는 상황인 계획 생성에 성공했을 때만 새로 서버에서 data를 받아올 수 있었다.

🙇🏻‍♂️ 참고

리액트 쿼리의 staleTime과 cacheTime

공식문서 - query keys

queryKey 작성방법 및 특징

React query : Query Invalidation

profile
꾸준히, 깊게

0개의 댓글