
서버 상태 관리, 이거 하나로 끝낸다.
실무에서 바로 쓰는 예제 & 개념 총정리
TanStack Query (구 React Query)는 API 요청, 캐싱, 리페칭, 로딩/에러 상태 관리 등을 자동화해주는
서버 상태 관리 라이브러리입니다.
useState, useEffect로 비동기 처리하지 마세요.
React Query가 훨씬 깔끔하게 해줍니다!
npm install @tanstack/react-query
npm install @tanstack/react-query-devtools
// App.tsx or main.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
type User = {
id: number;
name: string;
};
const fetchUsers = async (): Promise<User[]> => {
const res = await fetch('/api/users');
if (!res.ok) throw new Error('Network error');
return res.json();
};
import { useQuery } from '@tanstack/react-query';
const Users = () => {
const { data, isLoading, isError, error } = useQuery<User[]>({
queryKey: ['users'],
queryFn: fetchUsers,
});
if (isLoading) return <p>Loading...</p>;
if (isError) return <p>Error: {(error as Error).message}</p>;
return (
<ul>
{data?.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
};
| 옵션 | 설명 |
|---|---|
queryKey | 캐싱 키 |
queryFn | API fetch 함수 |
enabled | 조건부 쿼리 실행 |
staleTime | 데이터 신선도 유효 시간 |
refetchOnWindowFocus | 창 포커스 시 재요청 여부 |
select | 응답 데이터 가공 (ex. 날짜 포맷) |
import { useMutation } from '@tanstack/react-query';
const createUser = async (user: User) => {
const res = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify(user),
headers: { 'Content-Type': 'application/json' },
});
return res.json();
};
const mutation = useMutation({
mutationFn: createUser,
onSuccess: () => {
queryClient.invalidateQueries(['users']);
},
});
const mutation = useMutation({
mutationFn: createUser,
onMutate: async (newUser: User) => {
await queryClient.cancelQueries(['users']);
const prevUsers = queryClient.getQueryData<User[]>(['users']);
queryClient.setQueryData(['users'], (old = []) => [...old, newUser]);
return { prevUsers };
},
onError: (_err, _newUser, context) => {
queryClient.setQueryData(['users'], context?.prevUsers);
},
onSettled: () => {
queryClient.invalidateQueries(['users']);
},
});
<button onClick={() => mutation.mutate({ id: Date.now(), name: 'Joseph' })}>
Add User
</button>
const fetchPosts = async ({ pageParam = 1 }): Promise<Post[]> => {
const res = await fetch(`/api/posts?page=${pageParam}`);
return res.json();
};
import { useInfiniteQuery } from '@tanstack/react-query';
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
queryKey: ['posts'],
queryFn: fetchPosts,
getNextPageParam: (lastPage, allPages) => {
return lastPage.length === 10 ? allPages.length + 1 : undefined;
},
});
{data?.pages.map((page, i) => (
<div key={i}>
{page.map(post => <p key={post.id}>{post.title}</p>)}
</div>
))}
{hasNextPage && (
<button onClick={() => fetchNextPage()}>Load More</button>
)}
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
<ReactQueryDevtools initialIsOpen={false} />
쿼리 상태를 시각화해서 보는 디버깅 도구, 실무 필수!
queryKey는 배열로 구조화: ['user', userId]enabled: !!idselect로 데이터 포맷 가공useInfiniteQuery는 무한 스크롤 필수React Query는 서버 데이터를 "자동으로, 안정적으로" 다룰 수 있게 도와줍니다.
TypeScript와 함께 쓰면 더욱 강력하고 안전한 코드 작성이 가능해요.
프론트엔드라면 꼭 알아야 할 필수 라이브러리입니다!