사전 하이드레이션
개념

하이드레이션

gabury1고친 사람 github-actions[bot]

하이드레이션은 서버가 그려 보낸 화면에 브라우저가 동작을 붙이는 일입니다. 그 전까지 화면은 보이기만 하고 버튼을 눌러도 반응하지 않습니다. 서버 사이드 렌더링을 쓰는 웹 앱이 이 단계를 거칩니다. 데이터베이스 쪽에서는 조회한 행으로 객체를 채우는 일을 같은 이름으로 부릅니다.

쉽고 빠른 이해

하이드레이션은 서버가 보낸 화면에 클릭과 입력을 받는 코드를 이어 붙이는 일입니다. 쇼핑몰 상품 페이지가 뜨면 사진과 가격은 바로 보입니다. 장바구니 버튼은 이 일이 끝나야 눌립니다.

서버가 화면을 다 그려 보내면 첫 화면이 빨리 뜹니다. 그런데 서버가 보내는 것은 글자로 적은 문서라서 버튼을 눌렀을 때 할 일은 실리지 않습니다. 브라우저에서 화면을 처음부터 다시 그리면 이미 보인 화면을 버리게 됩니다.

  1. 서버가 다 그린 화면을 문서로 보냅니다
  2. 브라우저는 그 문서를 바로 띄웁니다
  3. 뒤이어 내려온 코드가 화면을 다시 계산해 이미 있는 화면과 짝을 맞춥니다
  4. 짝이 맞은 버튼과 입력칸에 할 일을 붙입니다

대가는 같은 화면을 두 번 계산하는 것입니다. 코드가 다 내려와 돌 때까지 화면은 보여도 눌리지 않습니다.

그래서 서버가 그려 보내면서 눌러야 할 것이 있는 페이지에만 씁니다. 읽기만 하는 페이지나 브라우저가 처음부터 그리는 페이지에는 필요 없습니다.

상세

모델하우스는 가구와 조명까지 갖춰 놓아 보기에는 다 지은 집입니다. 그런데 스위치를 눌러도 불이 켜지지 않습니다. 입주하는 날 전기 배선을 이어야 스위치가 제 일을 합니다.

서버가 보낸 화면이 모델하우스입니다. 스위치는 버튼과 입력칸입니다. 하이드레이션은 거기에 브라우저 코드라는 배선을 잇는 일입니다.

배선은 이렇게 잇습니다. 브라우저가 서버와 같은 화면 코드를 한 번 더 돌립니다. 돌린 결과는 메모리에만 그려 둡니다. 실제 화면은 이미 받은 것을 그대로 씁니다. 거기에 동작만 붙입니다.

이름은 영어 낱말 hydration 에서 왔습니다. 말린 것에 물을 되돌려 준다는 뜻입니다. 서버가 보낸 화면을 물기 빠진 화면으로, 브라우저 코드를 물로 본 이름입니다. 같은 일을 리하이드레이션(rehydration) 이라고도 부릅니다.

서버가 보내는 HTML 에 빠진 것

이 소절은 서버가 보내는 결과물에 무엇이 담기고 무엇이 빠지는지 봅니다. 하이드레이션이 채우는 것은 그 빠진 쪽입니다.

서버 사이드 렌더링은 화면을 서버에서 만들어 보내는 방식입니다. 서버가 화면 코드를 돌려 나온 결과를 HTML(HyperText Markup Language, 웹 문서를 적는 언어)로 적어 보냅니다. 브라우저는 그 글자를 읽어 바로 화면을 띄웁니다.

브라우저는 받은 HTML 을 읽어 DOM(Document Object Model, 문서 객체 모델)을 만듭니다. DOM 은 화면 요소를 부모와 자식으로 이은 트리입니다. 트리의 칸 하나하나를 노드라고 부릅니다. 화면은 DOM 을 따라 그려집니다.

클릭이나 입력이 일어났을 때 부르는 함수를 이벤트 핸들러라고 합니다. 장바구니 버튼의 이벤트 핸들러는 상품을 장바구니에 넣는 코드입니다. 이 함수가 붙어 있어야 버튼이 버튼 노릇을 합니다.

HTML 에 담기는 것은 요소의 종류와 글자와 속성뿐입니다. 어느 버튼에 어느 이벤트 핸들러가 붙어 있었는지는 남지 않습니다. 그 이음은 서버 메모리 안에만 있어서 글자로 옮겨지지 않습니다. 그래서 HTML 만 받은 화면의 버튼은 눌러도 아무 일이 없습니다.

