무엇
MutationCache는 뮤테이션의 상태가 담기는 저장소다. QueryCache의 뮤테이션 판으로, QueryClient가 내부에 하나 들고 있으며 직접 다룰 일은 드물다.
인스턴스가 필요하면 queryClient.getMutationCache()로 얻는다. 직접 생성하는 거의 유일한 이유는 전역 콜백을 붙이기 위해서다.
import { MutationCache, QueryClient } from '@tanstack/react-query'
const queryClient = new QueryClient({
mutationCache: new MutationCache({
onError: (error) => {
toast.error(`변경에 실패했습니다`)
},
}),
})
무슨 일이 일어나나
useMutation으로 만들어진 뮤테이션의 상태가 이 캐시에 담긴다. 생성자에 넘긴 콜백은 전역으로 동작하며, 개별 뮤테이션의 콜백보다 먼저 불리고 개별 콜백이 덮을 수 없다. 성공·실패·시작·종료 네 시점에 각각 onSuccess, onError, onMutate, onSettled를 붙일 수 있다.
각 콜백은 데이터·변수와 함께 뮤테이션 인스턴스까지 인자로 받아, 어느 뮤테이션에서 일어난 일인지 구분할 수 있다. 오프라인에서 멈춘 뮤테이션의 재개나 전역 에러 로깅 같은, "모든 뮤테이션에 공통"인 일을 여기서 처리한다.
사용법
전역 에러 처리가 가장 흔한 용도다. 뮤테이션마다 반복하지 않고 한 곳에서 실패를 잡는다.
import { MutationCache, QueryClient } from '@tanstack/react-query'
const queryClient = new QueryClient({
mutationCache: new MutationCache({
onError: (error, _variables, _onMutateResult, mutation) => {
// meta로 개별 문구를 심어 분기할 수 있다
const message = (mutation.meta?.errorMessage as string) ?? '요청 실패'
toast.error(message)
},
}),
})
캐시를 직접 봐야 할 때는 getAll()로 모든 뮤테이션 인스턴스를 꺼내고, subscribe(callback)으로 변화를 구독하며(해제 함수 반환), clear()로 전체를 비운다.
실무 예시
전역 onSuccess로 성공 시 관련 쿼리를 한꺼번에 무효화하는 패턴이다. meta에 무효화할 키를 심어 두면, 콜백에서 뮤테이션별로 다르게 처리할 수 있다.
const queryClient = new QueryClient({
mutationCache: new MutationCache({
onSuccess: (_data, _variables, _onMutateResult, mutation) => {
const keys = mutation.meta?.invalidates as unknown[][] | undefined
keys?.forEach((queryKey) => queryClient.invalidateQueries({ queryKey }))
},
}),
})
// 개별 뮤테이션에서
useMutation({
mutationFn: updateTodo,
meta: { invalidates: [['todos']] }, // 성공하면 todos 무효화
})
왜 중요한가
MutationCache는 실무에서 직접 조작할 일이 거의 없다 — 대부분의 뮤테이션 작업은 useMutation과 그 콜백으로 끝난다. 이 클래스를 꺼내 쓰는 거의 유일한 상황은 모든 뮤테이션에 걸리는 전역 에러 처리나 자동 무효화 로직을 한 곳에 두고 싶을 때다. 개념상으로는 "뮤테이션 상태도 캐시에 있다"는 구조를 이해하는 정도면 충분하다.
Reference