Hydration failed because the initial UI does not match what was rendered on the server 에러가 발생했던 경우들 🤔

·2024년 9월 23일

더취페이 프로젝트

목록 보기
18/37

오늘은 작업하면서 발생했던 error 중에 Hydration 관련해서 발생한 error의 2가지 상황에 대해서 정리하겠다. 에러는 둘 다 Hydration failed because the initial UI does not match what was rendered on the server로 같은데 발생 원인은 달라서 정리해보고자 한다.

hydration


hydration의 뜻에서 알 수 있듯이 수분 공급을 해주는 작업을 hydration이라고 한다.
react에서 의미하는 hydration은 서버에서 렌더링된 HTML 마크업에 기반하여 클라이언트 측에서 자바스크립트 이벤트와 상태를 연결하는 과정을 의미한다.

react는 기본적으로 클라이언트 렌더링 방식으로 실행되어 서버로부터 빈 페이지를 받고, 모든 렌더링이 브라우저에서 발생하지만, Next.js와 같은 프레임워크에서는 서버에서 렌더링된 HTML을 클라이언트로 보내고, 클라이언트에서는 해당 HTML을 가져와 자바스크립트 이벤트와 상태를 연결하는 과정을 거쳐야 한다. -> 서버 사이드 렌더링(SSR)

  1. 서버에서 Next.js를 사용하여 react 애플리케이션을 렌더링하고, 생성된 HTML을 클라이언트로 전송한다.
  2. 클라이언트에서는 전송된 HTML을 가져와서 기존 마크업과 일치시킨다.
  3. 일치하는 컴포넌트를 찾아 해당 컴포넌트의 이벤트 처리 및 상태 업데이트를 활성화한다.
  4. 클라이언트에서 이제 일반적인 react 애플리케이션처럼 동작하며, 상호작용과 상태 변화에 따라 UI를 업데이트할 수 있다.

hydration 발생 원인

Hydration 오류는 서버에서 렌더링된 HTML과 클라이언트에서 렌더링된 HTML이 다를 때 발생한다.

  1. 서버에서는 페이지를 렌더링할 때 동적인 데이터를 아직 가져오지 못한 상태일 수 있지만, 클라이언트에서는 자바스크립트가 로드된 후 데이터를 가져와서 새롭게 렌더링을 한다. 이 과정에서 서버와 클라이언트의 HTML 구조나 내용이 달라질 수 있다.

  2. 브라우저에서만 존재하는 객체들(window, document)을 서버에서 사용할 수 없기 때문에 이런 코드를 서버에서 실행하면 문제가 발생하고, 클라이언트에서 실행된 결과와 다르기 때문에 HTML이 불일치하게 된다.

  3. 서버와 클라이언트가 각각 다른 조건에 따라 다르게 렌더링하면 오류가 발생한다. 예를 들어, 서버에서는 특정 조건이 false라서 렌더링되지 않았지만, 클라이언트에서는 true로 변경되어 다른 요소가 렌더링될 수 있다.

해결 방안

Hydration 오류가 발생하지 않도록 하기 위해서는 다음과 같은 방법이 있다.

  1. 동적 데이터를 처리할 때 초기 상태를 일관되게 유지: 서버와 클라이언트에서 일관된 초기 값을 제공하여 서버에서 렌더링되는 내용과 클라이언트에서의 내용이 최대한 같도록 한다.

  2. useEffect로 브라우저 전용 로직 실행: window, document와 같은 브라우저 전용 객체는 클라이언트에서만 실행되도록 useEffect로 감싸서 서버에서는 실행되지 않게 한다.

  3. SSR 비활성화 (next/dynamic 사용): 클라이언트에서만 렌더링이 필요한 컴포넌트나 로직은 next/dynamic을 사용해 SSR을 비활성화할 수 있다.

table 태그

table과 관련한 오류는 상대적으로 간단한 이유였다.
일부 브라우저는 렌더링 과정에서 테이블의 HTML 구조를 자동으로 수정할 수 있다. 예를 들어, thead나 tbody가 없으면 브라우저는 이를 암묵적으로 추가할 수 있다. 서버에서 thead, tbody 없이 테이블을 렌더링했지만, 클라이언트 브라우저는 이 태그들을 자동으로 추가하여 렌더링했기 때문에 서버와 클라이언트의 DOM이 달라져 hydration 오류가 발생하게 된다.

즉, thead와 tbody 태그들을 추가해주면 바로 해결된다.

react-quill

Next.js에서 react-quill 모듈을 클라이언트 전용으로 로드하지 않아서 발생한 오류이다.
다음과 같이 ReactQuill을 import 해주어서 사용하고 있었다.

import ReactQuill from 'react-quill';

react-quill은 브라우저의 window, document 객체와 같은 클라이언트 전용 API에 의존하기 때문에 서버 측에서는 이러한 객체들이 존재하지 않기 때문에, 서버에서 이 컴포넌트를 렌더링하면 오류가 발생하게 된다.

import dynamic from 'next/dynamic';

const ReactQuill = dynamic(() => import('react-quill'), {
  ssr: false,
});

다음과 같이 수정해주면, 클라이언트 전용 컴포넌트는 Next.js의 dynamic 함수를 사용하여 클라이언트 측에서만 로드하도록 할 수 있다.

react-quill hydration 관련해서 다르게 해결 방법을 알려주는 게시글이 많은데 대부분 해결이 안 되고 dynamic으로만 해결이 됐다. 방법이 어렵지도 않고, 이해가 안 가는 부분이 아니기 때문에 클라이언트에 의존하는 모듈은 클라이언트에서 로드해주도록 하자.

참고 자료

hydrate가 무엇인가요?(react, next.js)
[Next.js] 테이블(table) 렌더링 시 하이드레이션(Hydration) 에러

profile
Frontend🍓

0개의 댓글