Next.js와 React Query의 완벽한 만남: Hydrate로 서버 데이터를 클라이언트에 매끄럽게 전달하기

LinkDropper·2025년 6월 19일
6

Others

목록 보기
3/4
post-thumbnail

Next.js는 SSR과 SSG를 지원하는 강력한 프레임워크이며, React Query는 API 호출 및 캐싱을 자동으로 처리해주는 매우 강력한 상태 관리 라이브러리입니다. 이 둘의 조합은 매우 강력하지만, 서버에서 가져온 데이터를 클라이언트로 자연스럽게 넘기기 위한 고민은 여전히 존재합니다.

React Query의 Hydrate 컴포넌트를 활용하면 이 문제를 깔끔하게 해결할 수 있습니다. 이번 글에서는 Hydrate의 개념, 동작 원리, 그리고 실제로 어떻게 Next.js 프로젝트에 적용하는지를 상세하게 설명합니다.


🤔 왜 Hydrate가 필요할까?

서버 사이드 렌더링을 사용하는 경우, 서버에서 데이터를 미리 받아와 화면을 렌더링합니다. 그러나 클라이언트는 이 상태를 알지 못하므로, React Query는 동일한 데이터를 클라이언트에서 다시 요청하게 됩니다. 이로 인해 불필요한 중복 요청과 느린 사용자 경험이 발생할 수 있습니다.

Hydrate를 사용하면 서버에서 가져온 데이터를 클라이언트 React Query의 QueryClient 상태에 미리 주입할 수 있으므로, 다음과 같은 이점이 있습니다:

  • 중복 요청 방지: 클라이언트에서 불필요하게 API를 다시 호출하지 않음
  • 빠른 첫 렌더링: 사용자에게 더 빠른 페이지를 제공
  • 🔄 일관된 상태 유지: SSR과 CSR 간의 상태 불일치 문제 해결

🔍 Hydrate의 동작 원리

Hydrate는 서버에서 React Query의 상태(QueryClient 내부 상태)를 dehydrate() 함수를 통해 JSON 직렬화 가능한 형태로 변환한 뒤, 클라이언트에서 Hydrate 컴포넌트를 통해 이를 QueryClient에 복원하는 방식으로 동작합니다.

이 과정을 요약하면 다음과 같습니다:

  1. 서버에서 QueryClient로 데이터를 prefetch
  2. dehydrate 함수로 직렬화
  3. Next.js의 getServerSidePropsgetStaticProps를 통해 props로 전달
  4. 클라이언트에서 Hydrate 컴포넌트로 역직렬화 후 QueryClient에 주입

이렇게 주입된 초기 캐시는 클라이언트에서 useQuery 훅으로 접근 시 바로 사용되며, 같은 queryKey와 동일한 fetch 함수를 사용하는 경우 새로운 요청 없이 즉시 캐시 데이터를 보여줍니다.


🛠 예제 코드로 살펴보는 Hydrate 활용법

1. 서버에서 QueryClient 생성 및 데이터 prefetch

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

const fetchData = async () => {
  const res = await fetch('https://api.example.com/data');
  return res.json();
};

export const getServerSideProps: GetServerSideProps = async () => {
  const queryClient = new QueryClient();
  await queryClient.prefetchQuery(['data'], fetchData);

  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  };
};

export default function Home() {
  const { data, isLoading } = useQuery(['data'], fetchData);
  return <div>{isLoading ? 'Loading...' : JSON.stringify(data)}</div>;
}

2. _app.tsx에서 Hydrate로 상태 복원

// pages/_app.tsx
import { Hydrate, QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useState } from 'react';
import type { AppProps } from 'next/app';

export default function MyApp({ Component, pageProps }: AppProps) {
  const [queryClient] = useState(() => new QueryClient());

  return (
    <QueryClientProvider client={queryClient}>
      <Hydrate state={pageProps.dehydratedState}>
        <Component {...pageProps} />
      </Hydrate>
    </QueryClientProvider>
  );
}

⚠️ Hydrate 사용 시 주의할 점

  • Hydrate는 반드시 QueryClientProvider 내부에 위치해야 합니다.
  • dehydrate로 직렬화된 데이터는 JSON으로 직렬화 가능한 값이어야 합니다.
  • queryKeyfetch 함수의 구현이 서버와 클라이언트에서 동일해야 캐시가 재활용됩니다.

💡 응용 팁과 실전 활용

  • 정적 페이지 (SSG)에서도 활용 가능: getStaticProps와 함께 사용하여 정적 페이지도 빠르게 구성 가능
  • 다국어 지원 시 유용: 초기 번역 데이터를 서버에서 주입하면 클라이언트에서 로딩 없이 UI 구성 가능
  • 로그인 사용자 정보 프리패칭: 로그인한 사용자의 프로필 정보를 미리 캐시에 넣어둘 수 있음
  • persistQueryClient와의 차이: Hydrate는 SSR 데이터 복원에 사용되며, persistQueryClient는 로컬 스토리지 등을 통한 캐시 지속에 활용됨

🧩 마무리: Hydrate는 선택이 아닌 필수!

React Query와 Next.js의 조합에서 Hydrate는 단순한 옵션이 아니라 꼭 활용해야 할 기능입니다. 서버에서 가져온 데이터를 클라이언트와 자연스럽게 이어주어 중복 요청을 방지하고, UX를 극대화하며, 성능까지 향상시켜주는 고마운 존재입니다.

Next.js 프로젝트에서 SSR을 적용하고 있다면, Hydrate를 반드시 고려해 보세요. 서버 상태 관리의 끝판왕으로, 더 빠르고 안정적인 웹 애플리케이션을 만들 수 있습니다.


🧪 링크 드라퍼, 지금 베타 테스트 중입니다

링크 드라퍼는 단순한 저장 툴이 아닙니다.
정리하고, 수정하고, 다시 꺼내보게 만드는 링크 관리 도구를 지향하고 있습니다.

• 🔗 빠르고 간편한 링크 저장
• 🧠 저장한 링크를 폴더별로 정리
• 🌐 폴더를 친구에게 공유 가능
• ⚡ 크롬 익스텐션 원클릭 저장

👉 링크 드라퍼 베타 체험하러 가기
👉 크롬 웹스토어에서 설치하기

💬 카카오톡 채널 추가하고 소식 받기

서비스 업데이트
기능 꿀팁
카카오톡 채널을 통해 빠르게 받아보세요!
👉 카카오톡 채널 추가하기

profile
“기록하는 습관을 도구로 만들다 — 두 개발자의 링크 드라퍼 구축기”

0개의 댓글