Error

React-Hydration-Error

bkdragon 2023. 3. 17. 17:01

Next.js로 개발하던 중 다음과 같은 에러를 만났다.

`Text Content does not match server-rendered HTML'

이 에러를 파헤쳐보려 한다.

 

공식문서에서는 이 에러를 React Hydration Error라고 정의하고 발생한 이유에 대해 다음과 같이 설명한다.

While rendering your application, there was a difference between the React tree that was pre-rendered (SSR/SSG) and the React tree that rendered during the first render in the Browser.
The first render is called Hydration which is a feature of React
.
어플리케이션을 렌더링하는 동안 미리 렌더링된 React 트리(SSR/SSG)와 브라우저에서 첫 번째 렌더링 중에 렌더링된 React 트리 사이에 차이가 있었습니다. 첫 번째 렌더는 리액트의 특징인 하이드레이션이라고 불립니다. 이로 인해 응답 트리가 DOM과 동기화되지 않고 예기치 않은 콘텐츠/속성이 나타날 수 있습니다.

Hydration은 무엇일까?  Next.js는 서버 사이드에서 html을 미리 렌더링한 정적 페이지를 브라우저로 보내고 React는 번들링된 javascript 코드를 브라우저로 보낸다. 브라우저는 이 둘을 매칭하는 작업을 진행하는데 그것을 Hydrate라고 한다.

 

나는 이 에러를 개인 프로젝트에서 만나게 되었는데, 같은 페이지를 두 탭에 띄워놓고 한쪽에서 댓글을 달고 다른 페이지에서 새로고침을 했을 때 발생했다. 콘솔 창에서 다음과 같은 메시지도 확인 할 수 있었다.

Warning: Text content did not match. Server: "ddddd" Client: "ddddddd"

지금까지 얻은 정보를 토대로 분석 아닌 분석을 하자면 pre-rendering에선 ''ddddd" 는데 브라우저의 첫번째 rendering에는 "ddddddd"가 나와서 이 두개가 match가 안된다는 것이다.

 

공식문서에선 useEffect를 활용한 해결방법을 제시한다. useEffect는 client side에서의 실행을 보장한다. 첫번째 렌더링이 다른것을 방지하기 위해 브라우저에서만 실행되게 제한하는 것이다.

 

그래서 나도 다음과 같은 코드를 추가했다. 

const [commentList, setCommentList] = useState<{
        list : {
            xPosition : string;
            yPosition : string;
            id : number;
            writerId : number;
            contents : string;
        }[];
        page : number;
        totalPage : number;
        hasMore : boolean;
    }>({
        list : [],
        page : 0,
        totalPage : 0,
        hasMore : false,
    });

...

useEffect(() => {
    if(commentsData) {
        setCommentList(commentsData)
    }
}, [commentsData])

 

commnetsData는  React Query의 useQuery를 이용해 비동기 통신으로 받아온 데이터이다. useQuery의 장점이 받아온 데이터를 캐시해주고 바로 사용할 수 있는 점인데, 새로운 상태를 만들고 useEffect 내부에서 상태를 변경해주는 작업이 사실 마음에 들지 않는다. 하지만 에러는 해결됐다.

 

더 좋은 방법이 있는지 지속적으로 고민해볼생각이다.