함수 코드 자체는 뒤에 따로 내려갑니다. 브라우저에서 그 함수를 버튼에 다시 잇는 일이 하이드레이션입니다.

화면이 들고 있는 값도 빠집니다. 펼친 메뉴, 장바구니 개수처럼 화면이 기억하는 값을 상태라고 부릅니다. 서버에서 돌던 상태는 HTML 로 바뀌는 순간 글자만 남습니다. 그 값을 바꾸고 화면을 다시 그릴 코드가 브라우저에는 아직 없습니다.

하이드레이션이 도는 순서

요청 하나가 들어와 버튼이 눌리기까지를 차례로 따라갑니다. 그림으로 전체를 먼저 봅니다. 그다음 단계마다 무엇이 일어나는지 풉니다.

화면 코드는 대개 조각으로 나뉩니다. 화면 조각 하나를 맡는 코드를 컴포넌트라고 부릅니다. 상품 사진 칸과 장바구니 버튼 칸이 저마다 컴포넌트가 됩니다.

브라우저에서 돌 코드는 파일 몇 개로 묶여 내려옵니다. 이 묶음을 번들이라고 부릅니다. 묶어 보내면 브라우저가 받을 파일 수가 줄어듭니다.

sequenceDiagram
    participant 브라우저
    participant 서버
    브라우저->>서버: 페이지 요청
    Note over 서버: 컴포넌트를 돌려 HTML 을 만듦
    서버-->>브라우저: HTML 과 상태 값
    Note over 브라우저: 화면이 보임 · 아직 안 눌림
    브라우저->>서버: 번들 요청
    서버-->>브라우저: 번들
    Note over 브라우저: 컴포넌트를 다시 돌림
    Note over 브라우저: DOM 과 짝을 맞춤
    Note over 브라우저: 이벤트 핸들러를 붙임 · 이제 눌림

서버는 HTML 과 함께 화면을 그릴 때 쓴 상태 값도 보냅니다. 앞 소절에서 HTML 로 바뀌며 글자만 남는다고 한 그 상태의 처음 값입니다. 브라우저가 같은 값으로 컴포넌트를 돌려야 서버와 같은 결과가 나오기 때문입니다.

이 값은 대개 HTML 안의 <script> 태그에 넣어 보냅니다. 적는 형식은 JSON(JavaScript Object Notation, 자바스크립트 객체 표기법)입니다. JSON 은 객체와 배열을 글자로 적는 형식입니다.

값을 이렇게 글자로 바꿔 싣는 일을 직렬화라고 합니다. 브라우저는 그 글자를 다시 값으로 읽어 컴포넌트에 넘깁니다.

짝 맞추기는 브라우저가 스스로 하지 않습니다. 번들에 함께 실려 온 화면 라이브러리가 합니다. 화면 라이브러리는 컴포넌트를 짜고 돌리는 데 쓰는 코드 묶음입니다. React 나 Vue 가 그 예입니다.

번들이 내려오면 라이브러리는 컴포넌트를 처음부터 다시 돌립니다. 그러면 화면이 어떻게 생겨야 하는지가 메모리에 다시 그려집니다. 많은 라이브러리가 이것을 가상 DOM 트리로 그립니다. 가상 DOM 은 화면의 모습을 자바스크립트 객체로 그려 둔 도면입니다.

그다음 라이브러리는 이 도면과 이미 있는 DOM 을 뿌리부터 나란히 훑습니다. 도면의 button 하나에 DOM 의 button 하나를 짝짓습니다. 짝이 맞으면 DOM 노드는 손대지 않고 이벤트 핸들러만 붙입니다.

끝까지 훑고 나면 화면이 브라우저 코드의 손에 들어옵니다. 그 뒤로 데이터가 바뀌면 브라우저가 화면을 고칩니다. 처음부터 브라우저가 그린 클라이언트 사이드 렌더링 페이지와 같은 처지가 됩니다.

새로 그리지 않고 짝을 맞추는 까닭

버튼을 살리는 더 단순한 길이 있습니다. 이 소절은 그 길과 견줘 하이드레이션이 무엇을 아끼는지 봅니다.

단순한 길은 서버가 보낸 화면을 지우고 브라우저에서 처음부터 다시 그리는 것입니다. 이러면 이벤트 핸들러가 붙은 새 화면이 생깁니다. 대신 이미 보이던 화면이 한 번 사라졌다 다시 나타납니다. 사용자 눈에는 화면이 깜박입니다.

