본문 바로가기

개발 공부/React

TanStack Query

TanStack Query

TanStack Query는 React에서 서버 데이터를 불러오고, 캐싱하고, 갱신하며, 상태를 관리하는 데 특화된 비동기 상태 관리 도구입니다.

즉, 서버 상태(Server State)를 클라이언트에서 최적으로 다룰 수 있게 해줍니다.

 

 

TanStack Query가 다루는 개념

개념  설명
Query 데이터를 가져오는 작업 (주로 GET)
Mutation 데이터를 변경하는 작업 (POST, PUT, DELETE 등)
QueryClient 쿼리 캐시, 무효화, 프리패치 등을 통제하는 중앙 컨트롤러
Query Key 쿼리를 식별하는 고유 키. 캐시와 재요청의 기준
Stale Time 데이터를 “신선한 상태”로 간주하는 시간
Cache Time 사용되지 않은 쿼리를 캐시에 유지하는 시간
Invalidation 데이터를 변경한 후 관련 쿼리를 강제로 refetch하게 만드는 메커니즘

 

 

주요 특징

기능  설명
캐싱 (Caching) 동일한 요청은 캐시에서 불러옴 (서버 트래픽 절감)
리페칭 (Refetching) 포커스 변경 시, 네트워크 다시 연결 시 자동으로 최신화
상태관리 자동화 isLoading, error, data 자동 제공
쿼리 무효화 (Invalidation) POST, PUT, DELETE 후 해당 쿼리 자동 새로고침 가능
Devtools 지원 상태를 시각적으로 디버깅 가능

 

 


설치 및 기본 세팅

npm install @tanstack/react-query
// main.tsx 또는 App.tsx 상위에 Provider 설정
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

const App = () => (
  <QueryClientProvider client={queryClient}>
    <MyApp />
  </QueryClientProvider>
);

 

 

기본 사용 예시 (GET 요청)

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

const fetchUsers = async () => {
  const res = await fetch('/api/users');
  if (!res.ok) throw new Error('사용자 정보를 불러오지 못했습니다');
  return res.json();
};

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

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

  return (
    <ul>
      {data.map(user => (
        <li key={user.id}>{user.name} ({user.email})</li>
      ))}
    </ul>
  );
}
  • queryKey: 쿼리의 고유 키. 캐시를 이 키로 식별합니다.
  • queryFn: 실제 데이터를 가져오는 함수
  • data, isLoading, error: 상태 자동 관리

 


 

예제: 유저 목록 + 생성 기능

  1. 유저 목록을 불러온다.
  2. 새로운 유저를 추가한다.
  3. 추가 후 유저 목록을 자동 갱신한다.

API 예시

  • GET /api/users
  • POST /api/users (JSON body: { name, email })

 

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

const fetchUsers = async () => {
  const res = await fetch('/api/users');
  if (!res.ok) throw new Error('유저 불러오기 실패');
  return res.json();
};

const addUser = async (newUser: { name: string; email: string }) => {
  const res = await fetch('/api/users', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(newUser),
  });
  if (!res.ok) throw new Error('유저 추가 실패');
  return res.json();
};

export function UserDashboard() {
  const queryClient = useQueryClient();
  const { data: users, isLoading } = useQuery({ queryKey: ['users'], queryFn: fetchUsers });

  const mutation = useMutation({
    mutationFn: addUser,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users'] });
    },
  });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    const form = e.target as HTMLFormElement;
    const name = (form.elements.namedItem('name') as HTMLInputElement).value;
    const email = (form.elements.namedItem('email') as HTMLInputElement).value;
    mutation.mutate({ name, email });
    form.reset();
  };

  return (
    <div>
      <h2>👥 유저 목록</h2>
      {isLoading ? <p>로딩 중...</p> : (
        <ul>
          {users.map((user: any) => <li key={user.id}>{user.name} - {user.email}</li>)}
        </ul>
      )}
      <form onSubmit={handleSubmit}>
        <input name="name" placeholder="이름" required />
        <input name="email" placeholder="이메일" required />
        <button type="submit">유저 추가</button>
      </form>
    </div>
  );
}
  • useQuery: 데이터를 불러오는 (GET) 쿼리용 훅
  • useMutation: 데이터를 변경하는 (POST/PUT/DELETE) 훅
  • useQueryClient: 쿼리 클라이언트를 조작할 수 있는 훅 (ex. invalidateQueries)
  • /api/users로 GET 요청을 보냄
  • 응답이 실패하면 에러를 던짐 (이건 useQuery의 error로 catch됨)
  • ['users']라는 쿼리 키를 기준으로 캐싱됨
  • fetchUsers 함수가 실행되어 유저 데이터를 가져옴
  • isLoading은 데이터를 아직 받아오지 못했을 때 true
  • mutationFn: addUser를 실행해 서버에 새 유저 추가
  • onSuccess: 추가 성공 시, ['users'] 키의 쿼리를 무효화 (invalidate) → 자동 refetch됨 → 최신 목록 표시됨

 

'개발 공부 > React' 카테고리의 다른 글

React Recoil  (3) 2025.08.16
React Query, Redux Toolkit Query  (0) 2025.06.22
Next.js - 동적 라우팅  (0) 2025.05.14
Next.js - layout.tsx, children  (0) 2025.05.11
Next.js- cookies().get(), middleware 로그인 유지  (0) 2025.05.09