노트

하이드레이션

Hydration

프론트엔드#react#nextjs · 연결된 개념 19개

쉽게 말하면

하이드레이션은 서버가 미리 그려 보낸 화면에 자바스크립트가 들어가서 버튼이 눌리게 만드는 거예요. 사진처럼 보이기만 하던 화면에 숨을 불어넣는 셈이죠.

비유의 한계 숨을 불어넣는다고 화면을 새로 그리는 건 아니에요. 이미 있는 DOM을 그대로 쓰기 때문에, 서버 HTML과 첫 클라이언트 렌더가 다르면 불일치 오류가 나요.

하이드레이션(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로.

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • loading 경계와 레이아웃 끌어올리기

    Next.js App Router에서 loading.tsx는 같은 세그먼트의 page와 그 아래(하위 layout 포함)를 suspense로 감싸지만, **같은 세그먼트의 layout은 감싸지 않는다**. 그래서 데이터를 기다릴 필요가 없는 공통 UI(탭바, 필터 헤더)를 page에서 layout으로 끌어올리면 스켈레톤에 덮이지 않고 처음부터 실제 모습으로 보인다.

  • Activity와 숨겨진 라우트 보존

    Activity는 React 19.2의 컴포넌트로, UI를 언마운트하지 않고 display: none으로 숨기면서 state와 DOM(Document Object Model)을 보존한다. 숨기는 동안 Effect는 정리(cleanup)된다. 예전 이름은 Offscreen이다.

  • 트랜잭션·트랜지션·뮤테이션 구분

    세 단어는 이름이 비슷하지만 다루는 층이 다르다. 뮤테이션(mutation)은 데이터를 바꾸는 작업, 트랜잭션(transaction)은 여러 변경을 전부 반영하거나 전혀 반영하지 않게 묶는 단위, React의 트랜지션(transition)은 화면 업데이트의 우선순위를 낮추는 표시다.

  • Redux 이후의 상태 관리 선택

    한때 React 앱의 기본값이던 Redux는 2025~2026년 기준으로 여러 선택지 중 하나가 됐다. 상태를 종류별로 나눠 각자에 맞는 도구를 고르는 방식이 자리 잡았기 때문이다.

  • 커넥션 드레이닝과 무중단 재시작

    배포 중 서버를 재시작하는 몇 초 동안 로드밸런서가 그 서버로 요청을 보내면 502가 난다. 먼저 로드밸런서에서 빼고(드레이닝), 진행 중인 요청을 마친 뒤 재시작하고, 준비되면 다시 넣는다.

보기 옵션