[TIL] React-Query에서 데이터 삭제/수정 후 목록이 갱신되지 않을 때 (캐시와 invalidateQueries 이해하기)

승민·2025년 4월 6일
0

TIL

목록 보기
12/20


팀 프로젝트에서 react-query를 사용해 게시글 목록 조회 + 삭제/수정 기능을 구현하던 중, 다음과 같은 문제를 겪었습니다.

게시글을 삭제 및 수정했는데, 데이터 변화가 없다...
새로 고침을 해야 수정된 데이터를 받는다.

이번 글에서는 내가 겪은 문제 상황과 함께, React-Query의 캐시 작동 방식, 그리고 어떻게 해결했는지를 정리했습니다.

삭제했는데 왜 안 사라져?

예를 들어 게시글 목록을 조회하는 쿼리가 이렇게 있다고 가정합니다.

const { data: posts } = useQuery(['posts'], fetchPosts);

그런데 deletePost를 통해 삭제 요청이 성공적으로 완료되어도,
화면에 보이는 posts 목록에서는 삭제한 글이 그대로 남아 있는데 이는 캐싱 때문입니다.

캐싱

React-Query는 useQuery에서 지정한 query key 기준으로 데이터를 캐싱합니다.

따라서 데이터 수정이 발생해도 해당 query의 캐시가 갱신되지 않으면, 컴포넌트는 이전의 stale한 데이터를 계속 사용하게 되는데, 서버 데이터는 변경되었지만 클라이언트는 여전히 이전 데이터를 참고하고 있는 것입니다.


해결 방법

invalidateQueries로 캐시 무효화
React-Query는 mutation의 onSuccess 콜백에서 관련된 쿼리 키를 명시적으로 무효화

const queryClient = useQueryClient();

const deletePostMutation = useMutation(deletePost, {
  onSuccess: () => {
    queryClient.invalidateQueries(['posts']); // 목록 캐시 무효화
  },
});

invalidateQueries(['posts'])를 호출하면, React-Query는 해당 쿼리를 stale 상태로 만들고, 자동으로 다시 fetch해, 결과적으로 목록이 최신 상태로 갱신됩니다.

관련 API 비교

메서드 설명

  • invalidateQueries
    해당 query를 stale 상태로 만들고, 다시 요청하게 함
  • refetchQueries
    지정된 query를 강제로 다시 fetch (invalidate보다 즉각적)
  • setQueryData
    쿼리의 캐시 데이터를 직접 수정 (Optimistic UI 등에 사용)

다른 방법들

staleTime과 cacheTime
staleTime: 이 시간이 지나기 전까지는 fresh 상태로 간주 (refetch 발생 안 함)
너무 길게 설정하면 invalidateQueries 효과가 늦게 반영될 수 있음

useQuery(['posts'], fetchPosts, {
  staleTime: 1000 * 60 * 5, // 5분
});

Optimistic UI

mutation 전에 UI를 먼저 바꿔 보여주고, 성공 시 확정 실패하면 rollback하는 로직이 필요함

onMutate, onError, onSettled 등 mutation의 생명주기 활용

const queryClient = useQueryClient(() => axios.post(`api/like/${id}`), {
	onMutate: async (id) => {
      // 'queryKey'로 진행 중인 refetch 취소하여 낙관적 업데이트를 덮어쓰지 않도록 함
		await queryClient.cancleQueries({
        	queryKey: ['queryKey]
        })
          
      // 이전 데이터를 받아옴
        const previousData = queryClient.getQueryData(['queryKey']);
      
      // 새로운 값으로 낙관적 업데이트
      	queryClient.setQueryData(['queryKey'], (prev) => !prev)
      
      	return { previousData }
    },
  // mutation이 실패한 경우
  onError: (err, newData, context) => {
    // onMutate로부터 반환된 context를 사용하여 rollback
    queryClient.setQueryData(['queryKey'], context.previousData)
  },
  onSettled: () => {
    // 성공, 실패 여부에 관계 없이 refetch
   	queryClient.invalidateQueries({queryKey: ['queryKey']}) 
  }
});

const deleteData = useMutation({
 	 
})

배운점

이번 경험을 통해 배운 핵심 포인트는 다음과 같습니다.

  1. mutation 후에는 반드시 관련 query를 invalidate하거나 setQueryData로 직접 반영해야 한다.
  2. query key 설계를 잘해야 invalidate 시 유연하게 대응할 수 있다.

React-Query를 더 깊이 이해하고 싶다면, 단순한 fetching뿐만 아니라
캐시 무효화, 상태 동기화 전략까지 함께 익히는 것이 중요하다고 느꼈습니다.

참고 자료

React-Query 공식 문서 - Query Invalidation
React-Query 공식 문서 - Mutations
React-Query 제대로 사용해보기 (1) useMutation

0개의 댓글