[Next.js] Hydration failed because the initial UI does not match what was rendered on the server 에러

hyeonQyu·2022년 5월 28일
21
post-thumbnail

에러 발생

Nextjs에서 Portal을 만들던 중 다음과 같은 에러가 발생했습니다.

Unhandled Runtime Error
Error: Hydration failed because the initial UI does not match what was rendered on the server.

문제가 발생한 Portal의 코드는 아래와 같았습니다.

function Portal(props: PortalProps) {
    const { children } = props;

    if (typeof window !== 'object') {
      return <></>;
    }

    const element = document.getElementById('portal');

    if (!element) {
        return <></>;
    }

    return ReactDOM.createPortal(children, element);
}

중간에 아래와 같은 window의 type을 검사하는 코드가 있는데

if (typeof window !== 'object') {
  return <></>;
}

Next.js가 SSR 방식을 사용하기 때문에 이 코드가 없으면 아래의 오류가 발생했고 이를 해결하고자 window의 type 검사 코드를 넣으니 Hydration error가 발생한 것이었죠.
Server Error
ReferenceError: document is not defined
This error happened while generating the page. Any console logs will be displayed in the terminal window.

다시 본론으로 돌아와서 이 Hydration error가 발생한 이유를 알기 위해선 Hydrate가 무엇인지 알아야 합니다.

Hydrate

Hydrate를 간략하게만 설명하자면 서버사이드에서 rendering된 정적 페이지와 번들링된 js코드를 클라이언트에게 보낸 후 js코드가 HTML DOM 위에서 다시 rendering 되면서 서로 매칭되는 과정입니다.

즉 이 코드가 들어가면서

if (typeof window !== 'object') {
  return <></>;
}

서버사이드에서 pre-rendering된 React 트리와 브라우저에서 처음 rendering되는 React 트리가 달랐기 때문에 발생한 에러였죠.

해결

Next.js의 공식 사이트에서 해결 방법을 찾을 수 있었어요.
https://nextjs.org/docs/messages/react-hydration-error
문서를 참고하여 위에서 작성한 Portal 코드를 아래와 같이 수정했더니 문제가 해결되었습니다.

function Portal(props: PortalProps) {
    const { children } = props;
    const [element, setElement] = useState<HTMLElement | null>(null);

    useEffect(() => {
        setElement(document.getElementById('portal'));
    }, []);

    if (!element) {
        return <></>;
    }

    return ReactDOM.createPortal(children, element);
}

서버사이드에서 document 객체에 접근하는 것을 막기 위해 문제가 되는 코드를 삽입한 것이었는데 클라이언트사이드에서 실행되는 useEffect 내에서 document 객체를 참조하면 문제를 해결할 수 있었습니다.

profile
백엔드가 하고 싶었던 프론트엔드 개발자

1개의 댓글

comment-user-thumbnail
2023년 6월 2일

현규님 도움이 많이되었어요 감사합니다 ㅎㅎ

답글 달기