Front-End/Next

[Next.js] React-Query로 쿼리 캐싱한 데이터 가져오기

Voyage_dev 2022. 11. 26. 23:40

리액트 쿼리는 서버사이드 렌더링을 지원한다. NextJS에서 React-Query를 연동하는 방법을 지난번에 소개했었다. 그러나 나 조차도 프로젝트를 하면서 내가 캐싱에 대해서 잘못 생각하고 있었나?라는 사실을 깨닫게 되었다. 그래서 이번에는 React-Query에 캐싱개념을 좀 더 공부해 볼려고 한다.

 

지난번에도 말했지만 이번에도 한 번 더 개념을 짚어보고 가보자.

NextJS에서는 getStaticProps 또는 getServerSideProps에서 props 객체를 페이지로 전달한 후에 리액트 쿼리의 initialData 옵션을 이용해서 SSR을 구현할 수도 있다. 이렇게 하는 방식이 잘못된 것은 아니지만 공식문서에서는 권장하지 않는다. 이유는 깊이 있는 컴포넌트 단에서 서버 사이드 렌더링한 데이터가 필요할 경우 해당 컴포넌트까지 props로 전달을 해줘야하는 불편함과 Depth가 깊어진다. 반면에 이후에 Hydration 방식을 사용하면 서버 사이드 렌더링때 사용했던 query key(식별자)로 데이터를 불러올 수 있다. 공식 문서에서도 InitialData 옵션으로 설정하는 방법보다는 Hydration 방식을 권장한다

Stale & CacheTime

Tanstack React-Query에서 캐싱 개념은 stale과 cachetime을 통해 이루어지고 있다.

StaleTime

  • 전달받은 데이터는 리액트 쿼리의 자료구조 내용 중 캐시에 저장이 되는데, 이때 이 캐시데이터의 stale 즉, “신선한 상태” 가 언제까지 유지할지를 말해주는 옵션이다. 기본 default 값은 0으로, 받아오는 즉시 stale 하다고 판단하며 캐싱 데이터와 무관하게 계속해서 fetching을 진행한다.

여기서 stale하다 혹은 신선하다라는 서버에서 조회한 데이터는 그때 당시의 데이터이고, 외부 요청으로 서버 데이터가 변경이 되었다면 내 브라우저가 가진 데이터는 이미 오래된 낡은 데이터가 되었으므로 이걸 stale하다라고 말한다.

CacheTime

  • 캐시 구조에 저장된 데이터는 메모리상에 존재하게 된다. 이 때, 메모리에 저장되어 있는 캐시 데이터가 언제까지 유지될지를 말해주는 옵션이다. 즉, 캐싱된 쿼리의 결과값은 계속 유지되는 것이 아니라 시간이 지나면 메모리에서 사라진다. 이때 기본 default 값은 5분이다.

Prefetching

Next.js에서는 prefetchQuery라는 메서드를 이용해 컴포넌트가 렌더링 되기 전에 페칭을 시작할 수 있다.

QueryClient 클래스를 이용해 queryClient 인스턴스를 생성한 후에 prefetchQuery 메서드를 호출했다. 그리고 해당 인스턴스를 dehydrate해서 props로 전달한다. 이후 queryClient는 hydration 과정에서 hydrate되어 클라이언트 측에서 사용 가능하다. 즉, 미리 fetch받을때 사용했던 쿼리 키를 가지고 프론트 측에서 동일한 키로 데이터를 불러오는 것이 가능하다.

 

prefetchQueries는 자바스크립트 번들이 평가되는 즉시 실행된다. 이는 라우트 기반 코드 스플리팅을 적용하는 경우 매우 잘 동작하는데, 사용자가 해당 페이지로 이동하는 즉시, 코드가 지연 로딩되고 평가되기 때문이다.

 

즉, 컴포넌트가 렌더링 되기 전에 시작된다. 프리페칭을 적용하면 서스펜스를 사용하는 경우에도 병렬 쿼리를 다시 사용할 수 있게 된다.

이제 자식 내에서 useQueryClient를 호출하면 위에서 만들어진 객체의 정보를 얻을 수 있다.

const cache = useQueryClient();
console.log(cache);

위 사진에서 볼 수 있든 queryMap이라는 프로퍼티로 prefetching으로 요청한 값이 캐싱되어 저장되고 있음을 알 수 있다.

 

이 순간의 키값은 prefetch로 useQuery를 통해 호출할 당시 첫 번째 인자로 전달된 값으로, 동일한 키를 가진 캐싱값이 존재할 경우, 이 캐싱값을 그대로 사용할 수 있다.

queryClient.getQueryData

// 자식 컴포넌트

const cache = useQueryClient();
const data = cache.getQueryData(["products"]) as ProductItems[];

getQueryData는 기존 쿼리의 캐시된 데이터를 가져오는데 사용할 수 있는 동기 함수이다.

useQuery

// 자식 컴포넌트

const { data } = useQuery(["products"], useGetProducts);

useQuery로 해당 queryKey로 호출된 기록이 있으면 캐시된 데이터를 가져오고 없으면 호출한다.

 

 

주의

  • 두 메서드 다 캐시된 데이터를 가져오지만 getQueryData는 해당 queryKey로 호출된 기록이 있으면 데이터를 가져오지만 없으면 undefined가 된다.
  • 예를 들어, A 컴포넌트와 B 컴포넌트가 있다고 했을 때, A 컴포넌트에서 useQuery로 호출하고 B 컴포넌트로 이동하면 B에서 getQueryData로 캐시된 값을 가져올 수 있으나 A를 거치지 않고 B로 바로 가버리면 undefined가 된다.

 

출처 : 아래의 사이트들을 보면서 큰 공부 하였습니다

https://velog.io/@chltjdrhd777/React-Query-캐싱에-대한-구현

https://velog.io/@eunbinn/seeding-the-query-cache

https://devkkiri.com/post/9611766e-dc1f-4355-a94d-6ac1b4fba13a

https://tanstack.com/query/v4/docs/reference/QueryClient