React Query 완벽 가이드: useQuery vs fetchQuery vs useMutation + 캐시 전략
1. useQuery
사용 시기
- 컴포넌트에서 데이터를 실시간으로 표시할 때
- 자동 리프레시나 폴링이 필요할 때
- 캐시된 데이터를 재사용할 때
- 데이터의 로딩/에러 상태를 UI에 표시해야 할 때
코드 예시
// 기본 사용
const { data, isLoading, error } = useQuery({
queryKey: \['posts'\],
queryFn: fetchPosts
});
// 자동 리프레시
const { data } = useQuery({
queryKey: \['notifications'\],
queryFn: fetchNotifications,
refetchInterval: 1000 \* 60 // 1분마다 갱신
});
// 조건부 실행
const { data } = useQuery({
queryKey: \['post', postId\],
queryFn: () => fetchPost(postId),
enabled: !!postId,
staleTime: 1000 \* 60, // 1분 동안 신선한 상태 유지
cacheTime: 1000 \* 60 \* 5 // 5분 동안 캐시 유지
});
2. fetchQuery
사용 시기
- 즉시 데이터가 필요한 경우
- React Router의 loader/action 내부
- 컴포넌트 외부에서 데이터 fetching이 필요할 때
- Promise 기반의 동기적 처리가 필요할 때
코드 예시
// loader에서 기본 사용
export const loader = (queryClient: QueryClient) => async () => {
const data = await queryClient.fetchQuery({
queryKey: \['posts'\],
queryFn: fetchPosts
});
return { data };
};
// 캐시 확인 후 fetch
export const loader = (queryClient: QueryClient) => async () => {
// 1. 캐시 확인
const cached = queryClient.getQueryData(\['posts'\]);
if (cached) return { data: cached };
// 2. 캐시 없으면 fetch
const data = await queryClient.fetchQuery({
queryKey: \['posts'\],
queryFn: fetchPosts,
staleTime: 1000 \* 60
});
return { data };
};
// ensureQueryData 사용
export const loader = (queryClient: QueryClient) => async () => {
const data = await queryClient.ensureQueryData({
queryKey: \['posts'\],
queryFn: fetchPosts,
staleTime: 1000 \* 60
});
return { data };
};
3. useMutation
사용 시기
- 데이터 생성/수정/삭제 작업
- 사용자 인터랙션에 의한 데이터 변경
- 낙관적 업데이트가 필요한 경우
- 변경 작업의 성공/실패에 따른 UI 처리가 필요할 때
코드 예시
// 기본 사용
const mutation = useMutation({
mutationFn: createPost,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: \['posts'\] });
toast.success('포스트가 생성되었습니다');
}
});
// 낙관적 업데이트
const mutation = useMutation({
mutationFn: updateTodo,
onMutate: async (newTodo) => {
await queryClient.cancelQueries({ queryKey: \['todos'\] });
const previousTodos = queryClient.getQueryData(\['todos'\]);
queryClient.setQueryData(\['todos'\], old => \[...old, newTodo\]);
return { previousTodos };
},
onError: (err, newTodo, context) => {
queryClient.setQueryData(\['todos'\], context.previousTodos);
}
});
4. 캐시 관리 전략
캐시 조작 메서드
// 1. 캐시 조회
const cachedData = queryClient.getQueryData(\['posts'\]);
const queryState = queryClient.getQueryState(\['posts'\]);
// 2. 캐시 수정
queryClient.setQueryData(\['posts'\], newData);
queryClient.setQueryData(\['posts'\], old => \[...old, newPost\]);
// 3. 캐시 무효화
queryClient.invalidateQueries({ queryKey: \['posts'\] });
queryClient.invalidateQueries({
predicate: (query) => query.queryKey\[0\] === 'posts'
});
캐시 설정
- staleTime: 데이터가 "신선"하다고 간주되는 시간
- cacheTime: 데이터가 캐시에 유지되는 시간
최적화된 캐시 전략
// loader에서의 최적화된 캐시 사용
export const loader = (queryClient: QueryClient) => async () => {
try {
// 1. 캐시 확인
const cached = queryClient.getQueryData(\['posts'\]);
if (cached) {
const state = queryClient.getQueryState(\['posts'\]);
// 캐시가 신선한 경우에만 사용
if (!state?.isStale) return { data: cached };
}
// 2. 캐시가 없거나 오래된 경우 새로 fetch
return queryClient.ensureQueryData({
queryKey: ['posts'],
queryFn: fetchPosts,
staleTime: 1000 * 60,
cacheTime: 1000 * 60 * 5
});
} catch (error) {
// 에러 처리
console.error('Data fetching failed:', error);
throw error;
}
};
5. 각 방법의 주요 차이점
1. 사용 환경
- useQuery: React 컴포넌트 내부만
- fetchQuery: 어디서든 사용 가능
- useMutation: React 컴포넌트 내부만
2. 제공하는 기능
- useQuery: 자동 캐싱, 자동 리프레시, 상태 관리, 데이터 구독
- fetchQuery: Promise 기반 동작, 수동 캐시 제어
- useMutation: 데이터 변경, 낙관적 업데이트, 롤백 기능
3. 상태 관리
// useQuery - 풍부한 상태 정보
const {
data,
isLoading,
error,
isFetching,
isStale,
refetch,
status
} = useQuery(options);
// fetchQuery - Promise만 제공
const data = await queryClient.fetchQuery(options);
// useMutation - 변경 관련 상태
const {
mutate,
mutateAsync,
isLoading,
error,
isSuccess,
reset
} = useMutation(options); '개발 > 기록' 카테고리의 다른 글
| react router의 loader를 사용할 때의 장점 (0) | 2024.11.10 |
|---|---|
| Zustand 상태 구독 여부에 따른 렌더링 차이 (0) | 2024.10.28 |
| 브랜치, 풀 리퀘스트, 이슈 연동 관리 가이드 (1) | 2024.10.22 |
| Render Props Pattern, 동적 props 처리, 디바운싱 (1) | 2024.10.21 |
| Compound Component 패턴 (1) | 2024.10.21 |