반응형
⚙️ Next.js + React Query로 서버 상태 최적화하기
클라이언트 중심의 React 앱이 점점 커지고, 페이지 단위로 분리된 Next.js 프로젝트가 대세가 되면서 이제는 “데이터 패칭” 그 자체보다도 서버 상태를 어떻게 효율적으로 관리할 것인가가 더 중요한 이슈가 되었습니다.
그 중심에 있는 조합이 바로 Next.js + React Query입니다.
단순한 useEffect + fetch
가 아니라, SSR/SSG와 클라이언트 사이드의 상태까지 아우르는 통합적 서버 상태 관리 전략을 구성할 수 있게 되죠.
이번 글에서는 실제로 활용 가능한 서버 상태 최적화 전략을 Next.js와 React Query 기반으로 차근차근 설명드리겠습니다.
1. 왜 서버 상태 최적화가 필요할까?
- 비동기적으로 외부(서버)에서 가져오는 데이터
- 여러 컴포넌트에서 공유되고 사용됨
- 변경 가능성이 있으며, 동기화 필요
보통은 useEffect + fetch
로 처리하지만, 다음과 같은 문제가 생깁니다:
- 중복 fetch → 비효율적인 네트워크 요청
- 전역 상태와 서버 상태가 섞임
- 초기화/로딩 처리 분산
- 캐싱/갱신 전략 부재
React Query의 장점
- 요청 캐싱 / 자동 갱신
- 요청 중복 제거 / 쿼리 키 기반 상태 분리
- 데이터 prefetch / SSR/SSG 대응
2. 기본 세팅
npm install @tanstack/react-query
// pages/_app.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function MyApp({ Component, pageProps }) {
return (
<QueryClientProvider client={queryClient}>
<Component {...pageProps} />
</QueryClientProvider>
);
}
3. 기본적인 서버 상태 패칭 구조
import { useQuery } from '@tanstack/react-query';
function fetchUser() {
return fetch('/api/me').then(res => res.json());
}
function Profile() {
const { data, isLoading, error } = useQuery(['user'], fetchUser);
if (isLoading) return <p>로딩 중...</p>;
if (error) return <p>에러 발생</p>;
return <div>{data.name}님 환영합니다</div>;
}
4. SSR/SSG와 React Query 조합하기
// pages/posts.tsx
import { dehydrate, QueryClient, useQuery } from '@tanstack/react-query';
function fetchPosts() {
return fetch('https://api.example.com/posts').then(res => res.json());
}
function PostsPage() {
const { data } = useQuery(['posts'], fetchPosts);
return <div>{data.length}개의 글</div>;
}
export async function getServerSideProps() {
const queryClient = new QueryClient();
await queryClient.prefetchQuery(['posts'], fetchPosts);
return {
props: {
dehydratedState: dehydrate(queryClient),
},
};
}
5. 서버 상태 무효화 및 갱신 전략
const queryClient = useQueryClient();
const mutation = useMutation(
(newPost) => fetch('/api/posts', {
method: 'POST',
body: JSON.stringify(newPost),
}),
{
onSuccess: () => {
queryClient.invalidateQueries(['posts']);
},
}
);
6. 조건부 패칭 및 staleTime 활용
const { data } = useQuery(['user'], fetchUser, {
enabled: !!session,
});
const { data } = useQuery(['products'], fetchProducts, {
staleTime: 300000, // 5분
cacheTime: 600000 // 10분
});
7. 데이터 미리 불러오기
const queryClient = useQueryClient();
const handleHover = () => {
queryClient.prefetchQuery(['user'], fetchUser);
};
8. 실전 패턴 예시
구성 | 방법 |
---|---|
목록 초기 데이터 | getStaticProps + prefetchQuery |
상세 페이지 | useQuery 클라이언트 요청 |
댓글 등록 | useMutation + invalidateQueries |
로그인 후 접근 | SSR + 쿠키 인증 |
9. React Query DevTools
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
<QueryClientProvider client={queryClient}>
<Component {...pageProps} />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
✅ 마무리 요약
- useQuery: fetch + 캐싱 + 상태 관리
- SSR 대응: getServerSideProps + dehydrate
- 갱신: useMutation + invalidateQueries
- UX 최적화: staleTime, enabled, prefetchQuery
다음 글에서는 App Router 기반에서 React Query + Suspense 조합을 다뤄보겠습니다.
반응형
'일상이 개발' 카테고리의 다른 글
Next.js App Router 환경에서 서버 상태와 URL 라우팅 최적화하는 실전 전략 (0) | 2025.04.10 |
---|---|
React 상태 동기화 이슈 완전 정복: Form ↔ 전역 상태 ↔ URL 쿼리 흐름 정리 (0) | 2025.04.09 |
Next.js와 React Query로 데이터 패칭 최적화하기: SSR, CSR, 캐싱까지 한 번에 잡기 (0) | 2025.04.08 |
Next.js 앱에서 사용자 경험을 높이는 실전 UX 전략 가이드 (0) | 2025.04.08 |
Next.js 최적화 전략 완벽 가이드: SSR, SSG, ISR 제대로 활용하기 (0) | 2025.04.08 |