서버 컴포넌트 (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 동작 원리
- 서버 렌더링: 서버에서 prefetchQuery로 데이터 fetch
- dehydrate: QueryClient 상태를 JSON으로 직렬화
- HTML 전송: 완성된 HTML + 직렬화된 상태 전송
- Hydration: 클라이언트에서 상태를 QueryClient에 복원
- 즉시 렌더링: 캐시에 데이터가 있으므로 로딩 없이 렌더링
실행 결과서버에서 렌더링됨
데이터를 가져오는 중...
타이밍
서버 렌더링 시각
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 최적화