Next.js와 React Query 조합으로 데이터 패칭 최적화하기 – 실시간, 캐싱, 에러처리까지 완벽 가이드
Next.js는 SSR/SSG/ISR 등 다양한 렌더링 전략을 제공하고, React Query는 데이터 요청/캐싱/로딩/에러 관리를 자동화해주는 도구입니다.
이 둘을 함께 사용하면 성능, 사용자 경험, 개발 효율성을 동시에 끌어올릴 수 있습니다.
이번 글에서는 Next.js와 React Query를 실무에서 어떻게 조합해 완성도 높은 데이터 패칭 시스템을 만들 수 있는지 렌더링 전략, prefetch, hydrate, 에러 핸들링, 캐싱 관리까지 총정리합니다.
1. 🔍 왜 React Query인가?
✅ React Query의 핵심 기능
- 자동 캐싱 / 자동 리패칭
- 로딩/에러/성공 상태 관리 내장
- 배경 refetch, 쿼리 무효화, prefetch
- devtools로 시각화 가능
📦 설치
npm install @tanstack/react-query
✅ 기본 구조
// _app.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
<QueryClientProvider client={queryClient}>
<Component {...pageProps} />
</QueryClientProvider>
✅ 데이터 패칭
import { useQuery } from '@tanstack/react-query';
const { data, isLoading, error } = useQuery({
queryKey: ['posts'],
queryFn: () => fetch('/api/posts').then(res => res.json()),
});
2. 🔄 Next.js와 조합할 때 주의할 점
📌 CSR 기본 – React Query 자체는 CSR 기반
- Next.js에서 기본으로 사용하면 CSR 렌더링
- 서버 측에서 데이터를 가져오려면 hydration 필요
✅ SSR과 함께 쓰는 방법 (Next.js 13 이하)
- getServerSideProps or getStaticProps에서 미리 데이터 패칭
- hydration으로 React Query에 넘겨줌
// pages/posts.js
export async function getServerSideProps() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return {
props: {
dehydratedState: dehydrate(queryClient),
posts,
},
};
}
➡️ Next.js에서 React Query를 제대로 쓰려면 서버 측 prefetch → 클라이언트 측 hydration 흐름을 이해해야 합니다.
3. 💧 서버 prefetch + 클라이언트 Hydration 구조
✅ dehydrate & hydrate를 이용한 데이터 초기화
Next.js에서 SSR or SSG 중 데이터를 미리 받아서 React Query에 전달하고, 클라이언트에서 해당 데이터로 초기화(Hydrate)하면 “깜빡임 없이” 렌더링할 수 있습니다.
// getServerSideProps 예시
import { dehydrate, QueryClient } from '@tanstack/react-query';
export async function getServerSideProps() {
const queryClient = new QueryClient();
await queryClient.prefetchQuery({
queryKey: ['posts'],
queryFn: () => fetch('https://api.example.com/posts').then(res => res.json()),
});
return {
props: {
dehydratedState: dehydrate(queryClient),
},
};
}
// _app.tsx 또는 페이지 내부
import { Hydrate } from '@tanstack/react-query';
<Hydrate state={pageProps.dehydratedState}>
<Component {...pageProps} />
</Hydrate>
🎯 장점
- 초기 로딩 없음 → 사용자 경험 향상
- SEO 대응 → 서버에서 데이터 포함
4. 🗂️ 캐싱 전략 – staleTime과 cacheTime
✅ staleTime
데이터를 신선한 상태로 간주하는 시간 (ms 단위)
- 기본값: 0 (페이지 포커스되면 바로 refetch)
- 자주 바뀌지 않는 데이터 → 5분 이상 권장
useQuery({
queryKey: ['settings'],
queryFn: fetchSettings,
staleTime: 1000 * 60 * 5, // 5분
});
✅ cacheTime
사용하지 않는 데이터가 메모리에서 유지되는 시간
useQuery({
queryKey: ['settings'],
queryFn: fetchSettings,
cacheTime: 1000 * 60 * 10, // 10분
});
📌 전략 팁
- staleTime은 사용자 기준 ‘데이터 최신성’
- cacheTime은 개발자 기준 ‘메모리 보존’
5. ♻️ 쿼리 무효화와 재요청 전략
📌 mutate 후 자동 refetch – queryClient.invalidateQueries()
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: updateUser,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['user'] });
},
});
➡️ 데이터가 바뀌면 관련 쿼리를 무효화하고 자동으로 다시 불러오게 설정합니다.
✅ 예시: 게시글 수정 → 목록 리패치
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['posts'] });
}
무효화 없이 수동으로 다시 불러오면 버그와 누락의 위험이 있습니다. React Query의 자동성능을 최대한 활용하세요.
6. ❗ 에러 처리 – 사용자 친화적인 피드백 제공
✅ useQuery의 에러 상태
const { data, isLoading, error } = useQuery({
queryKey: ['profile'],
queryFn: fetchUserProfile,
});
📌 에러 메시지 표시 예시
if (error) {
return <ErrorMessage text="데이터를 불러오는 중 문제가 발생했습니다." />;
}
✅ 공통 에러 처리 전략
- Axios 인터셉터 + toast 연동
- React Query devtools로 에러 추적
- HTTP 401/403/500 별 에러 분기
7. 🚀 Prefetch – 사용자가 도달하기 전에 미리 준비하자
📦 페이지 이동 전 데이터 미리 불러오기
queryClient.prefetchQuery({
queryKey: ['products'],
queryFn: fetchProducts,
});
✅ 링크 hover 기반 prefetch
<Link
href="/products"
onMouseEnter={() => {
queryClient.prefetchQuery({
queryKey: ['products'],
queryFn: fetchProducts,
});
}}
>
제품 보기
</Link>
➡️ 사용자가 이동하기 전에 데이터를 미리 준비해 “즉시 로딩되는 듯한 경험”을 제공합니다.
8. ⛓️ 병렬 요청 & 조건부 쿼리
✅ useQueries로 병렬 호출
const results = useQueries({
queries: [
{ queryKey: ['user'], queryFn: fetchUser },
{ queryKey: ['settings'], queryFn: fetchSettings },
],
});
✅ 조건부 호출 (enabled)
useQuery({
queryKey: ['userDetail', id],
queryFn: () => fetchUserDetail(id),
enabled: !!id, // id가 있을 때만 실행
});
➡️ 페이지에 따라 데이터 요청 시점을 제어하면 불필요한 네트워크 낭비를 줄이고 UX가 부드러워집니다.
9. 🧠 마무리 – Next.js × React Query는 최강의 데이터 조합
✨ 전략 요약
- SSR/SSG에서 dehydrate → Hydrate 패턴으로 초기 렌더링 최적화
- staleTime / cacheTime 조정으로 불필요한 요청 방지
- invalidateQueries로 상태 변경 시 자동 refetch
- 에러 핸들링과 토스트/뷰 처리 연계
- prefetch로 UX 속도 향상
- useQueries + enabled로 병렬 및 조건부 요청 설계
React Query는 단순히 데이터 가져오기 도구가 아닙니다. 데이터 흐름을 UX와 함께 최적화할 수 있는 설계의 핵심 도구입니다.
긴 글 읽어주셔서 감사합니다! 공감, 댓글, 공유로 응원해주시면 다음 글 제작에 큰 힘이 됩니다 🙌
'일상이 개발' 카테고리의 다른 글
Next.js 로딩 UX 완전 정복 – Skeleton, Suspense, 버튼 피드백까지 실전 적용 전략 (0) | 2025.05.11 |
---|---|
Next.js + React Query로 서버 상태 완전 정복 – 동기화, 캐싱, UX 최적화 전략 총정리 (0) | 2025.05.10 |
Next.js UX 전략 완전 정복 – 로딩, 인터랙션, 접근성까지 실전 중심 가이드 (0) | 2025.05.08 |
Next.js 최적화 전략 – SSR, SSG, ISR 실전 활용법 총정리 (0) | 2025.05.07 |
React 앱 최적화 & 유지보수 전략 – 성능부터 테스트까지 실무를 위한 전방위 가이드 (0) | 2025.05.06 |