일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- JPA
- RTK
- Spring
- component
- css
- tanstackquery
- frontend
- hook
- typescript
- test
- ReactHooks
- springboot
- satisfiles
- Chakra
- 티스토리챌린지
- 웹애플리케이션서버
- react-hook-form
- React
- go
- javascript
- java
- Redux
- JavaSpring
- golang
- Gin
- 오블완
- backend
- designpatterns
- storybook
- Today
- Total
bkdragon's log
React-Hydration-Error 본문
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 내부에서 상태를 변경해주는 작업이 사실 마음에 들지 않는다. 하지만 에러는 해결됐다.
더 좋은 방법이 있는지 지속적으로 고민해볼생각이다.