← 홈으로

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 최적화