일상이 개발

Next.js와 React Query로 데이터 패칭 최적화하기: SSR, CSR, 캐싱까지 한 번에 잡기

디어노미 2025. 4. 8. 23:39
반응형

🚀 Next.js와 React Query 조합으로 데이터 패칭 최적화하기

Next.js 프로젝트를 운영하다 보면 데이터 패칭이 점점 중요해집니다. 초기엔 getStaticPropsgetServerSideProps만으로도 충분해 보이지만, 사용자 기반 UI가 늘어나고 상호작용이 많아지면 클라이언트 측 상태 관리 도구가 필요해집니다.

이때 강력한 조합이 바로 Next.js + React Query입니다. 서버사이드 렌더링(SSR)과 클라이언트 데이터 캐싱을 자연스럽게 조합하면, 성능과 UX 모두를 잡을 수 있죠.

🧠 Next.js 기본 데이터 패칭 전략 복습

  • getServerSideProps - 요청 시마다 서버에서 HTML 생성
    로그인, 실시간 콘텐츠, SEO 최적화에 적합
  • getStaticProps + revalidate - 빌드 타임에 HTML 생성 + ISR
    블로그, 리스트 페이지 등에 적합

그러나 위 두 방식은 사용자 기반 실시간 데이터 갱신엔 한계가 있습니다.

⚙️ React Query의 역할

  • 자동 캐싱 / 백그라운드 refetch
  • 쿼리 무효화, prefetch 지원
  • 로딩/에러/성공 상태 관리
  • SSR/SSG 연동 지원

📦 설치 및 기본 설정

npm install @tanstack/react-query
// pages/_app.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

export default function App({ Component, pageProps }) {
  return (
    <QueryClientProvider client={queryClient}>
      <Component {...pageProps} />
    </QueryClientProvider>
  );
}

🔍 기본 예시: 사용자 정보 불러오기

import { useQuery } from '@tanstack/react-query';

function fetchUser() {
  return fetch('/api/me').then(res => res.json());
}

const { data, isLoading, error } = useQuery(['user'], fetchUser);

if (isLoading) return <p>로딩 중...</p>
if (error) return <p>에러 발생</p>
return <div>{data.name}님 환영합니다</div>

🧬 SSR + React Query 연동 (dehydrate/hydrate)

// pages/posts.tsx
import { QueryClient, dehydrate, useQuery } from '@tanstack/react-query';

function fetchPosts() {
  return fetch('https://api.example.com/posts').then(res => res.json());
}

export async function getServerSideProps() {
  const queryClient = new QueryClient();
  await queryClient.prefetchQuery(['posts'], fetchPosts);
  return { props: { dehydratedState: dehydrate(queryClient) } };
}

export default function PostsPage() {
  const { data } = useQuery(['posts'], fetchPosts);
  return <div>{data?.length}개의 글</div>;
}

✨ 고급 기능

1. 쿼리 무효화

const queryClient = useQueryClient();
queryClient.invalidateQueries(['posts']);

2. useMutation + 무효화

const mutation = useMutation(
  (newPost) => fetch('/api/posts', {
    method: 'POST',
    body: JSON.stringify(newPost)
  }),
  {
    onSuccess: () => {
      queryClient.invalidateQueries(['posts']);
    }
  }
);

🧩 실전 전략 예: SSG + React Query + CSR

  • 목록은 getStaticProps로 정적 생성
  • 상세/댓글 등은 React Query + CSR
  • 댓글 등록 후 invalidate로 자동 갱신

📈 최적화 팁

  • 쿼리 키 설계: ['posts', id] 등 명확히
  • staleTime, cacheTime 조정
  • enabled: false로 조건부 fetch 구현
  • prefetchQuery로 사전 데이터 로딩
  • useSuspenseQuery로 suspense 사용

✅ 마무리 정리

Next.js와 React Query를 조합하면 SSR, CSR, SSG 환경을 유연하게 아우를 수 있습니다. SEO부터 사용자 반응 속도까지 모두 최적화할 수 있는 강력한 조합이죠.

🔑 핵심 요약 체크리스트

  • useQuery: 클라이언트 데이터 fetch
  • dehydrate/hydrate: SSR 초기 데이터 렌더링
  • useMutation + invalidate: 데이터 갱신 처리
  • SSG + React Query 조합으로 초기/실시간 UX 모두 대응

📌 다음 글에서는 React Query + Suspense 조합, 또는 App Router에서의 데이터 패칭 구조 설계를 이어서 다뤄보겠습니다.

반응형