[Next.js] 서버 렌더링과 초기 UI 불일치로 인한 Hydration 에러 해결 방법

[Next.js] 서버 렌더링과 초기 UI 불일치로 인한 Hydration 에러 해결 방법

·

2 min read

라이북러리 프로젝트를 진행하면서 한 가지 에러가 발생하였다.

Hydration failed because the initial UI does not match what was rendered on the server.

한글로 번역하면 "초기 UI가 서버에서 렌더링된 것과 일치하지 않아 하이드레이션이 실패했습니다."이다.

대체 Hydration이 뭐고, 왜 초기 UI와 서버에서 렌더링 된 UI가 일치하지 않은 걸까?

🟢 Hydration이란?

React는 CSR만 존재하기 때문에 HTML, CSS 그리고 자바스크립트 모두 render()를 이용해 생성하여 모든 리소스를 한번에 렌더링한다.

반면, Next.js는 서버에서 보여줄 HTML을 미리 렌더링하여 가져오기 때문에 render()함수로 HTML 뼈대만 렌더링하고, hydrate()를 통해 서버에서 받아온 HTML에 자바스크립트를 연결한다.

Hydration의 사전적 정의는 "수분 공급"인데, HTML에 자바스크립트를 주입한다는 의미에서 Hydration이라는 용어를 사용하게 된 것이다.

🔴 에러가 발생한 이유?

Next.js 공식문서에서 Hydration 에러가 발생할 수 있는 케이스가 나열되어 있다.

나에게 해당하는 케이스는 바로 2번, typeof window !== 'undefined'를 사용한 것이다.

function Library() {
  ...
  const userToken = typeof window !== 'undefined' ? localStorage.getItem('userToken') : ''

  if (userToken) {
    return ...
    }

  return ...

Library 컴포넌트에서는 userToken에 따라 다른 요소를 반환한다. 이때 userToken은 localStorage 값에 따라 정해진다.

처음에는 typeof window !== 'undefined' 조건을 사용하지 않았다. 하지만 localStorage가 window(브라우저) 글로벌 객체이기 때문에 Next.js가 브라우저에 접근하지 않을 땐 localStorage가 존재하지 않는다는 에러를 발생시켰다.

따라서 typeof window !== 'undefined'를 사용한 것이다.

userToken 값이 바뀌는 과정은 다음과 같다.

  1. Node에서 실행 시 최상위 객체가 window가 아니므로, userToken빈 문자열이 된다.

  2. 브라우저 실행 시 최상위 객체가 window가 되므로, userTokenlocalStorage 값이 된다.

  3. 1번과 2번의 HTML 결과가 다르므로 에러가 발생한다.

🟢 해결 방법

더 이상window객체에 의존하지 않도록 한다.

Library 컴포넌트가 브라우저에 렌더링 되었을 때 userToken에 localStorage 값을 부여하는 것이다.

function Library() {
  ...
  const [userToken, setUserToken] = useState('')

  useEffect(() => {
    setUserToken(localStorage.getItem('userToken') || '')
  }, [])

  if (userToken) {
    return ...
    }

  return ...

정리하자면 다음과 같다.

  1. Node와 브라우저 실행 시, 둘 다 userToken빈 문자열로 초기화된다.

  2. Library 컴포넌트가 처음 렌더링되면 userTokenlocalStorage 값이 된다.


Text content does not match server-rendered HTML https://nextjs.org/docs/messages/react-hydration-error

Next.js의 렌더링 과정(Hydrate) 알아보기 https://www.howdy-mj.me/next/hydrate