Hydration 정의와 React render / 브라우저 Paint 구분
프론트엔드에서 rendering이라는 단어를 계속 마주치는데, 쓰이는 자리마다 가리키는 대상이 달랐다. 헷갈리는 채로 두면 Hydration이 뭔지도 끝까지 흐릿할 것 같아서 네 가지로 나눠 봤다.
- HTML 생성 — 누가 HTML 마크업을 만드는가
- HTML 파싱 — 브라우저가 HTML 문자열을 DOM으로 어떻게 바꾸는가
- 브라우저 렌더링 — DOM과 CSS로 실제 화면의 픽셀을 어떻게 만드는가
- React 렌더링 — React가 컴포넌트 트리를 계산하고 DOM을 어떻게 구성·변경할지 결정하는 과정
픽셀을 그리는 주체는 끝까지 브라우저였다
브라우저가 HTML과 CSS를 받아서 화면을 만들기까지는 여러 단계를 거친다. HTML과 CSS가 각각 따로 파싱된 뒤 합쳐지는 구조다.
HTML Parser는 HTML 문자열을 분석해 DOM을 만든다. 이런 마크업이 있다면,
<body>
<div id="root">
<h1>Hello</h1>
</div>
</body>브라우저 안에서는 이런 트리가 된다.
Document
└── body
└── div#root
└── h1
└── "Hello"CSS 쪽은 CSSOM(CSS Object Model, 스타일 규칙을 브라우저가 다룰 수 있는 객체 트리로 만든 것)이 된다. DOM과 CSSOM을 합치면 실제로 화면에 그려질 요소만 모인 Render Tree가 나온다. 그 위에서 각 요소의 위치와 크기를 계산하는 Layout, 색과 텍스트를 칠하는 Paint가 진행된다. Composite는 여러 레이어를 합성하는 단계인데, 항상 거치는 고정 단계가 아니라 경우에 따라 수행된다.
HTML을 파싱하고 픽셀을 그리는 주체는 브라우저다. 서버는 어느 쪽에도 끼어 있지 않다.
CSR은 빈 root에서 시작한다
CSR에서 서버가 주는 HTML은 대체로 껍데기(shell)다. 실제 UI가 될 마크업은 거의 없다.
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
<script src="/assets/main.js"></script>
</body>
</html>그래서 브라우저가 이 HTML을 파싱해 만든 DOM에는 비어 있는 #root 하나만 있다. 화면에 뭔가 보이려면 JavaScript가 실행되고 React가 DOM을 만들어 넣는 단계를 더 기다려야 한다.
React에서 그 시작점은 이 한 줄이다.
createRoot(document.getElementById("root")!).render(<App />);이때 React가 빈 root에 UI를 만들어 넣는 것은 Hydration이 아니다. 아무것도 없던 자리에 DOM을 새로 만드는 client-side rendering이고, mounting이라고 부르는 쪽이 맞다.
Hydration이 붙잡는 건 이미 만들어진 DOM이다
SSR에서는 서버에서 React가 컴포넌트를 실행해 마크업이 채워진 HTML을 만들어 보낸다. 브라우저가 받는 건 빈 껍데기가 아니라 내용이 들어 있는 HTML이다.
<div id="root">
<h1>Hello</h1>
<button>Click</button>
</div>브라우저는 이걸 파싱해 DOM을 만들고 곧바로 화면에 표시할 수 있다. CSR과 갈리는 지점이 여기다. JavaScript를 기다리지 않아도 화면은 이미 나와 있다.
브라우저 입장에서 이건 그냥 DOM이다. React는 아직 이 DOM과 아무 관계가 없다.
- 어떤 컴포넌트에서 나온 DOM인가
- 어떤 이벤트가 연결되어 있는가
- state는 무엇인가
- 이후 업데이트가 발생하면 어떤 부분을 변경해야 하는가
이 질문들에 답할 수 있게 하려면 브라우저에서 React가 실행되면서 기존 DOM과 자신의 컴포넌트 트리를 연결해야 한다. 그 연결 작업이 Hydration이다.
화면이 먼저 나오고, JavaScript가 도착한 뒤에 Hydration이 일어난다. Hydration으로 HTML이 채워지는 게 아니다. 서버가 만들어준 HTML로 이미 존재하는 DOM에 React가 붙는다.
React의 render는 브라우저의 Paint가 아니다
React에서 render라는 단어는 브라우저의 Paint와 같은 뜻이 아니다. React의 render는 컴포넌트를 실행해 React Element를 만들고, React Tree를 계산해서 어떤 UI 결과가 필요한지 정하는 단계까지다. 이 단계에서는 화면에 아무 일도 일어나지 않는다.
실제 DOM 변경이 필요하면 그다음 Commit 단계에서 DOM에 반영되고, 그제야 브라우저가 자기 렌더링 과정을 시작한다.
경계는 Commit에 있다. Commit이 DOM을 바꾼 뒤에야 브라우저가 Layout과 Paint를 돌린다.