배경
프론트 주니어로서 웹팩, 바벨 등을 직접 셋업해보며 이해를 기르고자 프로젝트를 시작했다.
React18과 Next.js를 사용하고 있고, TypeScript를 엄격히 적용하고자 한다.
config 파일 등으로 필요한 기본 설정 후 페이지를 구성하고 컴포넌트를 하나씩 만들어 가다 보니 빈 페이지에서는 없던 에러가 많이 등장할 것. 에러핸들링을 하며 알게된 것들을 기록하고자 한다.
누군가는 Next.js를 처음 사용할 때 window객체가 undefined라는 에러를 본 적이 있을 것이다.
(window는 브라우저가 제공하는 객체이니, 서버사이드 렌더링과 충돌할 수 밖에 없다.)
나의 경우에는 <table/>
객체를 직접 사용하여 hydration 에러가 발생했다.
Expected server HTML to contain a matching <table> in <div>.
(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.
서버사이드에서 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 컴포넌트로 import해올 때 { SSR: false } 옵션을 설정해야 함
(클라이언트 사이드 렌더링을 한다는 의미)
*여기에{ suspense: true }
옵션도 함께 설정해야 한다는 의견도 있음 github 🔗
HTML과 DOM의 차이
그래서 텍스트, 스타일, 노드 구조도 바꿀 수 있는 것 (getElmentById, innerText.. 등)
*document 는 root node의 추상화된 개념인 반면 getElementById, getElementByClassName, parentNode, removeChild와 같은 메소드는 HTML DOM API에서 비롯되었다고 함
즉 우리가 웹 페이지의 컨텐츠를 동적으로 수정하고 싶으면 DOM을 수정하는 것임
*React.createElement() 메소드로 만들 수 있는 React element는 dom element의 가상버전이라고 보면된다.
React.createClass메소드를 사용하고 state가 있는게 차이점이다.
HTML스러운JSX블럭은 상태값을 가질 수 있는 render메소드를 반환하고 가장 좋은 것은 state가 바뀔때마다 컴포넌트가 다시 그려진다는 점이다.
=> 동적인 HTML디자인의 매우 좋은 도구이고 Components가 가상돔에 직접접근할 수는 없지만 ReactElement로 쉽게 컨버팅해 사용한다.