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: 상태 자동 관리
예제: 유저 목록 + 생성 기능
- 유저 목록을 불러온다.
- 새로운 유저를 추가한다.
- 추가 후 유저 목록을 자동 갱신한다.
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 |