[Next.js] Hydration 에러 핸들링

수툴리 양·2022년 12월 16일
0

TIL(2)

목록 보기
1/8

배경
프론트 주니어로서 웹팩, 바벨 등을 직접 셋업해보며 이해를 기르고자 프로젝트를 시작했다.
React18과 Next.js를 사용하고 있고, TypeScript를 엄격히 적용하고자 한다.
config 파일 등으로 필요한 기본 설정 후 페이지를 구성하고 컴포넌트를 하나씩 만들어 가다 보니 빈 페이지에서는 없던 에러가 많이 등장할 것. 에러핸들링을 하며 알게된 것들을 기록하고자 한다.

Hydration 에러

누군가는 Next.js를 처음 사용할 때 window객체가 undefined라는 에러를 본 적이 있을 것이다.
(window는 브라우저가 제공하는 객체이니, 서버사이드 렌더링과 충돌할 수 밖에 없다.)

나의 경우에는 <table/> 객체를 직접 사용하여 hydration 에러가 발생했다.

Expected server HTML to contain a matching <table> in <div>.
  • thead 태그 속성
(property) JSX.IntrinsicElements.thead: React.DetailedHTMLProps<React.HTMLAttributes<HTMLTableSectionElement>, HTMLTableSectionElement>
Hydration failed because the initial UI does not match what was rendered on the server.

hydration 이란?

서버사이드에서 pre-rendering한 정적페이지와, 번들링(압축)된 javascript코드를 클라이언트에 보내고, 자바스크립트 코드가 정적페이지 (정확히는 html코드를 파싱한 dom tree) 위에 다시 렌더링되면서 매칭되는 과정

즉 위 에러 'initial UI does not match ... on the server.'가 직관적으로 에러를 알려주고 있는 것.

  • 이 에러는 서버사이드에서 pre-rendering된 React DOM-tree와 클라이언트 사이드의 리액트 돔트리와 달라서 발생
  • 컴포넌트가 어떤 조건에서만 리턴(렌더)하도록 했을 때도 같은 오류. (서버에서 렌더링하여 나오는 결과물인 정적페이지 과정까지에서는 브라우저 내장객체를 당연히 참조할 수 없으니까)
참고: [블로그](https://velog.io/@hyeonq/Next.js-Hydration-failed-error)
  • 해결 방법

    ⇒ Next.js의 dynamic 컴포넌트를 사용하거나*

    ⇒ 클라이언트 사이드 렌더링 시 실행되는 useEffect 안에서 window 객체 참조


    *다만 dynamic 컴포넌트를 사용해도 해당 컴포넌트 렌더가 두번 되는 이슈가 발생

    ⇒ dynamic 컴포넌트로 import해올 때 { SSR: false } 옵션을 설정해야 함
    (클라이언트 사이드 렌더링을 한다는 의미)

    *여기에{ suspense: true } 옵션도 함께 설정해야 한다는 의견도 있음 github 🔗



#HTML != DOM

HTML과 DOM의 차이

  • HTML은 태그, 속성(attributes)로 구성하는 규칙이 있는 언어
  • 브라우저가 HTML을 파싱하여 DOM tree를 그린다.
  • DOM(Document Object Model)은 객체모델로, document에 접근이 가능한 API임
    • 그래서 텍스트, 스타일, 노드 구조도 바꿀 수 있는 것 (getElmentById, innerText.. 등)

      *document 는 root node의 추상화된 개념인 반면 getElementById, getElementByClassName, parentNode, removeChild와 같은 메소드는 HTML DOM API에서 비롯되었다고 함

    참고 블로그: https://velog.io/@ddinggu/HTML-DOM-간단-정리, https://devlibrary00108.tistory.com/46

즉 우리가 웹 페이지의 컨텐츠를 동적으로 수정하고 싶으면 DOM을 수정하는 것임


  • *React.createElement() 메소드로 만들 수 있는 React element는 dom element의 가상버전이라고 보면된다.

  • React.createClass메소드를 사용하고 state가 있는게 차이점이다.

  • HTML스러운JSX블럭은 상태값을 가질 수 있는 render메소드를 반환하고 가장 좋은 것은 state가 바뀔때마다 컴포넌트가 다시 그려진다는 점이다.

    => 동적인 HTML디자인의 매우 좋은 도구이고 Components가 가상돔에 직접접근할 수는 없지만 ReactElement로 쉽게 컨버팅해 사용한다.

  • 리렌더링 과정: ReactComponent (상태값을 가지고 있어 변화가 일어나면) → ReactElement* 로 컨버팅 → 변화된 부분을 virtual dom 차원에서 확인 → html dom 으로 변환 → DOM에 업데이트 → 브라우저가 최적화
profile
developer; not kim but Young

0개의 댓글