지우는 동안 사용자가 한 일도 사라집니다. 입력칸에 치던 글자와 내려간 스크롤 위치가 처음으로 돌아갑니다.

다시 그리면 브라우저가 칠하는 일도 처음부터 다시 합니다. 요소마다 크기와 위치를 계산하는 단계가 레이아웃입니다. 그 결과대로 화면을 칠하는 단계가 페인트입니다. 새로 그리면 둘 다 화면 전체에 걸쳐 돕니다.

하이드레이션은 이미 있는 DOM 노드를 다시 씁니다. 노드를 새로 만들지 않으니 깜박임이 없습니다. 레이아웃과 페인트도 화면 전체에 걸쳐 다시 돌지 않습니다. 브라우저가 더 하는 일은 컴포넌트를 돌리고 짝을 맞추는 계산입니다.

서버와 브라우저의 결과가 어긋날 때

짝 맞추기는 서버와 브라우저가 같은 화면을 그린다는 약속 위에 섭니다. 이 소절은 그 약속이 깨지는 예와 피하는 길을 봅니다.

같은 코드라도 도는 곳이 다르면 결과가 갈릴 수 있습니다. 서버에는 브라우저 창이 없습니다. 지금 시각도 서버가 요청을 처리한 순간과 브라우저가 코드를 돌린 순간이 다릅니다.

JavaScript
// 서버에서 돌 때
typeof window            // 'undefined'
new Date().getSeconds()  // 1

// 브라우저에서 돌 때
typeof window            // 'object'
new Date().getSeconds()  // 3

첫 줄은 브라우저 창 객체가 있는지 묻습니다. 둘째 줄은 지금이 몇 초인지 묻습니다. 이런 값을 화면에 그리면 서버가 보낸 HTML 과 브라우저의 도면이 서로 다른 글자를 갖게 됩니다.

짝이 안 맞는 이런 경우를 하이드레이션 불일치(hydration mismatch)라고 부릅니다. 라이브러리는 대개 경고를 남깁니다. 어긋난 부분을 버리고 브라우저 쪽 결과로 다시 그리기도 합니다. 그러면 그 부분에서는 하이드레이션이 아끼려던 깜박임과 다시 그리는 일이 돌아옵니다.

피하는 길은 첫 화면을 그리는 코드가 어디서 돌든 같은 값을 내게 하는 것입니다. 시각처럼 갈리는 값은 서버가 정해 데이터로 함께 보냅니다. 브라우저에서만 얻을 수 있는 값은 하이드레이션이 끝난 뒤에 읽어 화면을 고칩니다.

화면은 보이는데 안 눌리는 구간

하이드레이션의 대가는 시간과 코드 양으로 나타납니다. 이 소절은 사용자가 겪는 공백과 서버·브라우저가 두 번 하는 일을 봅니다.

HTML 이 도착해 화면이 보인 때부터 하이드레이션이 끝날 때까지 페이지는 보이기만 합니다. 이 사이에 버튼을 누르면 아무 일도 일어나지 않습니다. 사용자는 페이지가 고장 났다고 느끼기 쉽습니다.

이 공백은 번들이 클수록 길어집니다. 번들을 내려받는 시간과 자바스크립트를 해석하는 시간과 컴포넌트를 다시 돌리는 시간이 다 더해집니다. 휴대폰처럼 처리 능력이 낮은 기기에서는 더 길어집니다.

페이지가 언제부터 눌리는지 재는 지표가 따로 있습니다. TTI(Time to Interactive, 상호작용 가능 시점)가 그 하나입니다. 화면이 처음 보인 시점과 이 시점 사이가 벌어질수록 공백이 긴 것입니다.

일도 두 번 합니다. 서버가 컴포넌트를 돌려 HTML 을 만들었습니다. 브라우저도 같은 컴포넌트를 다시 돌려 같은 결과를 계산합니다.

내려가는 것도 둘입니다. 화면을 글자로 적은 HTML 이 갑니다. 그 화면을 다시 계산할 번들도 함께 갑니다.

하이드레이션이 필요한 페이지

모든 웹 페이지가 하이드레이션을 거치지는 않습니다. 이 소절은 화면을 어디서 그리느냐에 따라 필요한지가 어떻게 갈리는지 표로 봅니다.

