우선 , Next.js는 기본적으로 모든 페이지를 pre-rendering한다.
즉, 모든 페이지의 HTML를 미리 생성하고, 생성된 HTML은 자바스크립트 코드와 연결되어
유저와 페이지가 상호작용 할 수 있는 interactive한 페이지를 만들어낸다.
이것이 hydration이다.
🔥 문제상황
중고나라 웹사이트를 개발하며 처음으로 sentry를 들여다보며 자주 발생하는 error를 확인했다.
Hydration failed because the initial UI does not match what was rendered on the server.
오류 문구 그대로 서버에서 그려지는 HTML 페이지와 클라이언트에서 그려지는 HTML 페이지가 다르기 때문에 문제가 발생하는 것이다.
즉, Pre-Rendering 했던 트리와 Hydration을 하기 위해 클라이언트에서 자바스크립트 파일을 실행한 렌더 트리가 일치하지 않아 Hydration을 정상적으로 실행할 수 없는 것이었다.
🤔 원인 분석
위 문제를 해결하기 위해 Next.js 공식문서를 찾아봤다.
가게 정보 페이지에서는 아래와 같은 원인으로 Hydration에 실패한 것이다.
import { useState, useEffect } from 'react'
export default function App() {
const [isClient, setIsClient] = useState(false)
useEffect(() => {
setIsClient(true)
}, [])
return <h1>{isClient ? 'This is never prerendered' : 'Prerendered'}</h1>
}
문제 상황이 발생한 가게 상세 페이지 렌더링 순서는 아래와 같다.
- params으로부터 가게의 seq를 가져온다.
- 해당 seq으로부터 server-side에서 가게 상세 정보를 받아온다.
- 오류가 발생하지 않으면, 해당 정보를 hydration한다.
- client-side에서는 가게 seq를 통해 로그인한 유저의 가게인지 판단한다.
- 내 가게 여부에 따라 보여주는 component를 다르게 렌더링한다.
이를 통해, 특정 요소에 대한 불일치가 발생한다는 것을 확인할 수 있게 되었다.
💡 해결방법
내가 시도했던 해결방법은 총 2가지이다.
📌 공식문서 예제 코드 기반
import { useState, useEffect } from 'react'
export default function 가게상세페이지() {
const [isRender, setIsRender] = useState(false);
const isMyStore = 로그인유저스토어번호 === 가게상세번호;
useEffect(() => {
setIsRender(true)
}, [])
return <div>{isRender &&
(isMyStore ? < 내가게 관련 컴포넌트 /> : < 타가게 관련 컴포넌트/> )}
</div>
}
isRender라는 상태를 만들어 client-side에서 실행되는 useEffect안에서 render를 감지하고, 돔을 업데이트 해주는 방법으로 풀어내었다.
위의 방법으로 문제는 해결되었지만, hydration을 사용하는 페이지에서 모두 이를 적용하는 것이 과연 효율적인 방법일까..?에 대한 의문점이 생겼다. 팀원들과 함께 이를 공유하면서 도출한 두번째 해결 방법은 Server-side에서 내 가게 여부를 판단한 결과를 hydration하는 것이었다.
📌 SSR에서 내 가게 여부 판단하기
import { useState, useEffect } from 'react'
export default function 가게상세페이지({isMyStore}) {
// 생략...
return <div>{ isMyStore ? < 내가게 관련 컴포넌트 /> : < 타가게 관련 컴포넌트/> }</div>
}
export const getServerSideProps: GetServerSideProps = async (context) => {
const isMyStore = 로그인유저스토어번호 === 가게상세번호;
return {
props : {
isMyStore,
dehydratedState,
error,
}
}
server-side에서 내 가게 여부를 판단하여, 이에 대한 컴포넌트 렌더링을 진행할때, client에서 업데이트를 감지하지 않아도 동일한 렌더트리를 바로 그릴 수 있도록 수정하였다.
🌈 느낀점
모든 페이지를 pre-rendering하는 next.js에서 hydration을 진행할 때, 고려해야되는 부분을 놓치고 있었다.
해당 오류를 해결하면서, 정확한 기술 사용 방법에 대한 경각심을 가질 수 있게 되었고, 공식 문서를 기반으로 문제를 해결하는 것이 중요함을 알게 되었다.