프론트엔드

[Tanstack Query] InvalidateQueries vs FetchQuery

icems0428 2026. 2. 7. 15:59

오늘은 Tanstack Query의 개념 중 헷갈리는 InvalidateQueries, FetchQuery의 기능과 차이점에 대해 알아보고자 한다.

InvalidateQueries란?

  • 지정한 쿼리 키의 캐시를 무효화 시킨다.
  • 이때 무효화란, data를 stale(상한 상태)하게 만드는 것을 의미한다.

 

stale하다는 것은, 서버 데이터를 즉시 가져오는 것을 보장하지는 않는다.

서버 데이터 호출은 일반적으로 다음과 같은 상황에서 일어난다.

 

  1. active, 캐시가 존재하지 않을 때 (gcTime 만료 등으로) fetch
  2. active, stale할 때 (캐시는 존재하지만, 상한 데이터이므로) refetch

 

gcTime과 staleTime은 아예 다른 축인 것을 기억하면 좋다.

gcTime(구 cachedTime)은 캐시를 저장하는 시간이고,

staleTime은 "캐시가 있더라도 상했는지 표시할거야"이다.

 

비유를 해보자면, gcTime은 냉장고에 음식을 보관하는 시간이고, staleTime은 유통기한이다.

냉장고를 열었을 때, 음식이 없으면 새로 장을 봐와야 하고,

유통기한이 지났으면 일단 음식은 먹을 수는 있되, 필요하다면 뒤에서 새로 장을 본다.

 

우리는 데이터를 사용하는 active 상태일 때 (냉장고를 연 순간),

캐시가 없거나 상한 데이터이면 서버 데이터를 가져온다.

 

만약 상한 데이터가 남아 있다면, 신선한 데이터가 새로 도착하기 전까지는 상한 데이터를 사용한다.

이는 다른 말로는, 캐시를 무효화해서 데이터를 stale하게 만들어도 UI에는 바로 최신 데이터를 보장하지 못할 수 있다고 해석할 수 있다.

 

이럴 때에는 캐시가 바뀌면 UI도 변경되도록 의존성이 제대로 설계되어 있는지,

또는 여러 캐시가 연쇄적으로 업데이트 될 때는, 아직 갱신되지 않은 캐시를 사용하고 있지는 않은지 살펴볼 필요가 있다.

 

여러 상황이 있겠지만, 결국 데이터를 fetch(또는 refetch)하는 방법과 시점을 잘 파악하는 것이 중요하다.

새로 데이터를 가져오고 싶을 때 우리는 invalidateQueries나 fetchQuery를 주로 사용할 수 있다.

 

InvalidateQueries

  • 지정한 쿼리 키의 캐시를 무효화 시킨다.
  • 이때 무효화란, data를 stale(상한 상태)하게 만드는 것을 의미한다.

FetchQuery

  • 쿼리를 바로 fetch 하고, 데이터를 캐시에 저장한다.
  • ( 만약 fetch한 데이터를 다른 쿼리 키에도 저장하고 싶다면, setQueryData를 활용할 수 있다. )

 

위에서 말했듯이, invalidate는 데이터를 stale하게 만드는 것까지만 수행하기 때문에, await를 건다고 해도

페이지 이동 시 최신 데이터가 바로 보이지 않고, 이전 데이터가 잠시 보일 수 있다.

이 현상은 특히 이전 페이지에서는 데이터가 inactive였다가, 이동한 페이지에서 데이터가 active일 때 두드러질 수 있다.

 

따라서, 만약 페이지가 렌더링되기 전에 최신 데이터를 보여주고 싶다면,

await + fetchQuery를 조합하여 사용하는 것이 좋다.

await가 실행된 이후 페이지를 이동한다면 해당 쿼리 키에 대해서는 최신 데이터를 보장할 수 있다.

 

복잡한 상태 관리

마지막으로, 여러 상태가 연결되어 있는 복잡한 캐시 상태를 관리해야 할 때는 어떻게 해야할까?

예를 들면, 독립된 엔터티 간에 의존 관계가 있는 경우가 있을 수 있다.

즉 각각이 다른 쿼리와 쿼리키 계층으로 관리되기 때문에, 캐시를 설계할 때 특히나 신경써야 한다.

 

캐시 전략은 백엔드 API 응답에 따라서 방법이 매우 다양해진다.

 

먼저, 조회뿐만 아니라 mutation (생성, 수정, 삭제) 결과에 식별자가 포함된다면 프론트 캐시 전략이 쉬워진다.

 

모든 데이터를 준다면, mutation이 성공하면 응답에서 setQueryData를 하면 가장 간단하게 데이터를 바꿔 낄 수 있다.

하지만 정보가 많아 무거워진다면 불필요한 통신 비용이 발생할 수 있다.

 

만약 식별자만 준다면, 이를 토대로 다시 조회 쿼리를 fetch 하거나 invalidate 하면 된다.

 

하지만, mutation에서 정보를 보수적으로 주게 되면 난이도가 올라간다.

onMutate로 요청 바디나 기존 캐시에서 데이터를 꺼내 낙관적 업데이트를 해야 하는데, 낙관적 업데이트에 익숙하지 않다면 꽤나 코드가 복잡해져 어려울 수 있다.

 

물론 그럼에도 해결책은 있는데, 캐시 범위를 넓게 잡아서 invalidate 하면 된다.

하지만 이러면 관련 없는 다른 데이터들도 캐시를 무효화시키기 때문에, 불필요한 API 호출이 발생할 수 있다.

 

필자의 경험상,
좋아요와 같이 구조가 단순한 경우에는 낙관적 업데이트가 가장 관리하기 쉬웠고,
그 외의 경우에는 mutation 응답에 식별자 정보를 포함하도록 API를 설계하는 편이
프론트 캐시 전략을 단순하게 만드는 데 도움이 되었다.