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은 캐시가 있는 상태에서도 새로운 데이터를 조회를 하므로 데이터를 가져오는 중입니다...가 보여진다.