본문 바로가기

Front-End

Hydrate가 무엇이고 어떻게 해결할까?

오늘 블로그에서 다룰 내용은 제가 최근에 Next에서 개발을 하다가 Hydrate가 발생해서 이러한 개념은 왜 나타나는 것인지, 또 어떻게 해결을 해야 할지 궁금증이 생겼습니다. 많은 개발자들은 이미 많이 들어본 용어가 아닐까 싶습니다. 하지만 이러한 개념이 무엇이고, 어떻게 해결하는지에 대해서 모르거나 더 궁금해하실 개발자님들이 있을 것 같아 이번 블로그에서는 이 Next.js에서 Hydrate가 무엇인지에 대해서 알아보겠습니다! 🔥

 

먼저, 시작하기에 앞서서 다들 CSR과 Pre-Rendering의 차이는 대충 뭔지 알고 계시죠??
간략하게 설명을 해보겠습니다.

 

CSR

React에서는 CSR 방식을 사용하는 거 다들 아시죠? 처음 브라우저가 빈 html 파일을 받아서 실제로 UI에

보이지 않다가, 유저의 화면이 렌더링이 완료 된다면 한 번에 화면을 보여줄 수 있는 것입니다.

 

Pre-Rendering

SSR과 SSG의 공통된 개념을 Pre-Rendering이라고 합니다. Next 정보는 아래로 👇

 

 

언제까지 React 쓸래?? 이제는 Next 사용해야지

혹시 웹 프론트엔드 분야를 하고 있는 개발자님들에게 궁금한 점이 있습니다. 혹시 프로젝트를 하실 때 리액트를 쓰시나요?? 아니면 Next를 사용하시나요?? 저는 Next가 아닌 React로만 프로젝트를

ltr2006.tistory.com

 

이러한 Next의 Pre-Rendering은 모든 페이지를 미리 렌더링을 한다는 특징을 가지고 있는데, 이는 Next가 모든 일을 클라이언트 측에서 수행을 하는 것이 아닌, 각 페이지의 html을 미리 생성을 하고 그 파일과 자바스크립트 코드와 연결을 한다. 유저가 브라우저에 접속을 하게 될 경우에 코드가 실행되어 유저 간의 상호작용이 된다.

 

그래서 SSG? SSR? 뭐가 다른데?

Next에서는 미리 렌더링을 하는 방식은 2가지입니다. 

빌드 타임에 html이 생성이 되어서 매 요청마다 이를 재사용을 하는 SSG(Static-site Generation)

다른 하나는 매 요청마다 html을 생성하는 SSR(Server-side Rendering)

빌드 시에 생성하는 것을 재사용 vs 요청마다 생성

 

Hydrate란 무엇일까?

Next에서 UI가 렌더링을 하는 과정에서 자바스크립트 코드들이 html DOM 요소들과 매칭이 되는 개념

 

결국에는 Next는 클라이언트에서 웹 페이지를 보내기 전에 SSR에서 Pre-Rendering 된 html 파일을 

클라이언트에게 전달을 한다 이후에 리액트가 번들링 된 자바스크립트 코드를 클라이언트에게 전송한다.

자바스크립트 코드 이전에 보내진 html DOM 요소 위에서 한 번 더 렌더링을 하면서 각자의 요소로 찾아간다.

이를 통해서 페이지가 정상적으로 작동을 하게 되는 것이고, 이를 Hydration이라고 한다. 

 

자료를 찾아보면서 두 번이나 렌더링을 하면 안 좋은 것이 아닐까?라고 생각이 들었다.

하지만 Pre-Rendering가 된 문서는 자바스크립트의 요소들이 배제된 아주 가볍고 작은 파일이라고 한다.

그래서 사용자에게 빠르게 로드가 되는 웹 페이지를 제공할 수 있다고 한다. 그리고 Client-Side에서 자바스

크립트의 파일을 렌더링을 할 경우에는 각 DOM 요소에 자바스크립트 속성만 매칭하지 웹 페이지를 다시 

레이아웃을 잡고 배치시키는 paint() 함수까지는 실행이 안 된다고 한다.

 

HydrationNext에서만 발생이 될까?

No.

 

결국에는 Next에서만 발생하는 동작이 아니라, ReactDom에서 발생하는 함수이다.

즉, UI를 실제로 브라우저가 렌더링 할 때 사용하는 라이브러리라는 것이다.

 

React에서는 render함수뿐만이 아니라 hydrate라는 함수가 존재합니다.

render, hydrate는 React 18에서 createRoot, hydrateRoot로 대체됩니다.
ReactDOM.hydrate()

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

 

render()와 메서드는 동일하지만 ReactDOMServer에서 HTML을 렌더링 한 container을 hydrate 하기 위해 사용을 한다. SSR을 통해서 마크업이 있는 경우에는 다시 렌더링을 하지 않고, hydrate를 통해서 기존 마크업에 이벤트를 추가한다. 기존 DOM Tree에서 해당되는 DOM요소를 찾아서 정해진 자바스크립트 속성을 적용시킨다는 것이다.

 

두 번째 인자는 특정 컴포넌트를 넣는데, DOM 요소에 하위로 hydrate가 된다는 특징이 있고, 렌더링을 통해 새로운 웹 페이지를 구성할 DOM은 생성하지 않는다.

 

내가 겪었던 문제

기존에 전역 상태 관리인 Recoil atom를 사용하면서 겪은 문제인데,  refresh를 할 경우에는 기존 value가 사라지는 것을 알 수 있는데, 이러한 문제를 해결하고나 recoil-persist를 사용하려고 하였다. 하지만 render 하는 페이지에서 hydration 에러가 발생하였다.

위와 같은 에러가 발생하였다. hydration 에러를 겪어본 적이 없기에 공식 문서를 참고하여서 해결하였다.

 

Next 문서 바로 가기 ⬇️

 

Text content does not match server-rendered HTML

Using App Router Features available in /app

nextjs.org

아무대로 렌더링 로직 window와 같은 브라우저 전용 API 사용 localStorage에서 걸린 것 같다.

 

해결방법으로는 useEffect 클라이언트에서만 실행하는 데 사용하는 방법으로 채택을 하였다.

구성 요소가 초기 클라이언트 측 렌더링 중에 동일한 콘텐츠 서버 측을 렌더링 하는지 확인하는 것이다.

아래와 같이 작성하였다.

 

const SchoolListPage = () => {
  const schoolFilterText = useRecoilValue(SchoolFilterText)
  const { data, refetch } = useGetClubList(schoolToConstants[schoolFilterText])

  useEffect(() => {
    refetch()
  }, [schoolFilterText])

그리하여서 React Hydration 중에 useEffect가 호출이 되고 windo 수화 불일치 없이 사용 가능한 브라우저 API이다.

 

또 다른 방법도 있기 때문에 실제 Next.js 공식 문서를 참고하는 것을 추천한다 ⭐️

 

느낀 점

아직 사실 이해를 하지 못 한 부분도 있고, 에러가 발생할 때 당황했던 기억도 있는데 조금 더 자세히 알아보고 공식문서를 뒤져서 조금 더 알아볼까 한다. 추가적으로 Pre-Rendering을 알아봐야겠다.