
nextjs에서 전역 상태 변수인 recoil을 사용중인데 새로고침만 하면 Hydration 오류 발생
=> 원인을 찾아보니 모든 컴포넌트에 같이 랜더링 되는 header 부분에서 useEffect없이 token을 사용하고 있어서 오류가 발생하고 있었다. => 다시 말해 나는 token이 뭔지 모르는데 token값을 사용해서 오류!
// header.js
{!token ?
<Link href="/login"><a className="mr-5 hover:text-gray-900">로그인</a></Link>
:<><button className="mr-5 hover:text-gray-900" onClick={logout}>로그아웃</button></>
}
따라서 useEffect를 추가해서 해결함
const [ssrCompleted, setSsrCompleted] = useState(false);
useEffect(() => setSsrCompleted(true), []);
{ssrCompleted && !token ?
<Link href="/login"><a className="mr-5 hover:text-gray-900">로그인</a></Link>
:<><button className="mr-5 hover:text-gray-900" onClick={logout}>로그아웃</button></>
}
Hydrate는 Server Side 단에서 렌더링 된 정적 페이지와 번들링된 JS파일을 클라이언트에게 보낸 뒤, 클라이언트 단에서 HTML 코드와 React인 JS코드를 서로 매칭 시키는 과정을 말한다.
Next.js의 웹 페이지 구성 원리
Next.js는 클라이언트에게 웹 페이지를 보내기 전에 Server Side 단에서 미리 웹 페이지를 Pre-Rendering 한다.
그리고 Pre-Redering으로 인해 생성된 HTML document를 클라이언트에게 전송한다.
그런데 이 시점에서 중요한 것은 아래 내용이다.
현재 클라이언트가 받은 웹 페이지는 단순히 웹 화면만 보여주는 HTML일 뿐이고, 자바스크립트 요소들이 하나도 없는 상태이다. 이는 웹 화면을 보여주고 있지만, 특정 JS 모듈 뿐 아니라 단순 클릭과 같은 이벤트 리스너들이 각 웹 페이지의 DOM 요소에 하나도 적용되어 있지 않은 상태임을 말한다.
그러면 이렇게 페이지만 보여주고 동작조차 하지 못하는 마치 빈 껍데기 같은 웹 페이지가 나중에는 어떻게 정상적으로 동작하게 되는 것일까.
Next.js Server에서는 Pre-Rendering된 웹 페이지를 클라이언트에게 보내고 나서, 바로 리액트가 번들링 된 자바스크립트 코드들을 클라이언트에게 전송한다.
그리고 이 자바스크립트 코드들이 이전에 보내진 HTML DOM 요소 위에서 한번 더 렌더링을 하면서, 각자 자기 자리를 찾아가며 매칭이 된다.
이 과정을 Hydrate라고 부른다.
이것은 마치 자바스크립트 코드들이 DOM 요소 위에 물을 채우 듯 필요로 하던 요소들을 채운다 하여 Hydrate(수화)라는 용어를 쓴다고 한다.
Server에서 한번 렌더링하고 Client에서도 한번 더 렌더링 하면 비효율적인 렌더링 방식 아닌가요?
어쩌면 두번 렌더링 하는 것은 비효율적으로 보일 수 있다.
그러나 서버 단에서 빠르게 Pre-Rendering하고 유저에게 빠른 웹 페이지로 응답할 수 있다는 것에 더욱 큰 이점을 가져갈 수 있다.
심지어 이 Pre-Rendering 한 Document는 모든 자바스크립트 요소들이 빠진 굉장히 가벼운 상태이므로 클라이언트에게 빠른 로딩이 가능하다.
이는 같은 화면에 대해 두 번 렌더링이 일어난다는 단점을 보완하고도 남는다.
더 나아가서 클라이언트 단에서 자바스크립트가 렌더링을 할 때, 단지 각 DOM 요소에 자바스크립트 속성을 매칭 시키기 위한 목적이므로 실제 웹 페이지를 다시 그리는 과정까지는 하지 않는다.(Paint 함수 호출 X)