개발 블로깅/Next.js

Next.js의 Hydrate란?

Hello이뇽 2021. 8. 8. 23:00

 

 

 

Next.js 프레임워크의 동작원리를 제대로 파악하고 있는 개발자라면 Hydrate에 대해선 이미 익숙한 용어일 것이다. 

그러나 Next.js의 주요 동작 방식 중 하나임에도, 눈에 잘 띄지 않아 놓치기도 쉬운 개념인 만큼, 한번 제대로 정리를 하고 넘어가보려고 한다.

 

Hydrate는 Server Side 단에서 렌더링 된 정적 페이지와 번들링된 JS파일을 클라이언트에게 보낸 뒤, 클라이언트 단에서 HTML 코드와 React인 JS코드를 서로 매칭 시키는 과정을 말한다.

 

출처: https://aboutmonica.com/blog/server-side-rendering-react-hydration-best-practices

 

이 과정이 왜 필요한지 간략하게 설명하기 위해, 우선 React에 대해 잠깐 얘기해보자.

 

React의 웹  페이지 구성 원리

React는 JS파일만을 이용하여 웹 화면을 구성하는 원리를 가지고 있다. 그래서 실제 HTML 코드는 안에 내용이 하나도 없는 상태이다. (Client Side Rendering이 SEO에 적합하지 않은 이유이기도 하다.) 

 

// public/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

 

위 코드는 처음 리액트 프로젝트 세팅할 때 많이 본 익숙한 HTML 코드일 것이다.

단순 뼈대만 있는 HTML document와 JS 파일들을 클라이언트로 모두 보낸 뒤, 클라이언트 단에서 JS 코드들을 통해 웹 화면을 렌더링 하며 페이지를 그리게 된다.

웹 페이지 렌더링을 한 뒤에도 페이지 내에서 동작하는 모든 이벤트 또한 자바스크립트로 인해 일어나게 된다.

 

 

// src/index.js

import React from "react";
import ReactDOM from "react-dom";
import App from './src/App';

ReactDOM.render(<App />, document.getElementById("root"));

 

위 코드처럼 public/index.html에는 아무 내용 없는 기본 뼈대만 있고, 나머지는 src/index.js의 자바스크립트 코드에서 모든 화면을 렌더링 한 뒤 HTML DOM 요소 중 root라는 아이디를 가진 엘리먼트를 찾아서 하위로 주입을 하게 된다.

 

Next.js의 웹  페이지 구성 원리

Next.js는 클라이언트에게 웹 페이지를 보내기 전에 Server Side 단에서 미리 웹 페이지를 Pre-Rendering 한다. 그리고 Pre-Redering으로 인해 생성된 HTML document를 클라이언트에게 전송한다. 

그런데 이 시점에서 중요한 것은 아래 내용이다.

현재 클라이언트가 받은 웹 페이지는 단순히 웹 화면만 보여주는 HTML일 뿐이고, 자바스크립트 요소들이 하나도 없는 상태이다. 이는 웹 화면을 보여주고 있지만, 특정 JS 모듈 뿐 아니라 단순 클릭과 같은 이벤트 리스너들이 각 웹 페이지의 DOM 요소에 하나도 적용되어 있지 않은 상태임을 말한다.

 

그러면 이렇게 페이지만 보여주고 동작조차 하지 못하는 마치 빈 껍데기 같은 웹 페이지가 나중에는 어떻게 정상적으로 동작하게 되는 것일까.

Next.js Server에서는 Pre-Rendering된 웹 페이지를 클라이언트에게 보내고 나서, 바로 리액트가 번들링 된 자바스크립트 코드들을 클라이언트에게 전송한다. 

 

 

네트워크 탭을 보면, 맨 처음 응답받는 요소가 document Type의 파일이고, 이후에 React 코드들이 렌더링 된 JS 파일들이 Chunk 단위로 다운로드되는 것을 확인할 수 있다.

그리고 이 자바스크립트 코드들이 이전에 보내진 HTML DOM 요소 위에서 한번 더 렌더링을 하면서, 각자 자기 자리를 찾아가며 매칭이 된다.

이 과정을 Hydrate라고 부른다.

이것은 마치 자바스크립트 코드들이 DOM 요소 위에 물을 채우 듯 필요로 하던 요소들을 채운다 하여 Hydrate(수화)라는 용어를 쓴다고 한다.

 

출처: https://qanda.ai/ko

 

아마 위의 GIF 이미지처럼 잠깐의 스타일 깜빡임이 Next.js에서 나타나는 일반적으로 많이 보는 현상일 것이다.

