[React] TanStack Query (구 React Query)

2025. 4. 22. 22:59·JavaScript/React

React로 앱을 만들다 보면 가장 먼저 마주치는 실무 과제는 “서버에서 데이터를 어떻게 가져올 것인가?”입니다. 그동안은 useEffect + axios 조합이 사실상 표준처럼 사용되었지만, 이 방식은 캐싱, 리페칭, 에러 처리, 상태 관리 등에서 반복되는 코드와 복잡한 로직을 야기합니다.

이 문제를 단 한 줄로 해결하는 라이브러리가 있으니, 그것이 바로 TanStack Query (구 React Query) 입니다.

TanStack Query란?

TanStack Query는 데이터 패칭(fetching), 캐싱(caching), 동기화(syncing), 업데이트(updating)를 위한 강력한 비동기 상태 관리 라이브러리입니다. 초기에는 React Query라는 이름으로 시작되었고, 현재는 Vue, Svelte, Solid 등 다양한 프레임워크를 지원하면서 TanStack Query라는 이름으로 통합되었습니다.

공식 사이트: https://tanstack.com/query

주요 특징

  • 자동 캐싱: 동일한 쿼리는 자동으로 메모이제이션 처리
  • 자동 리페칭: 포커스 전환 시 자동 데이터 리프레시
  • 로딩, 에러, 성공 상태 자동 관리
  • Query Key 기반 관리
  • 서버 상태를 클라이언트처럼 다룸
  • 강력한 개발자 도구 제공

기본 사용법

설치

npm install @tanstack/react-query

QueryClient 설정

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <YourComponent />
    </QueryClientProvider>
  );
}

데이터 패칭하기

import { useQuery } from '@tanstack/react-query';
import axios from 'axios';

const fetchUsers = async () => {
  const { data } = await axios.get('/api/users');
  return data;
};

function Users() {
  const { data, isLoading, isError, error } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
  });

  if (isLoading) return <p>로딩 중...</p>;
  if (isError) return <p>에러: {error.message}</p>;

  return (
    <ul>
      {data.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

쿼리의 라이프사이클

  • isLoading: 첫 요청 중
  • isFetching: 백그라운드 리패칭 중 (캐시된 상태 유지)
  • isSuccess, isError: 결과 상태
  • staleTime: 캐시된 데이터가 유효하다고 여기는 시간
  • cacheTime: 쿼리가 사라지고도 메모리에 남아있는 시간
useQuery({
  queryKey: ['data'],
  queryFn: fetchFn,
  staleTime: 1000 * 60, // 1분 동안은 리패칭하지 않음
  cacheTime: 1000 * 300, // 5분간 메모리에 보관
});

Mutation과 비동기 작업

Mutation은 데이터를 “변경”하는 데 사용됩니다.

import { useMutation, useQueryClient } from '@tanstack/react-query';

function AddUser() {
  const queryClient = useQueryClient();

  const mutation = useMutation({
    mutationFn: (newUser) => axios.post('/api/users', newUser),
    onSuccess: () => {
      queryClient.invalidateQueries(['users']);
    },
  });

  return (
    <button
      onClick={() => mutation.mutate({ name: 'New User' })}
    >
      유저 추가
    </button>
  );
}

캐시와 상태 관리

React Query는 서버 상태를 자체 캐시에 보관합니다. 이 캐시는 React의 로컬 상태와 다르게 시간 기반으로 갱신되고 백그라운드에서 업데이트됩니다. 이를 통해 전역 상태 관리 툴 없이도 대부분의 서버 상태를 처리할 수 있습니다.

Query Invalidation & Refetch

invalidateQueries는 특정 쿼리를 "더 이상 신선하지 않다"고 표시해서 다음에 자동으로 refetch하게 만듭니다.

queryClient.invalidateQueries(['users']);

또는 명시적으로 다시 불러오기:

queryClient.refetchQueries(['users']);

병렬 쿼리 / 의존 쿼리

병렬 쿼리

const usersQuery = useQuery({ queryKey: ['users'], queryFn: fetchUsers });
const postsQuery = useQuery({ queryKey: ['posts'], queryFn: fetchPosts });

의존 쿼리

const userQuery = useQuery({ queryKey: ['user', userId], queryFn: fetchUser });

const detailsQuery = useQuery({
  queryKey: ['details', userQuery.data?.id],
  queryFn: fetchDetails,
  enabled: !!userQuery.data,
});

Prefetching & SSR

미리 불러오기

queryClient.prefetchQuery({
  queryKey: ['users'],
  queryFn: fetchUsers,
});

SSR (Next.js 예시)

export async function getServerSideProps() {
  const queryClient = new QueryClient();

  await queryClient.prefetchQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
  });

  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  };
}

Devtools로 디버깅

npm install @tanstack/react-query-devtools
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

<QueryClientProvider client={queryClient}>
  <App />
  <ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>

Devtools를 통해 각 쿼리의 상태, 캐시, 리패칭 여부를 실시간으로 확인할 수 있어 디버깅이 매우 편리합니다.

내부 동작 원리

  • QueryClient는 전체 상태를 전역으로 관리하는 객체입니다.
  • QueryObserver가 쿼리의 변경을 감지하고 리렌더링을 트리거합니다.
  • 쿼리는 키(queryKey)를 기준으로 고유 식별되고, 같은 키에 대해 중복 요청은 자동으로 병합됩니다.
  • staleTime과 cacheTime은 각각 쿼리의 신선도와 생존 기간을 결정합니다.

이러한 내부 구조 덕분에 TanStack Query는 비동기 데이터를 로컬 상태처럼 사용하면서도 서버의 진실된 상태를 유지할 수 있습니다.

결론

TanStack Query는 단순한 데이터 패칭 라이브러리가 아닙니다. 그것은 서버 상태를 다루는 새로운 철학입니다. 그동안 반복적으로 작성하던 데이터 요청 로직, 에러 처리, 리패칭 로직을 대폭 줄이면서도 성능은 훨씬 뛰어납니다.

초기 러닝 커브는 있을 수 있지만, 한 번 익숙해지면 애플리케이션 전체의 구조를 개선하고 유지보수성을 획기적으로 끌어올릴 수 있습니다.

 

728x90
'JavaScript/React' 카테고리의 다른 글
  • [React] Props 지옥에서 벗어나는 법
  • [React] Context API
  • [React] 로컬스토리지는 왜 문제를 일으킬까?
  • [React] 컴포넌트 라이프사이클(Component Lifecycle)
츄핑
츄핑
    250x250
  • 츄핑
    개발로그
    츄핑
  • 전체
    오늘
    어제
    • 분류 전체보기
      • CS
        • 자료구조
        • 알고리즘
        • 운영체제
        • 네트워크
        • 데이터베이스
        • 인프라
        • Web
      • PS
        • 백준
      • JavaScript
        • React
        • Express
        • NestJS
        • TypeScript
        • Node.js
        • Electron
      • Java
        • Spring
      • Dart
        • Flutter
      • PHP
        • CodeIgniter
      • etc
        • 이산수학
        • 선형대수학
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    티스토리챌린지
    오블완
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
츄핑
[React] TanStack Query (구 React Query)
상단으로

티스토리툴바