Error: Text content does not match server-rendered HTML
Next.js의 getServerSideProps를 활용하여 특정 페이지에서 SSR(server-side rendering) 사용했는데 React 트리 간에 차이가 존재하여, React 트리가 DOM과 동기화되지 않아 발생한 이슈였습니다.😭
조금 더 자세히 확인해보면 Next.js는 Pre-rendering
이 Default입니다.
Pre-rendering의 HTML 파일을 생성하는 방법은 2가지입니다. 하나는 정적 생성
, 서버측 렌더링
입니다.
이런 이유 때문에 SSR 사용 시 서버 측 렌더링 할 때 생성한 HTML로 1차 Paint가 진행됩니다.
그리고 난 후 서버로부터 받은 데이터를 가지고 HTML에 인터랙션 기능 및 이벤트 핸들러 등을 연결합니다.
이런 과정을 hydration
이라고 합니다.
즉 hydration
과정에서 React는 렌더링 된 컨텐츠가 서버와 클라이언트 간에 동일할 것으로 예상하는데 이 부분에서 차이가 있을 때 내용을 수정합니다.
그런데 개발 모드에서는 이런 불일치가 발생할 때 react는 불일치에 대한 알림을 보내주는데 그 알림을 제가 받았습니다ㅎㅎ
위의 사진을 보시면 초기에 서버 측 렌더링으로 생성한 HTML을 FCP(First Contentful Paint)하고 있습니다. 그 후 서버로부터 Response를 받으면 HTML에 인터랙션 기능 등을 연결해주는 작업이 진행됩니다. 이때 발생한 시간 차이로 데이터가 불일치하게 되고 이때 React Hydration Error
가 발생한 것입니다.
서버측 렌더링 된 HTML | HTML에 인터랙션 추가 |
---|---|
![]() | ![]() |
상황에 따라 해결 방법은 여러 가지입니다. window 객체를 활용해서 window 객체가 준비됐다면(=HTML에 인터랙션 기능 등 연결이 완료됐다) 그 때 데이터를 표시하는 방법이 있는데 이 방법을 사용하려면 window 객체의 상태를 체크해주는 코드가 필요합니다. 상태를 체크해주는 코드보다 React의 suspense를 활용하여 해결하는것이 코드량이 적을것이라고 생각해서 Suspense를 사용해서 해결했습니다.
[수정전 코드]
interface IProps {
header?: ReactNode;
}
const UserList: React.FC<IProps> = ({ header }) => {
return (
<List
header={header}
footer={
<div>{new Date().toLocaleDateString()}</div>
}
bordered
dataSource={mockDataUsers}
renderItem={(item: IUser, index: number) => (
<List.Item>
<>
<Typography.Text mark>[{index + 1}]</Typography.Text>
{item.nickname}
</>
</List.Item>
)}
/>
);
};
export default UserList;
[수정 후 코드]
interface IProps {
header?: ReactNode;
}
const UserList: React.FC<IProps> = ({ header }) => {
return (
<List
header={header}
footer={
<Suspense fallback={null}>{new Date().toLocaleDateString()}</Suspense>
}
bordered
dataSource={mockDataUsers}
renderItem={(item: IUser, index: number) => (
<List.Item>
<>
<Typography.Text mark>[{index + 1}]</Typography.Text>
{item.nickname}
</>
</List.Item>
)}
/>
);
};
export default UserList;
사이드 프로젝트하면서 이슈들을 마주칠 때마다 성장할 수 있는 계기(?)인거 같아 기분 좋다. 사이드 프로젝트에서만큼은 이슈들이 많이 생겼으면 좋겠다 ㅎㅎㅎ
https://nextjs.org/docs/messages/failed-loading-swc
https://nextjs.org/docs/basic-features/pages#pre-rendering
https://nextjs.org/docs/messages/react-hydration-error