TanStack Query(구 React Query)는 서버에서 가져온 데이터를 캐시하고, 오래된 데이터를 다시 가져오고, 로딩·에러 상태를 관리해 주는 비동기 상태 관리자다. 데이터 요청 함수 자체가 아니라 그 결과의 생명주기를 맡는다.
서버 상태(Server State)와 클라이언트 상태(Client State)
- 클라이언트 상태(모달 열림, 입력값)는 앱이 소유하고, 동기적이며, 항상 최신이다
- 서버 상태는 원격에 저장돼 있고, 비동기로 가져오며, 다른 사람이 바꿀 수 있어 언제든 낡을 수 있다. 클라이언트가 보는 것은 스냅샷일 뿐이다
이 둘을 같은 전역 스토어에 넣던 관행을 바꾼 것이 이 라이브러리의 핵심이다(Redux 이후의 상태 관리 선택).
const { data, isLoading, isFetching, error } = useQuery({
queryKey: ['todos', { status }], // 키가 바뀌면 다시 요청(의존성 배열 역할)
queryFn: () => fetchTodos(status),
})- 오래된(stale) 쿼리는 창 포커스, 컴포넌트 마운트, 네트워크 재연결, 키 변경 때 백그라운드에서 다시 요청된다. 기준 시간은 staleTime과 gcTime
isLoading은 캐시가 비어 있는 첫 요청 중,isFetching은 캐시가 있어도 백그라운드 요청 중이면true다. 보여 줄 데이터가 없을 때 스피너는isLoading으로 판단한다data는 처음엔undefined다. 기본값을 주거나 useSuspenseQuery를 쓴다select로 캐시에서 필요한 부분만 꺼내 구독할 수 있다
바꾸기와 미리 채우기
useMutation: 서버 데이터를 바꾸는 요청.mutate(인자)로 호출하고 상태는isPending으로 본다. 기본으로 재시도하지 않는다. 성공 후invalidateQueries로 관련 쿼리를 다시 가져오거나, 낙관적으로(Optimistic Update) 캐시를 먼저 고친다- 캐시에 넣는 방법:
prefetchQuery(서버에서 미리 가져와 캐시에 넣음),setQueryData(클라이언트 값을 캐시에 넣음),initialData(캐시에 넣음),placeholderData(캐시에 넣지 않고 보여 주기만 함) - 무한 스크롤(Infinite Scroll)은
useInfiniteQuery(getNextPageParam,hasNextPage,fetchNextPage). 목록이 길면 리스트 가상화과 함께 쓴다 - 에러는 반환값의
error, Error Boundary,QueryCache의 전역onError로 처리한다 - 테스트에서는 테스트마다 새
QueryClient를 만들고retry: false를 준다
v4에서 v5로
2026-10 기준 React용(@tanstack/react-query)과 코어의 최신 메이저는 v5(5.104)다. v5에서 바뀐 큰 것들은 다음과 같다.
- 객체 시그니처 하나만:
useQuery(['todos'], fn, opts)같은 위치 인자 형태가 사라지고useQuery({ queryKey, queryFn, ...opts })만 남았다.queryClient의 메서드(invalidateQueries({ queryKey })등)도 같다 - 쿼리 콜백 제거:
useQuery의onSuccess·onError·onSettled가 없어졌다. 데이터에 맞춰 다른 state를 맞추는 용도라면 렌더 중 파생하고, 에러 알림은QueryCache의 전역 콜백으로 옮긴다. mutation의 콜백은 그대로 있다 isLoading→isPending: 상태 이름loading이pending(아직 데이터 없음)이 됐다. 새isLoading은isPending && isFetching, 즉 예전isInitialLoading의 뜻이다cacheTime→gcTime: 이름만 바뀌었다(staleTime과 gcTime)keepPreviousData→placeholderData: keepPreviousData(isPlaceholderData),useErrorBoundary→throwOnError,Hydrate→HydrationBoundary,useInfiniteQuery는initialPageParam필수- React 18 이상이 필요하다(
useSyncExternalStore사용)
mutation 콜백은 실행 순서대로 값을 받는다. 5.104 기준 onSuccess(data, variables, onMutateResult, context)다. variables는 mutate(인자)에 넘긴 값 그대로라 "방금 저장한 폼의 날짜로 목록 이동"처럼 응답에 없는 입력값을 쓸 때 편하다. 세 번째는 onMutate가 반환한 값(낙관적 업데이트 롤백용, 예전 이름 context)이고, 네 번째 context에는 client·meta·mutationKey가 들어 있다.
서버 렌더에서 useQuery가 무엇을 실행하는지, 서버 gcTime이 메모리에 주는 영향은 서버 렌더에서 훅이 하는 일.
키 설계는 쿼리 키 설계와 키 팩토리. 서버 쪽 캐시와 층이 어떻게 다른지는 LRU 캐시와 캐시 계층 비교, 브라우저 HTTP(HyperText Transfer Protocol) 캐시와의 차이는 브라우저 캐시.
출처: TkDodo - Practical React Query · TanStack Query - Migrating to v5