하이드레이션(Hydration)은 서버가 미리 렌더해 보낸 HTML(HyperText Markup Language)을 클라이언트에서 다시 만들지 않고, 그 DOM(Document Object Model)에 React 컴포넌트를 연결해 이벤트 핸들러와 상태를 붙이는 과정이다(hydrateRoot). Dan Abramov의 비유로는 "마른 HTML에 상호작용이라는 물을 주는 일"이다.
- SSR(Server-Side Rendering) 덕분에 사용자는 JS(JavaScript)가 오기 전에 내용을 본다. 하지만 하이드레이션이 끝나기 전에는 버튼이 반응하지 않는다. 보이는 시점과 쓸 수 있는 시점이 다르다(Core Web Vitals)
- 서버 HTML과 첫 클라이언트 렌더 결과가 달라지면 하이드레이션 불일치(Hydration Mismatch)가 난다. 렌더 중
Date.now(),Math.random(),window분기가 흔한 원인이다(React의 규칙과 렌더 순수성)
통짜 하이드레이션 vs 선택적 하이드레이션(Selective Hydration)
- Pages Router 시절: 페이지 전체 JS를 받고 트리 전체를 한 번에 하이드레이션했다. 모든 부분이 동시에, 마지막에야 인터랙티브해졌다
- React 18 + App Router: 서버 컴포넌트는 JS를 보내지 않으니 하이드레이션 대상이 아니다.
'use client'부분만 하이드레이션하고, Suspense 경계마다 JS가 도착하는 대로 따로 살아난다. 사용자가 먼저 건드린 경계를 우선 처리하기도 한다
flowchart LR
subgraph P["통짜 하이드레이션 (Pages Router)"]
direction TB
A1["SSR HTML 도착 (보이지만 안 움직임)"] --> A2["페이지 전체 JS 로드"] --> A3["트리 전체를 한 번에 하이드레이션"] --> A4["마지막에야 전체가 인터랙티브"]
end
subgraph S["선택적 하이드레이션 (React 18 + App Router)"]
direction TB
B1["SSR HTML 도착 (보이지만 안 움직임)"] --> B2["'use client' 부분 JS만 경계별로 로드"] --> B3["경계마다 따로 하이드레이션 (먼저 건드린 경계 우선)"] --> B4["준비된 경계부터 인터랙티브"]
end
P ~~~ S
주의: UI가 데이터보다 먼저 살아날 때
선택적 하이드레이션은 버튼을 일찍 누를 수 있게 만든다. 그런데 로그인 세션처럼 사용자별 데이터는 캐시할 수 없어 여전히 네트워크 왕복이 필요하다. 그러면 "버튼은 눌리는데 세션은 아직 모르는" 구간이 실제로 노출된다. 이때 user === null을 로그아웃으로 단정하면 로그인한 사용자에게 "로그인하세요"가 뜬다. 처방은 타이밍을 늦추는 게 아니라 '아직 모름'과 '확실히 없음'을 다른 상태로 구분하는 것이다(판별 유니언, 특이 케이스와 널 객체).
렌더링 전략 전체는 CSR·SSR·SSG·ISR, 조각 단위 전송은 스트리밍 SSR, 전환 경험은 Pages Router에서 App Router로.