반응형
소개
React Query는 React 애플리케이션에서 서버 상태를 관리하기 위한 강력한 도구입니다. 이번 글에서는 QueryClient
의 핵심 기능과 활용 방법을 자세히 알아보겠습니다.
설치
# npm을 사용하는 경우
npm install @tanstack/react-query
# yarn을 사용하는 경우
yarn add @tanstack/react-query
# pnpm을 사용하는 경우
pnpm add @tanstack/react-query
QueryClient 기본 설정
import { QueryClient } from '@tanstack/react-query';
// 기본 QueryClient 생성
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5분
gcTime : 10 * 60 * 1000, // 10분 (v5는 gcTime, v4는 cacheTime)
retry: 3,
refetchOnWindowFocus: true,
},
},
});
기본 옵션 설명
옵션 | 설명 | 기본값 |
---|---|---|
staleTime | 데이터가 신선하다고 간주되는 시간 | 0 |
cacheTime | 데이터가 캐시에 유지되는 시간 | 5분 |
retry | 실패 시 재시도 횟수 | 3 |
refetchOnWindowFocus | 창 포커스 시 재조회 여부 | true |
예시
기본적인 쿼리 설정
// api/userApi.ts
interface User {
id: number;
name: string;
email: string;
}
// 쿼리 키 타입 정의
export const userKeys = {
all: ['users'] as const,
lists: () => [...userKeys.all, 'list'] as const,
list: (filters: string) => [...userKeys.lists(), { filters }] as const,
details: () => [...userKeys.all, 'detail'] as const,
detail: (id: number) => [...userKeys.details(), id] as const,
};
// hooks/useUsers.ts
export const useUsers = (filters?: string) => {
return useQuery({
queryKey: userKeys.list(filters),
queryFn: () => fetchUsers(filters),
select: (data) => {
return data.sort((a, b) => b.id - a.id);
},
});
};
데이터 수동 관리
// 데이터 직접 설정
queryClient.setQueryData(userKeys.detail(1), {
id: 1,
name: "John",
email: "john@example.com"
});
// 데이터 업데이트
queryClient.setQueryData(userKeys.detail(1), (old: User) => ({
...old,
name: "John Doe"
}));
캐시 무효화
// 특정 쿼리 무효화
const invalidateUser = async (userId: number) => {
await queryClient.invalidateQueries({
queryKey: userKeys.detail(userId)
});
};
// 전체 사용자 쿼리 무효화
const invalidateAllUsers = async () => {
await queryClient.invalidateQueries({
queryKey: userKeys.all
});
};
낙관적 업데이트
const useUpdateUser = () => {
return useMutation({
mutationFn: updateUser,
onMutate: async (newUser) => {
// 이전 쿼리 취소
await queryClient.cancelQueries({
queryKey: userKeys.detail(newUser.id)
});
// 이전 데이터 저장
const previousUser = queryClient.getQueryData(userKeys.detail(newUser.id));
// 낙관적 업데이트
queryClient.setQueryData(userKeys.detail(newUser.id), newUser);
return { previousUser };
},
onError: (err, newUser, context) => {
// 에러 발생 시 롤백
queryClient.setQueryData(
userKeys.detail(newUser.id),
context.previousUser
);
},
onSettled: (newUser) => {
// 쿼리 무효화
queryClient.invalidateQueries({
queryKey: userKeys.detail(newUser.id)
});
},
});
};
페이지네이션 처리
export const usePaginatedUsers = (page: number) => {
return useInfiniteQuery({
queryKey: ['users', 'infinite'],
queryFn: ({ pageParam = 1 }) => fetchUserPage(pageParam),
getNextPageParam: (lastPage) => lastPage.nextPage,
getPreviousPageParam: (firstPage) => firstPage.prevPage,
select: (data) => ({
pages: data.pages,
pageParams: data.pageParams,
totalUsers: data.pages.reduce((acc, page) => acc + page.users.length, 0),
}),
});
};
실시간 데이터 동기화
// 웹소켓 연결과 함께 사용
useEffect(() => {
const ws = new WebSocket('ws://api.example.com');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// 실시간 데이터로 캐시 업데이트
queryClient.setQueryData(userKeys.detail(data.userId), (old: User) => ({
...old,
...data
}));
};
return () => ws.close();
}, []);
전역 에러 처리
const queryClient = new QueryClient({
defaultOptions: {
queries: {
onError: (error: Error) => {
// 전역 에러 처리
if (error.message === 'Unauthorized') {
// 로그아웃 처리
logout();
}
},
},
},
});
QueryClient 고급 기능
캐시 지속성
import { persistQueryClient } from '@tanstack/react-query-persist-client';
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister';
const persister = createSyncStoragePersister({
storage: window.localStorage,
});
persistQueryClient({
queryClient,
persister,
maxAge: 1000 * 60 * 60 * 24, // 24시간
});
커스텀 캐시 시간
// 동적 캐시 시간 설정
const useCustomQuery = (id: string) => {
return useQuery({
queryKey: ['custom', id],
queryFn: () => fetchData(id),
staleTime: id === 'important' ? Infinity : 1000 * 60,
gcTime: id === 'important' ? Infinity : 1000 * 60 * 5,
});
};
성능 최적화
선택적 데이터 업데이트
// 특정 조건에 맞는 데이터만 업데이트
queryClient.setQueriesData(
{ queryKey: userKeys.all, exact: false },
(old: User[]) => old?.map(user =>
user.id === updatedUser.id ? updatedUser : user
)
);
데이터 프리패칭
// 데이터 미리 로드
const prefetchUser = async (userId: number) => {
await queryClient.prefetchQuery({
queryKey: userKeys.detail(userId),
queryFn: () => fetchUser(userId),
});
};
마치며
QueryClient는 React Query의 핵심 기능을 제어하는 중요한 도구입니다. 적절한 설정과 활용을 통해 효율적인 상태 관리와 최적화된 사용자 경험을 제공할 수 있습니다.
'TypeScript' 카테고리의 다른 글
[React Query] useMutation과 useQuery (0) | 2024.12.24 |
---|---|
[React] Zustand로 배우는 간단하고 강력한 상태 관리 (0) | 2024.12.20 |
프론트엔드와 백엔드 간 Enum 동기화 (0) | 2024.12.07 |
React 환경 변수 : .env (1) | 2024.12.05 |
회원가입 폼 쉽게 만들기 : react-hook-form (1) | 2024.11.22 |