TanStack Query의 모든 것

여러 프로젝트를 진행하면서 다양한 기술과 도구들을 사용해왔는데, 그중에서도 특히 서버 상태 관리에 큰 도움을 주는 TanStack Query에 대해 정리해보려 합니다.
Aug 30, 2025
TanStack Query의 모든 것

1. 소개

안녕하세요, Plaid DX팀에서 프론트엔드 리드를 맡고 있는 김충영입니다.
여러 프로젝트를 진행하면서 다양한 기술과 도구들을 사용해왔는데, 그중에서도 특히 서버 상태 관리에 큰 도움을 주는 TanStack Query에 대해 정리해보려 합니다.
개인적으로는 앞으로 어떤 프론트엔드 작업을 하더라도 이 라이브러리를 쓰지 않고 완성하기는 쉽지 않을 거라고 생각합니다. 그만큼 개발 과정에서 주는 이점이 너무너무 크기 때문인데요, 이번 글에서는 TanStack Query를 왜 사용하는지, 어떤 훅(hook)들이 있는지, 그리고 세부적인 옵션들은 무엇이 있고 어떤 상황에서 사용하는지를 중심으로 이야기를 풀어나가겠습니다.
저 역시 이 라이브러리를 쓰면서 자주 되돌아보게 되거나, 가끔은 까먹었던 부분을 다시 정리해야 하는 경우가 많았습니다. 또 새로운 사실을 알게 될 때마다 기록해두고 싶다는 생각도 있었고요. 그런 이유로 제 Plaid의 블로그의 첫 글 주제를 TanStack Query로 선택하게 됐습니다 🥹. 재밌게 읽어주세용 ㅎㅎ
 

2. TanStack Query란?

notion image

2.1. 클라이언트 상태 vs 서버 상태

프론트엔드 애플리케이션에서 다루는 상태(state)는 크게 두 가지로 나눌 수 있습니다.
바로 클라이언트 상태(Client State)와 서버 상태(Server State)입니다.
  • 클라이언트 상태는 브라우저 안에서만 존재하는 상태로, 사용자의 입력값이나 UI 컴포넌트 간의 토글 상태 같은 것들을 의미합니다. 예를 들어 다크 모드 여부나 모달창의 열림/닫힘 상태 등이 여기에 해당하죠.
  • 서버 상태는 서버와 네트워크를 통해 주고받는 데이터로, API 요청 결과나 DB에 저장된 정보를 불러오는 과정에서 관리하게 됩니다. 서버와 동기화되어야 하고, 캐싱·리페치(refetch)·에러 처리 등이 따라오기 때문에 단순히 useState로는 관리하기가 어렵습니다.

2.2. React Query → TanStack Query로의 확장

이때 이런 서버 상태 관리를 돕기 위해 등장한 라이브러리가 바로 React Query입니다.
이름에서 알 수 있듯이 처음에는 React에 특화된 라이브러리였는데요, 프론트엔드 생태계 전반에서 서버 상태 관리에 대한 수요가 커지면서, Vue·Svelte·Solid 같은 다양한 프레임워크에서도 활용할 수 있도록 확장되었습니다.
그 과정에서 이름도 TanStack Query로 리브랜딩되어 지금은 특정 프레임워크에 국한되지 않는 범용적인 도구로 자리잡았습니다.
 

3. 핵심 개념

TanStack Query를 이해하기 위해서는 먼저 Query, Mutation, QueryClient, Devtools 네 가지 개념을 짚고 넘어가는 게 좋습니다.

3.1. Query

Query는 데이터를 가져오고 캐싱하는 단위를 말합니다. API 호출을 useQuery 를 통해서 수행하면, Tanstack query가 내부적으로 데이터를 캐싱하고, 동일한 요청은 네트워크를 중복 호출하지 않도록 관리합니다.
(보통 GET 요청에서 사용합니다)
⇒ 즉, Query는 서버 상태를 불러오는 기본 단위라고 할 수 있습니다.
// 기본적인 예시입니다. const { data, isLoading, error } = useQuery({ queryKey: ['todos'], // 캐싱을 위한 key queryFn: () => getTodos(), // 실제 데이터를 불러오는 함수 });

