노트

하이드레이션

Hydration

프런트엔드#react#nextjs · 연결된 개념 16개

쉽게 말하면

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

비유가 깨지는 곳 숨을 불어넣는다고 화면을 새로 그리는 건 아니에요. 이미 있는 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이다.

  • Redux 이후의 상태 관리 선택

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

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

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

  • 설정 기반 UI

    화면 구조를 컴포넌트를 나열한 코드가 아니라 설정 데이터(보통 배열)로 선언하고, 렌더러가 그 설정을 읽어 그리는 패턴. 구조를 바꿀 때 렌더링 코드 대신 데이터만 고친다.

보기 옵션