페이지 하이드레이션 까닭
서버가 그려 보내고 상호작용이 있다 필요 보이는 화면에 동작을 붙여야 한다
서버가 그려 보내고 상호작용이 없다 필요 없음 붙일 동작이 없다
브라우저가 처음부터 그린다 필요 없음 짝을 맞출 서버 HTML 이 없다

둘째 줄은 블로그 글이나 설명서처럼 읽기만 하는 페이지입니다. 이런 페이지에 번들을 보내 하이드레이션하면 대가만 치릅니다. 셋째 줄은 [[클라이언트 사이드]] 렌더링입니다. 이 페이지는 거의 빈 HTML 을 받습니다. 브라우저가 DOM 을 처음부터 만들므로 짝을 맞출 것이 없습니다.

하이드레이션을 덜 하는 방법

대가를 줄이려는 방법이 여럿 나와 있습니다. 공통점은 브라우저가 다시 돌리는 코드의 양을 줄이는 것입니다. 이 소절은 이름과 줄이는 것만 표로 짚습니다.

방법 줄이는 것
점진적 하이드레이션 컴포넌트마다 때를 나눠 필요한 조각부터 살린다
부분 하이드레이션 상호작용이 있는 컴포넌트만 살리고 나머지는 HTML 로 둔다
아일랜드 아키텍처 페이지를 정적 HTML 로 짓고, 살아 있는 조각(섬)마다 번들을 따로 받아 저마다 살린다
서버 컴포넌트 서버에서만 도는 컴포넌트를 번들에 싣지 않고 하이드레이션도 건너뛴다

부분 하이드레이션은 무엇을 살리느냐를 말합니다. 아일랜드 아키텍처는 그렇게 되도록 페이지를 처음부터 섬 단위로 나눠 짓는 설계입니다. 부분 하이드레이션을 이루는 방법의 하나로 볼 수 있습니다.

넷 다 「페이지 전체를 한 번에 다시 돌린다」를 깨는 방향입니다. 다시 돌릴 조각을 고르거나 때를 늦춥니다.

같은 이름을 쓰는 ORM 의 하이드레이션

백엔드 코드에서도 이 낱말을 만납니다. 뜻이 달라 따로 짚습니다.

ORM(Object-Relational Mapping, 객체-관계 매핑)은 데이터베이스의 행을 프로그램의 객체로 옮겨 주는 도구입니다. 몇몇 ORM 은 조회 결과의 행을 읽어 객체의 필드를 채우는 단계를 하이드레이션이라고 부릅니다. 빈 객체를 만든 뒤 열 값을 하나씩 넣어 쓸 수 있는 객체로 만드는 일입니다.

두 뜻은 껍데기에 내용을 채워 쓸 수 있게 만든다는 그림을 함께 빌렸습니다. 하는 일은 서로 관계가 없습니다. 웹 화면의 하이드레이션은 앞 소절들이 다룬 뜻입니다.

관련 항목

하이드레이션이 뒤따르는 렌더링 방식

서버 사이드 렌더링 · 클라이언트 사이드 렌더링 · 정적 사이트 생성기 · 프리렌더링 · 스트리밍 SSR · 렌더링 · 단일 페이지 애플리케이션

하이드레이션이 다루는 브라우저 구조

DOM · HTML · 브라우저 · JavaScript · 이벤트 · 이벤트 핸들러 · 이벤트 위임

하이드레이션이 거치는 처리 단계

번들 · 직렬화 · 가상 DOM · 재조정 · 렌더와 커밋 · hydrateRoot

하이드레이션을 이루는 화면 코드 단위

컴포넌트 · 상태 관리 · props · JSX

하이드레이션 비용을 드러내는 오류와 지표

하이드레이션 불일치 · TTI · 첫 콘텐츠풀 페인트 · 총 차단 시간 · 다음 페인트까지의 상호작용 · 레이아웃 · 페인트

하이드레이션을 줄이는 렌더링 기법

점진적 하이드레이션 · 부분 하이드레이션 · 아일랜드 아키텍처 · 서버 컴포넌트 · 재개 가능성 · 코드 분할 · 지연 로딩

하이드레이션을 구현한 화면 라이브러리

React · Vue.js · Next.js · Nuxt · Svelte · Astro · Qwik

같은 낱말을 쓰는 ORM 쪽 개념

ORM · 엔티티 · 결과 집합 · 역직렬화

다른 이름: hydration · hydrate · 하이드레이트 · 리하이드레이션 · rehydration