캐시 무효화는 언제 필요할까요?
데이터가 오래되어서 최신 데이터를 받아와야 하는 경우, 업데이트 된 데이터로 최신화가 필요한 경우, 동시성 문제 방지 등등...
사용자가 꼭 수동으로 데이터를 가져와야 하는 경우가 아니라면 적절한 시점에 데이터를 최신화해주는게 좋은 UX로 가는길로 보입니다.
tanstack-query는 DX, UX를 위한 몇가지 전략을 제시합니다. queryKey를 기반으로 각 쿼리 데이터를 식별하는데, queryKey를 사용한 캐시 전략은 key 관리방법에 가깝기 때문에 제외하고 기능적으로 어떤 캐시 관리 방법 있는지 살펴보겠습니다.
자동 리페치는 특정 이벤트 발생 시, 쿼리가 자동으로 다시 실행되도록 하는 전략입니다.
const { data, isLoading } = useQuery("todos", fetchTodos, {
refetchOnWindowFocus: true, // 브라우저 창 포커스 시 자동 리페치
refetchOnMount: true, // 컴포넌트 마운트 시 자동 리페치
refetchInterval: 60000, // 60초마다 자동 리페치
staleTime: 60000, // 1분 동안 데이터를 "신선한" 상태로 간주
cacheTime: 300000, // 5분 동안 캐시 유지
});
queryClient.invalidateQueries
를 사용해 특정 쿼리 키를 가진 데이터를 수동으로 무효화하고, 다시 가져올 수 있습니다.
import { useMutation, useQueryClient } from "@tanstack/react-query";
const queryClient = useQueryClient();
const mutation = useMutation(updateTodo, {
onSuccess: () => {
// 'todos' 키를 가진 모든 쿼리 무효화
queryClient.invalidateQueries("todos");
},
});
데이터가 성공적으로 업데이트된 것처럼 UI를 미리 업데이트한 후 서버 응답에 따라 상태를 조정합니다.
const mutation = useMutation(updateTodo, {
onMutate: async (newTodo) => {
// 쿼리 무효화 전 작업 취소
await queryClient.cancelQueries("todos");
// 기존 데이터 저장
const previousTodos = queryClient.getQueryData("todos");
// 미리 UI 업데이트
queryClient.setQueryData("todos", (old) =>
old.map((todo) => (todo.id === newTodo.id ? newTodo : todo))
);
return { previousTodos };
},
onError: (err, newTodo, context) => {
// 롤백
queryClient.setQueryData("todos", context.previousTodos);
},
onSettled: () => {
// 작업 완료 후 무효화
queryClient.invalidateQueries("todos");
},
});
낙관적 업데이트를 완료한 후에, invalidateQueries를 실행합니다.
왜 refetch를 하지않고 invalidation 동작을 하는걸까요?
invalidate: 무효화하다.
tanstack에서 invalidation은 쿼리의 데이터가 더이상 최신 데이터가 아님을 의미합니다.
tanstack-query에서 데이터를 새로 fetch하는 조건은 아래와 같습니다.
- 캐시상태가 유효한가
stale
: 유효하다- staleTime을 사용하거나, 수동으로 invalidate하는 경우
- 쿼리가 다시 활성화 될 때
- 쿼리가 비활성화 되었다가 다시 활성화 된 경우
refetchOnWindowFocus
나refetchOnReconnect
의 옵션을 사용하는 경우
- queryKey가 변경될 때
useQuery(["todos", { userId }], fetchTodos);
이 경우 userId가 변경되면 이전 캐시 데이터를 무시하고 새로 fetch함.
- 수동으로 데이터를 fetch하는 경우
- refetch 사용
- invalidate후 쿼리가 active 되어있는 상태라면 자동으로 쿼리를 fetch
- 쿼리가 Active 되어있다: 쿼리를 사용하는 컴포넌트가 렌더링 되어있는 경우
-
queryClient.invalidateQueries(["todos"]);
- 캐시가 만료된 경우
catchTime
옵션에 따라 쿼리 캐시가 만료된 경우 (기본 5분)-
useQuery(["todos"], fetchTodos, { cacheTime: 1000 * 60 * 10 }); // 10분
이런 조건일때 tanstack-query에서는 데이터를 새로 가져옵니다.
따라서 invalidateQuery
는 쿼리가 더이상 유효(stale) 하지않음을 알려주고, 쿼리가 사용중(actvie)인 경우(컴포넌트가 렌더링 되어있는 경우) 데이터를 새로 fetch합니다.