React.js

React Query 2편

Hoon1994 2022. 11. 4. 16:46

 

✏️ React Query의 Stale

 

저번 포스팅에서는 React Query의 기초적인 부분에 대해서 알아보았다. 
이번 포스팅에서는React Query의 Stale과 Cache에 대해서 적어보려고 한다.

 

 

Stale

React Query에서 Stale은 만료되었다는 뜻을 가지고 있다. React Query Devtools를 보면 Stale, Fetching, Fresh, Inactive 등의 상태를 확인할 수 있다.여기서 Stale에 표시되는 Query는 만료된 데이터를 뜻한다.

 

만료된 데이터라는 것을 무엇일까? Stale이란 신선하지 않은.. 이라는 뜻을 가지고 있다. 데이터가 신선하지 않고, 서버에서 다시 새로운 데이터를 받아야 하는 뜻으로 볼 수 있을 것 같다. 

 

React Query에서는 Stale Time이라는 속성을 정의할 수 있다. 기본 값은 0이다. 
0으로 두면 항상 만료된 데이터가 되서, 불러오자마자 Stale 상태가 된다. 만약 서버 데이터가 항상 최신을 유지해야 하는 값이 아니라면, Stale Time을 길게 잡아서 가져온 데이터를 활용할 수 있다.

 

Data Refresh는 만료된 데이터에서만 실행이 가능하다. 만료된 데이터인지는 Devtools에서 확인할 수 있다. 

 

사용 예시

const { data, isLoading, isError, error } = useQuery('posts', fetchPosts, { staleTime: 2000 });

 

 

Cache

React Query에서 Cache는, 우리가 아는 그 Cache라고 보면 될듯하다. 가져온 데이터를 Cache 해두고, 재사용을 한다.

React Query의 Cache 동작 방식은 이렇다.

  1. 특정 Query에 대한 활성 useQuery가 없는 경우 (데이터가 사용되고 있지 않을 경우) 콜드 스토리지로 이동
  2. 구성된 CacheTime이 지나면 Cache Data가 만료됨 (기본 값 5분)
  3. 5분은 useQuery가 활성화된 후 경과된 시간.
  4. Cache가 만료되면 가비지 컬렉션이 실행되고, 클라이언트는 데이터 사용이 불가하다. 

예시는 아래와 같다.

  1. 첫번째 useQuery 인스턴스가 생성되면 데이터를 가져오고 해당 Query Key 아래에 Cache가 된다.
  2. 해당 Query Key로 다른 곳에서 인스턴스가 생성이 되면 해당 Key에 대한 데이터가 있기 때문에 캐시된 데이터가 즉시 반환되며, 새 네트워크 요청을 한다.
  3. 요청이 성공적으로 완료되면 해당 Key의 Cache Data가 새로운 Data로 업데이트되고, 두 인스턴스 모두 업데이트됨.
  4. 두 인스턴스가 모두 마운트 해제되어 더 이상 사용되지 않을 경우 Cache Time이 설정됨.
  5. Cache Time이 만료되기 전에 한번 더 같은 Key로 호출되면 2 ~ 3번 반복
  6. Cache Time이 만료되기 전까지 인스턴스가 생성되지 않을 경우 Cache Data가 삭제되고 가비지 컬렉터에 수집됨. 

 

Cache 관련해서 useQuery를 사용할 때 조심해야 할 항목이 있다.

 

상단에는 여러개의 게시글이 있고, 게시글을 클릭하면 하단에 해당 게시글에 대한 댓글이 표시된다는 화면이 있다고 가정해보자. 댓글을 가져오려면 어떻게 해야 할까?

 

export function Detail({ id}) {
	const { data, isLoading, isError, error } = useQuery('comments', () => getComments(id));
	...
}

 

만약 위와 같이 사용한다면, 문제가 생기는 것을 알 수 있을 것이다. 게시글을 누를 때마다 해당 게시글의 id에 해당하는 댓글을 가져오는데, Query의 Key 값이 같기 때문에 id가 다른 게시글을 클릭하더라도 Cache Data를 불러오게 되어 게시글 id에 따라 댓글이 변경되지 않을 것이다. 이 문제를 해결하려면, 의존성 배열을 사용해야 한다.

 

export function Detail({ id}) {
	const { data, isLoading, isError, error } = useQuery(['comments', id], () => getComments(id));
	...
}

이렇게 설정할 경우 각자 다른 Query로 다뤄지기 때문에 정상적으로 동작이 된다.

행여나 이런 문제를 겪었을 때 Cache 무효화 하는 방법으로 해결을 하면 안된다. Cache Data는 유지하고 있어야 한다.

 

위의 댓글을 가져오는 방식대로 Pagination을 구현할 수도 있다.

const [currentPage, setCurrentPage] = useState(0);

  
const { data, isLoading, isError, error } = useQuery(['posts', currentPage], () => fetchPosts(currentPage));

페이지 이동이 될 때 해당 페이지 번호에 따라 Query Key가 생기고 Data를 불러온다.

불러온 Data는 다른 페이지로 이동하게 되면 Cache Data로 남아있고, 클릭했던 번호를 다시 누를 경우 Cache Data를 미리 보여주고 새로운 Data를 가져올 수 있어 UX면에서도 좋은 것 같다. 이런 부분이 React Query의 매력 아닐까 싶다.