3.2. Mutation

Mutation은 서버의 데이터를 변경하는 동작(POST, PUT, DELETE 등)을 다루는 개념입니다.
데이터를 수정하면 자동으로 관련 Query를 무효화(invalidate)하여 최신 데이터를 다시 가져오도록 할 수 있습니다.
const mutation = useMutation({ mutationFn: (newTodo) => createTodo(newTodo), onSuccess: () => { queryClient.invalidateQueries(['todos']); // 관련 쿼리 새로고침 }, }); // 사용 예시 mutation.mutate({ title: '새 할 일' });

3.3. QueryClient

QueryClient는 TanStack Query의 중심이 되는 객체로, 모든 Query와 Mutation의 캐싱, 상태 관리, 무효화 전략 등을 총괄합니다. 보통 앱 최상단에서 QueryClientProvider로 감싸서 전역에서 사용할 수 있도록 설정합니다. queryClient를 직접 사용해 수동으로 데이터를 무효화하거나, 캐시를 업데이트하는 패턴도 실무에서 자주 활용합니다. 특히 mutation을 사용하고 다른 쿼리를 무효화해서 클라이언트 측에서 모두 동일한 데이터를 볼 수 있도록 해주는 중요한 기술 입니다..!
(아래 사진은 Plaid의 프로젝트 중 하나인 리더스클럽 FE의 예시입니다)
notion image

3.4. Devtools

TanStack Query Devtools는 Query 상태를 시각적으로 확인할 수 있는 개발자 도구입니다.
notion image
  • 현재 캐시된 데이터
  • 각 Query의 상태 (loading, error, success)
  • 자동 리페치가 일어나는 타이밍
위와같은 것들을 실시간으로 확인할 수 있어, 디버깅과 튜닝에 많은 도움이 됩니다.

4. 기본 사용법

다음으로 기본적인 tanstackQuery에서 가장 많이 사용하는 훅들을 알아보겠습니다. 아래 4가지 훅이 가장 보편적으로 사용되는 훅들이라고 생각하시면 됩니다. 간단한 사용법과, 언제 사용하는지, 어떻게 사용하는지, 디테일한 옵션들은 무엇이 있는지에 대해 알아보겠습니다.

4.1. useQuery

useQuery는 TanStack Query에서 서버 데이터를 조회(GET)할 때 사용하는 가장 기본적이고 핵심적인 훅입니다. 데이터의 요청, 캐싱, 동기화를 자동으로 관리해주어 서버 상태를 편리하게 다룰 수 있게 해줍니다.
 
핵심 사용 패턴: Custom Hook으로 감싸기
useQuery를 사용할 때는 아래와 같이 기능에 따라 커스텀 훅으로 감싸는 것이 좋습니다. 이 패턴을 통해 데이터 로직을 한곳에서 관리하여 재사용성을 높이고 코드를 깔끔하게 유지할 수 있습니다.
이때 커스텀 훅의 유연성을 확보하는 것이 중요한데, options 파라미터를 활용하면 됩니다.
  • 공통 규칙: 모든 컴포넌트에서 동일하게 적용되어야 할 queryKey, queryFn 등은 훅 내부에 정의합니다.
  • 예외 허용: 특정 컴포넌트에서만 필요한 옵션(e.g., staleTime, enabled)은 options 파라미터로 받아와 적용할 수 있게 열어둡니다.

아래는 useQuery의 사용 예시입니다.
import { useQuery, UseQueryOptions } from '@tanstack/react-query'; // Post 타입 정의 type Post = { id: number; title: string; }; type UsePostsQueryOptions = Omit<UseQueryOptions<Post[]>, 'queryKey' | 'queryFn'>; // 커스텀 훅이 options를 인자로 받도록 설계 export const usePostsQuery = (options?: UsePostsQueryOptions) => { return useQuery({ // 필수 옵션 queryKey: ['posts'], queryFn: fetchPosts, // 추가 옵션 ...options, }); }; // --- 컴포넌트에서의 사용 --- // 일반적인 사용 (기본 staleTime 5분이 적용됨) const { data } = usePostsQuery(); // 특정 페이지에서 staleTime을 10분으로 늘리고 싶을 때 (staleTime은 기본적으로 5분) const { data: specialPosts } = usePostsQuery({ staleTime: 1000 * 60 * 10, }); // 특정 조건이 만족될 때만 쿼리를 실행하고 싶을 때 const { data: conditionalPosts } = usePostsQuery({ enabled: someCondition === true, });
v5 주요 변경점: v5부터 useQuery에서는 onSuccess, onError, onSettled 콜백 옵션이 제거되었습니다. 데이터 조회 성공/실패에 따른 부수 효과(Side Effect)는 useEffect 훅을 사용하거나, useMutation을 통해 처리하는 것이 권장됩니다.

4.2. useSuspense

useSuspenseQueryuseQuery의 변형 버전으로, React 18의 핵심 기능인 Suspense 및 Error Boundary와 완벽하게 통합되도록 설계된 훅입니다.
기존의 useQuery와의 가장 큰 차이점은 로딩 및 에러 상태를 직접 반환하지 않는다는 점입니다.
대신,
  • 데이터를 가져오는 중일 때는 컴포넌트 렌더링을 일시 중단(Suspend)시키고,
  • 에러가 발생하면 에러를 던져서(Throw) 가장 가까운 Error Boundary에서 처리하도록 합니다.
이를 통해 로딩 및 에러 처리를 컴포넌트 내부가 아닌, 상위 컴포넌트 트리에서 선언적으로 관리할 수 있습니다.

언제 사용하나요?

  • if (isPending) return <Spinner /> 와 같은 로딩 상태 분기문을 컴포넌트에서 제거하고 싶을 때
  • 여러 데이터 요청의 로딩 상태를 <Suspense>를 통해 한 번에 관리하고 싶을 때
  • 에러 처리를 <ErrorBoundary>를 통해 중앙에서 통합 관리하고 싶을 때
import { useSuspenseQuery, UseSuspenseQueryOptions } from '@tanstack/react-query'; type UseSuspensePostsQueryOptions = Omit<UseSuspenseQueryOptions<Post[]>, 'queryKey' | 'queryFn'>; export const useSuspensePostsQuery = (options?: UseSuspensePostsQueryOptions) => { return useSuspenseQuery({ // 공통 규칙: 고유 키와 데이터 fetcher queryKey: ['posts'], queryFn: fetchPosts, // 2. 예외 허용: 컴포넌트에서 받은 options 객체 ...options, }); };
 
이 훅을 사용하면 로딩과 에러 상태를 직접 다루지 않으므로 코드가 매우 간결해진다는 장점이 있습니다.
그대신 로딩 및 에러 UI의 책임은 상위의 Suspense와 ErrorBoundary로 위임됩니다.
  1. 데이터를 사용하는 컴포넌트
    1. import { useSuspensePostsQuery } from './hooks/useSuspensePostsQuery'; function PostList() { // isPending, isError 없이 바로 data를 구조분해 할당. // 데이터 로딩이 끝나지 않았으면 이 컴포넌트는 여기서 잠시 중단된다. const { data: posts } = useSuspensePostsQuery(); return ( <ul> {posts.map(post => ( <li key={post.id}>{post.title}</li> ))} </ul> ); }
  1. 컴포넌트를 감싸는 상위 컴포넌트
    1. 실제 로딩 및 에러 처리는 이 컴포넌트가 담당합니다. PostList가 중단되면 Suspense의 fallback UI가 대신 보여집니다. 에러가 발생하면 ErrorBoundary의 fallback UI가 보여집니다.
      import { Suspense } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; import PostList from './components/PostList'; import Spinner from './components/Spinner'; import ErrorMessage from './components/ErrorMessage'; function PostPage() { return ( <div> <h1>게시글 목록</h1> <ErrorBoundary fallback={<ErrorMessage />}> <Suspense fallback={<Spinner />}> <PostList /> </Suspense> </ErrorBoundary> </div> ); }
       
      그래서.. useQuery랑 useSuspenseQuery중 뭘 쓰나요?? 보통 전통적인 방식이면서 높은 자유도를 가진 useQuery를 사용합니다. isPending이나 isError 상태를 직접 다루는 것이 직관적이고, 컴포넌트 단위로 세밀한 UI 제어가 가능한 장점이 있습니다.
      하지만, Plaid에서 B2B Saas로 저희가 기획부터 디자인까지 담당해서 제공하는 프로젝트라면… useSuspenseQuery를 사용하는 것도 좋을 것 같습니다. React가 지향하는 선언적인 UI 작성 방식과 맞아 떨어지기도하고, 코드 가독성과 유지 보수성도 크게 향상되는 장점이 있는 것 같습니다.
      결론적으로… 일단 상세한 UI와 자유도가 높은 비지니스 로직이라면 useQuery를, 그 반대라면 useSuspenseQuery를 사용하는 것이 좋아보입니다.

4.3. useInfiniteQuery

드디어 useInfiniteQuery네요. useInfiniteQueryuseQuery를 확장한 훅으로, 페이지 기반의 데이터를 점진적으로 불러오는 UI(예: 무한 스크롤, 더 보기 버튼)를 구현하는 데 특화되어 있습니다.
사용자가 스크롤을 내리거나 버튼을 클릭하면, 기존 데이터는 유지한 채 다음 페이지의 데이터를 계속해서 목록에 추가해 나가는 기능을 굉장히 쉽게 구현해줍니다.
 
언제 사용하나요??
  • 인스타그램 피드나 페이스북 타임라인처럼 사용자가 아래로 스크롤할 때마다 새로운 콘텐츠를 계속 로드해야 할 때
  • "더 보기" 버튼을 눌러 다음 페이지의 게시물 목록을 이어 붙여 보여주고 싶을 때
  • 채팅 앱에서 이전 대화 내용을 위로 스크롤하여 불러올 때
 
핵심 사용 패턴: initialPageParamgetNextPageParam
useInfiniteQueryuseQuery의 옵션에 더해, 페이징을 관리하기 위한 두 가지 핵심 옵션이 추가됩니다.
  • initialPageParam: 가장 처음에 요청할 페이지 번호(또는 커서)를 지정합니다.
  • getNextPageParam: 마지막으로 성공한 API 응답 데이터를 기반으로, 다음에 요청할 페이지 번호가 무엇인지 계산하여 반환하는 함수입니다. 만약 더 이상 불러올 페이지가 없다면 undefinednull을 반환합니다.
 
아래는 예시 코드입니다.
import { useInfiniteQuery, UseInfiniteQueryOptions } from '@tanstack/react-query'; type Post = { id: number; title: string; }; type PaginatedPosts = { data: Post[]; nextPage?: number; }; type UseInfinitePostsQueryOptions = Omit< UseInfiniteQueryOptions<PaginatedPosts>, 'queryKey' | 'queryFn' | 'initialPageParam' | 'getNextPageParam' >; // --- 커스텀 훅 --- export const useInfinitePostsQuery = (options?: UseInfinitePostsQueryOptions) => { return useInfiniteQuery({ queryKey: ['posts', 'infinite'], queryFn: fetchPosts, // infinite query에서 추가되는 핵심적인 옵션 initialPageParam: 1, // 첫 페이지는 1번부터 시작 getNextPageParam: (lastPage) => { // lastPage: 가장 최근에 받아온 페이지 데이터 // lastPage.nextPage가 있으면 그 값을, 없으면 더 이상 페이지가 없다는 의미로 undefined 반환 return lastPage.nextPage; }, ...options, }); };
 
컴포넌트에서의 활용법
useInfiniteQuery는 데이터를 불러오기 위한 함수와 현재 상태를 알려주는 값들을 추가로 반환합니다.
  • data.pages: 지금까지 불러온 모든 페이지의 데이터가 배열 형태로 담겨 있습니다. ([ [page1_data], [page2_data], ... ])
  • fetchNextPage: 다음 페이지의 데이터를 요청하는 함수입니다.
  • hasNextPage: getNextPageParamundefined를 반환하지 않으면 true가 됩니다. 더 불러올 페이지가 남았는지 알려줍니다.
  • isFetchingNextPage: 다음 페이지 데이터를 불러오는 중일 때 true가 되는 로딩 상태입니다.
import { useInfinitePostsQuery } from './hooks/useInfinitePostsQuery'; import { Fragment } from 'react'; function InfinitePostList() { const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isPending, isError, } = useInfinitePostsQuery(); if (isPending) return <div>초기 데이터 로딩 중...</div>; if (isError) return <div>에러가 발생했습니다.</div>; return ( <div> {data.pages.map((page, i) => ( <Fragment key={i}> {page.data.map((post) => ( <div key={post.id} className="post"> {post.title} </div> ))} </Fragment> ))} <button onClick={() => fetchNextPage()} disabled={!hasNextPage || isFetchingNextPage} > {isFetchingNextPage ? '불러오는 중...' : hasNextPage ? '더 보기' : '마지막 페이지입니다'} </button> </div> ); }

4.4. useMutation

useMutation은 서버의 데이터를 생성(Create), 수정(Update), 삭제(Delete)할 때 사용하는 훅입니다. useQuery가 데이터를 읽어오는 역할이라면, useMutation은 데이터를 변경하는(Write) 모든 작업을 책임집니다.
 
핵심 사용 패턴: mutationFn과 생명주기 콜백
useMutation의 가장 중요한 옵션은 mutationFn과, 뮤테이션의 각 단계에서 부수 효과(Side Effect)를 실행할 수 있게 해주는 onSuccess, onError 같은 콜백 함수들입니다.
  • mutationFn: 필수 항목. 서버에 변경 요청을 보내는 비동기 함수입니다. mutate 함수가 호출될 때 전달받은 변수를 인자로 받습니다.
  • onSuccess: 뮤테이션이 성공했을 때 실행되는 콜백입니다. 가장 중요한 역할은 queryClient.invalidateQueries를 호출하여 관련된 쿼리 캐시를 무효화하는 것입니다. 이를 통해 데이터 변경 후 화면을 최신 상태로 자동 동기화할 수 있습니다.
  • onError: 뮤테이션이 실패했을 때 실행되는 콜백입니다. 사용자에게 에러 메시지를 보여주거나, 에러 로그를 수집하는 등의 작업을 처리합니다.
import { useMutation, useQueryClient, UseMutationOptions } from '@tanstack/react-query'; type Post = { id: number; title: string; }; type NewPost = { title: string; }; type UseAddPostMutationOptions = Omit< UseMutationOptions<Post, Error, NewPost>, 'mutationFn' >; export const useAddPostMutation = (options?: UseAddPostMutationOptions) => { const queryClient = useQueryClient(); return useMutation({ mutationFn: addPost, // 성공 처리 로직 onSuccess: () => { console.log('게시물 추가 성공!'); queryClient.invalidateQueries({ queryKey: ['posts'] }); }, // 에러 처리 로직 onError: (error) => { console.error('게시물 추가 실패:', error.message); }, ...options, }); };

5. TanStack Query 똑똑하게 사용하기 🧐

이 파트는 좀 더 디테일하게 읽어주셨으면 합니다! 앞으로 프로젝트에 적용할 내용이기 때문에, 틀린 부분이 있다면 언제든지 태클 걸어주셔도 됩니다.

5.1. 핵심원칙: 모든 Query와 Mutation을 커스텀 훅으로 감싸라

가장 중요한 원칙이라고 생각합니다. useQueryuseMuatation 을 컴포넌트에서 직접 사용하는 대신, 기능별로 커스텀 훅을 만들어 추상화하는 것입니다.
왜 이렇게 해야할까요? 🤔 1. 응집도 향상 및 단일 책임 원칙: 데이터 페칭 관련 로직이 하나의 훅에 캡슐화되어 코드가 훨씬 깔끔해집니다. 2. 재사용성: 여러 컴포넌트에서 동일한 데이터를 필요로 할 때, 이 훅을 호출하기만 하면 됩니다. 3. 유지보수 용이성: API 명세가 변경되거나, 캐시 전략을 수정해야 할 때 해당 커스텀 훅만 수정하면 되므로 변경의 영향 범위가 명확해집니다.

5.2. Query Key는 명확해야하고 동적으로 관리해라

이것도 정말!!! 중요한 원칙입니다. Query Key는 쿼리의 캐싱을 관리하는 핵심입니다. 서버의 상태를 유일하게 식별할 수 있는 값으로 만들어야 합니다.
  • 계층적으로 구성
    • ['todos', 'list', {status: 'done'}] 처럼 어떤 데이터인지 명확히 알 수 있게 만들어야 합니다.
  • 의존성 포함
    • 쿼리가 특정 변수에 의존한다면, 반드시 Query Key에 포함시켜야 해당 변수가 변경될 때 쿼리가 다시 실행됩니다.
// hooks/useTodoQuery.ts // todo 도메인과 관련된 QueryKey Factory const todoKeys = { all: ['todos'] as const, lists: () => [...todoKeys.all, 'list'] as const, list: (filters: string) => [...todoKeys.lists(), { filters }] as const, details: () => [...todoKeys.all, 'detail'] as const, detail: (id: number) => [...todoKeys.details(), id] as const, } // GET /todos?status={status} export function useTodoListQuery(filters: { status: string }) { return useQuery({ // filters 객체가 변경되면 queryKey가 바뀌어 refetch됨 (이 예시가 의존성의 예시) queryKey: todoKeys.list(filters.status), queryFn: () => fetchTodoList(filters), }); } // GET /todos/{id} export function useTodoDetailQuery(id: number) { return useQuery({ queryKey: todoKeys.detail(id), queryFn: () => fetchTodoById(id), enabled: !!id, // id 값이 있을 때만 쿼리 실행 }); }
Tip: 위 예시처럼 queryKeysFactory 를 만들어 사용하면, 우리들의 실수를 줄이고 mutation에서 invalidateQueries를 사용할 때 굉장히 편리합니다.
 

5.3. useMutation은 이렇게 사용해라

데이터를 변경(CUD - Create, Update, Delete)을 처리하는 useMutation 은 사용자 경험과 데이터 정합성에 직접적인 영향을 줍니다.
그래서 onSuccess에서 Query Invalidation은 필수입니다!!
데이터 변경이 성공했을 때, 관련된 useQuery 캐시를 무효화(invalidate)하여 자동으로 데이터를 다시 불러오게 만들어야합니다.
// hooks/useAddTodoMutation.ts export function useAddTodoMutation() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (newTodo: NewTodo) => addTodo(newTodo), // 성공 시 onSuccess 콜백 실행 onSuccess: () => { // 'todos'로 시작하는 모든 쿼리를 무효화 queryClient.invalidateQueries({ queryKey: todoKeys.all }); }, onError: (error) => { // 에러 처리 로직 (사용자에게 Toast 메시지 표시, 로깅 등등..) console.error("Todo 추가 실패:", error); } }); } // components/AddTodoForm.tsx function AddTodoForm() { const { mutate, isPending } = useAddTodoMutation(); const handleSubmit = (newTodo: NewTodo) => { mutate(newTodo); }; // ... }
 

🍯 UX를 극대화하는 Optimistic Updates(낙관적 업데이트)

사용자가 버튼을 클릭하는 즉시 UI가 성공한 것처럼 먼저 바뀌고, 백그라운드에서 실제 서버 요청을 보내는 기법입니다. 사용자는 서버 응답을 기다릴 필요가 없어 훨씬 빠르다고 느낍니다.
(Plaid에서는 리더스 클럽 푸시알림 설정에서 활용됐습니다..! )
 
아래 코드와 같이 useMutation의 onMutate, onError, onSettled를 활용해 구현합니다.
// hooks/useUpdateTodoMutation.ts export function useUpdateTodoMutation() { const queryClient = useQueryClient(); return useMutation({ mutationFn: updateTodo, // 1. Mutation이 시작되기 직전에 실행 onMutate: async (updatedTodo) => { // 진행 중인 refetch를 취소 (낙관적 업데이트를 덮어쓰지 않도록) await queryClient.cancelQueries({ queryKey: todoKeys.lists() }); // 이전 데이터 스냅샷 const previousTodos = queryClient.getQueryData<Todo[]>(todoKeys.lists()); // 새로운 값으로 캐시를 낙관적으로 업데이트 queryClient.setQueryData(todoKeys.lists(), (old) => old?.map(todo => todo.id === updatedTodo.id ? updatedTodo : todo) ); // context에 이전 데이터 스냅샷을 반환 return { previousTodos }; }, // 2. Mutation 실패 시 실행 onError: (err, updatedTodo, context) => { // onMutate에서 반환된 context를 사용해 데이터 롤백 if (context?.previousTodos) { queryClient.setQueryData(todoKeys.lists(), context.previousTodos); } // 에러 UI 처리 }, // 3. 성공/실패 여부와 관계없이 Mutation 완료 후 항상 실행 onSettled: () => { // 최신 서버 데이터와 동기화 queryClient.invalidateQueries({ queryKey: todoKeys.lists() }); }, }); }
 

마무리

지금까지 TanStack Query와 함께한 저의 첫 기술 블로그 여정을 마무리하려 합니다. 리액트 생태계에서 서버 상태 관리를 논할 때 빼놓을 수 없는, 어쩌면 가장 중요한 라이브러리라는 생각에 첫 글의 주제로 선정해 보았습니다.
사실 이 글은 Plaid를 위한 것이기도 하지만, 저 자신을 위한 기록이기도 합니다. TanStack Query의 사용에 익숙해지다가도 종종 기본으로 돌아가 개념을 다시 정리해야 할 때가 있었고, 새로운 패턴을 발견할 때마다 잊지 않도록 정리해두고 싶다는 생각을 늘 해왔습니다. 그런 의미에서 제 첫 글을 TanStack Query로 쓰게 된 것은 좋은 선택이었던 것 같습니다.
이번 글에서는 TanStack Query가 나오게 된 배경부터 각종 다양한 꿀팁까지 이야기를 풀어보았습니다. 다음 포스트에서는 오늘 다루지 못한 여러 디테일한 옵션들, 쿼리 키 팩토리, queryClient를 활용한 전역 상태 관리, 그리고 더욱 정교한 캐싱 전략 등등 여러 내용을 추가해보도록 하겠습니다.
첫 글이라 부족한 점이 많았을지 모르지만, 이 글이 TanStack Query를 처음 접하시거나 다시 한번 개념을 다지고 싶은 분들께 작은 도움이 되었으면 합니다. 긴 글 읽어주셔서 진심으로 감사합니다. 😊
 
Share article

플래드