← 홈으로

SSR + Hydration

서버에서 데이터를 미리 가져와 HTML과 함께 전달하는 방식

서버 컴포넌트 (page.tsx)
1// page.tsx (서버 컴포넌트)
2import { dehydrate, HydrationBoundary } from '@tanstack/react-query';
3import { getQueryClient } from '@/providers/QueryProvider';
4 
5export default async function SSRPage() {
6 const queryClient = getQueryClient();
7 
8 // 서버에서 데이터 미리 가져오기
9 await queryClient.prefetchQuery({
10 queryKey: ['posts'],
11 queryFn: () => fetch('/api/posts').then(res => res.json()),
12 });
13 
14 return (
15 // dehydrate로 서버 상태를 직렬화하여 클라이언트로 전달
16 <HydrationBoundary state={dehydrate(queryClient)}>
17 <PostList />
18 </HydrationBoundary>
19 );
20}
클라이언트 컴포넌트 (PostList.tsx)
1// PostList.tsx (클라이언트 컴포넌트)
2'use client';
3 
4import { useQuery } from '@tanstack/react-query';
5 
6export default function PostList() {
7 // 서버에서 prefetch한 데이터가 이미 캐시에 있음!
8 // → isLoading이 false로 시작, 즉시 렌더링
9 const { data } = useQuery({
10 queryKey: ['posts'],
11 queryFn: () => fetch('/api/posts').then(res => res.json()),
12 });
13 
14 return (
15 <ul>
16 {data?.posts.map(post => (
17 <li key={post.id}>{post.title}</li>
18 ))}
19 </ul>
20 );
21}

💡SSR + Hydration 동작 원리

  1. 서버 렌더링: 서버에서 prefetchQuery로 데이터 fetch
  2. dehydrate: QueryClient 상태를 JSON으로 직렬화
  3. HTML 전송: 완성된 HTML + 직렬화된 상태 전송
  4. Hydration: 클라이언트에서 상태를 QueryClient에 복원
  5. 즉시 렌더링: 캐시에 데이터가 있으므로 로딩 없이 렌더링
실행 결과서버에서 렌더링됨

데이터를 가져오는 중...

타이밍

서버 렌더링 시각
2025-12-23T02:04:03.035Z
데이터 Prefetch 완료
클라이언트에서 추가 로딩 없음
0ms

Hydration이란?

Hydration은 서버에서 생성된 정적 HTML에 JavaScript를 "물을 주듯" 연결하여 인터랙티브하게 만드는 과정입니다.

TanStack Query의 경우, dehydrate()로 서버의 캐시 상태를 직렬화하고, HydrationBoundary로 클라이언트의 QueryClient에 복원합니다.

⚠️CSR과의 차이점

CSR
  • • 빈 HTML 전송
  • • JS 실행 후 API 호출
  • • 로딩 스피너 표시
  • • SEO 불리
SSR + Hydration
  • • 완성된 HTML 전송
  • • 데이터가 이미 캐시에 있음
  • • 즉시 렌더링
  • • SEO 최적화