react-query 캐싱 전략과 prefetch

useQuery에서의 쿼리 키 설계, prefetchQuery 사용법, staleTime과 retry 설정, isFetching과 isLoading 차이에 대해 실습 코드로 학습한 내용을 정리합니다.


1. 쿼리 키 전략

React Query는 queryKey가 같으면 동일한 캐시 공간을 사용하기 때문에, 서로 다른 데이터를 명확히 구분해야 할 때는 쿼리 키를 세분화해줘야 한다. 아래 댓글과 같은 데이터를 id 또는 index 마다 조회를 하는데 각 데이터의 id마다 캐시 공간을 부여 해줘야 함

다음과 같은 예시를 보자면

// ❌ 잘못된 예시 - queryKey가 모두 "comments"로 동일하여 캐시가 덮어씌워짐 ( 같은 내용이 나옴)
useQuery({
  queryKey: ["comments"],
  queryFn: fetchComments,
});
 
// ✅ 올바른 예시 - post.id를 포함하여 각 게시글마다 독립적인 캐시 공간을 부여
useQuery({
  queryKey: ["comments", post.id],
  queryFn: () => fetchCommentsByPost(post.id),
});

쿼리 키를 동적으로 구성함으로써 각 post에 대한 comments 데이터를 독립적으로 캐싱할 수 있음.


2. prefetchQuery

페이징 처리를 하면 다음 페이지를 미리 불러와 캐싱 할 수 있다. 그게 바로 prefetchQuery 이다.

그러면 왜 미리 불러오면 좋을까?

  • prefetchQuery를 통해 사용자가 버튼을 누르기 전에 데이터를 미리 불러와 캐시해두면, 더 빠른 UX를 제공할 수 있다.
  • 만약 prefetchQuery를 사용하지 않는다면 데이터를 Pagination처리를 하는 경우에 이전/다음 페이지를 누를 때 마다 다음 페이지의 데이터를 불러올 때까지 로딩 상태를 기다려야 한다
    • 그것은 UX 측면에서 좋지 않다는거다
    • 이를 처리 하기 위해 prefetchQuery로 다음 페이지를 누를때 그 다음 페이지의 데이터를 같이 불러온다 - 사용자가 다음 페이지(예: 2페이지)를 조회할 때, prefetchQuery를 활용해 그 이후의 페이지(3페이지) 데이터를 미리 불러와 캐시에 저장해둔다.
    • 이로 인해 사용자는 다음(3page) 페이지로 이동하더라도 로딩 없이 캐시된 데이터를 즉시 확인할 수 있으며, 데이터가 fetching 되는 순간을 인식하지 못할 정도로 매끄러운 UX를 경험하게 된다.
const queryClient = useQueryClient();
 
useEffect(() => {
  if (currentPage < maxPostPage) {
    const nextPage = currentPage + 1;
 
    queryClient.prefetchQuery({
      queryKey: ["posts", nextPage],
      queryFn: () => fetchPosts(nextPage),
    });
  }
}, [currentPage, queryClient]);
  • 페이지 이동 시 즉시 데이터 렌더링이 가능하다
  • 로딩 스피너를 줄여 UX 개선이 가능하다
  • 캐시 활용으로 불필요한 네트워크 요청 방지가 가능함

3. isLoading vs isFetching에 대해 자세히 알아보자

const { data, isError, isLoading, isFetching } = useQuery({
  queryKey: ["posts", currentPage],
  queryFn: () => fetchPosts(currentPage),
  staleTime: 2000,
});

isFetching

  • 비동기 쿼리 함수가 진행 중인 경우 항상 true를 반환한다
  • 캐시가 있어도 stale 상태면 refetch 시 isFetching: true를 반환한다

isLoading

  • isLoading은 isFetching의 하위 개념이다
  • 쿼리가 처음 실행될 때는 true
  • 캐시가 없는 상태에서 데이터를 처음 불러오는 경우

예시로 쉽게 아래 코드로 동작을 살펴보면

const { data, isLoading, isFetching } = useQuery({
  queryKey: ["posts", currentPage],
  queryFn: () => fetchPosts(currentPage),
  staleTime: 2000,
});
 
if (isLoading) return <h3>로딩 중 입니다...</h3>;
if (isFetching) return <h3>데이터를 가져오는 중입니다...</h3>;
  • isLoading은 캐시가 없는 상태에서 다음 데이터를 조회하는 경우에만 true를 반환하므로 다음 페이지의 데이터는 이미 캐싱이 되었으니 로딩 중 입니다...가 나타나지 않는다.
  • 하지만 isFetching은 캐시가 있는 상태에서도 새로운 데이터를 조회를 하므로 데이터를 가져오는 중입니다...가 보여진다.
react-query 캐싱 전략과 prefetch