새롭게 페이지를 로딩할 때마다 약간 뒤늦게 스타일이 적용되는 듯한 이 과정이, HTML DOM 요소에 뒤늦게 자바스크립트가 동작하고 Hydration 돼서 나타나는 현상이다.

(정확하게는, 자바스크립트로 외부 서버에 웹폰트를 요청해서 받아오는데, Hydrate 이전에는 웹 폰트를 아직 요청하지 못해 적용되지 않아서이다.)

 

 

Server에서 한번 렌더링하고 Client에서도 한번 더 렌더링 하면 비효율적인 렌더링 방식 아닌가요?

어쩌면 두번 렌더링 하는 것은 비효율적으로 보일 수 있다.

그러나 서버 단에서 빠르게 Pre-Rendering하고 유저에게 빠른 웹 페이지로 응답할 수 있다는 것에 더욱 큰 이점을 가져갈 수 있다. 심지어 이 Pre-Rendering 한 Document는 모든 자바스크립트 요소들이 빠진 굉장히 가벼운 상태이므로 클라이언트에게 빠른 로딩이 가능하다.
이는 같은 화면에 대해 두 번 렌더링이 일어난다는 단점을 보완하고도 남는다.

더 나아가서 클라이언트 단에서 자바스크립트가 렌더링을 할 때, 단지 각 DOM 요소에 자바스크립트 속성을 매칭 시키기 위한 목적이므로 실제 웹 페이지를 다시 그리는 과정까지는 하지 않는다.(Paint 함수 호출 X)

 

Hydrate 과정은 Next.js에서만 일어나는 과정인가요?

사실 Hydrate는 Next.js에 종속된 동작이 아니라 ReactDOM 함수이다.

흔히 리액트 프로젝트 구축 시 초반에 꼭 작성해주는 ReactDOM.render() 함수와 잠깐 비교를 해보자.

ReactDOM.render(element, container, [callback]);

 

ReactDOM.render() 함수는 특정 컴포넌트를 두 번째 파라미터인 지정된 DOM 요소에 하위로 주입하여 렌더링을 처리해주는 함수이다. 
그리고 렌더링이 완료되면 특정 이벤트를 처리할 콜백 함수를 세 번째 파라미터로 넣어줄 수 있다.

 

ReactDOM.hydrate(element, container, [callback]);

 

ReactDOM.hydrate() 함수는 특정 컴포넌트를 두 번째 파라미터인 지정된 DOM 요소에 하위로 hydrate 처리만 한다. 이는 렌더링을 통해 새로운 웹 페이지를 구성할 DOM을 생성하는 것이 아니라, 기존 DOM Tree에서 해당되는 DOM 요소를 찾아 정해진 자바스크립트 속성(이벤트 리스너 등)들만 부착시키겠다는 말이다.

 

Hydrate에 대해 우리가 신경 써야 할 것이 있을까요?

사실 그냥 웹 페이지 및 일반 Feature 개발만 한다면 이 과정을 몰라도 Next.js를 쓰는 것에 큰 문제는 없지만, 우선 원리는 알면 좋다.
그리고 나는 겪어보진 않았었는데, 어찌어찌 구글링 하다가 Next.js에서 Hydrate로 인해 발생하는 스타일 이슈 같은 게 있나 보다. 

이와 관련된 자세한 내용은 내가 봤던 아래 블로그 링크로 남겨둔다.

 

Next.JS hydration 스타일 이슈 피해가기

Next.JS를 사용해 웹을 만들어가다보면, 어느 순간 Hydration 이슈를 마주치게 된다. 이번엔 그 상황이 언제, 왜 생겨나는지를 파악해보고, 이걸 피해가를 방법을 알아보자.

fourwingsy.medium.com

 

그리고 혹시나 Next.js 내에 Redux를 사용하게 된다면, 아마 Next-Redux-Wrapper라는 라이브러리를 이용하게 될 텐데, Reducer root Store에서 Hydrate를 처리해 주는 부분이 있다. 이는 Server Side 단에서 dispatch 했던 Store들을 클라이언트 단에서 그대로 사용할 수 있도록 Redux Store도 클라이언트 단에서 같이 Hydration이 필요하기 때문이다.

 

그리고 사실 지금와서 Hydrate에 대해 블로깅을 한 이유는, 이번에 웹 성능 최적화 작업을 하다가 이 Next.js Hydration에 대해 흥미로운 점을 발견하게 돼서 이에 대한 블로깅(Partial Hydration에 대해)을 하기 위해 미리 Hydrate에 대해 우선 정리를 했다.




반응형