Closed hyeyoonS closed 5 months ago
보편적인 하이드레이션(Next.js에서 소개하는) 에러 발생 케이스와 대처 방법
SSR을 포함한 앱을 개발하다보면, 클라이언트에서만 존재하는 데이터를 사용해야하는 순간이 온다. (예: 유저의 timezone에 맞춘 시간, local storage)
이 데이터들은 서버에서 값을 알 수 없으므로, 클라이언트에서 데이터를 처리해야 한다.
서버에서도 이 내용을 처리하려고 하면, 서버에서 랜더링된 결과와 클라이언트 내용이 달라진다. 즉, hydration mismatch가 날 수 있다.
위 문제를 해결하기 위한 대표적인 방식으로는, useEffect를 활용하는 것이다. useEffect로 컴포넌트가 완전히 랜더링 될 때 까지 실행을 지연시키는 것이다. 하지만 이 방식은 두가지 결함이 있다.
useEffect는 다시한번 랜더링 하여, 상황에 따라 유저는 컨텐츠가 버벅거리는것처럼 보일 수 있다. SSR로 페이지를 접속한 경우가 아닌, CSR로 페이지를 접속한 케이스에서도 useEffect는 불필요하게 돌 수 있다. 이 글에서는 useEffect 문제를 보완할 두가지 방법을 소개한다
다른 방식 1 : suppressHydrationWarning
function UsingSurpressWarning() {
const [state] = useState(() => (isServer() ? "server" : "client"));
return <div suppressHydrationWarning={true}>{state}</div>;
}
// 서버에서는 'server'가 내려오고 클라이언트는 'client'를 랜더링하려 하여
// hydration mismatch가 일어나야 suppressHydrationWarning 무시한다.
이 flag는 hydration mismatch에 따른 에러를 무시하게 해준다.
또, 위 코드처럼 useState를 활용해 초기값을 가져온다면 effect 실행 전, 첫 랜더링 사이클에 즉각적으로 데이터 반영을 할 수 있다. 따라서, 가장 빠르고 덜 어색하다. 이 방법은 주로 단순한 텍스트 내용의 변경에 유용하며, DOM 구조의 변경에는 적용할 수 없습니다. 예를 들어, 서버에서는
Example
B-2: useExternalSyncStore [useExternalStore]라는 리액트 훅을 사용하는 방법이다.
이 훅은 본래 외부 데이터와 내부 리액트 랜더링 라이프사이클을 연결해주는 훅인데, 이 훅의 또 다른 특징은 CSR에서 마운트되었는지 SSR+Hydration에서 마운트 되었는지 구분할 수 있다.
function UsingExteranlSync() {
const date = useSyncExternalStore(
emptySubscribe,
() => {
return `client__${Date.now()}`; // 클라이언트 스냅샷 함수.
},
() => {
return null; // 서버 스냅샷 함수.
}
);
return date != null ? date : null;
}
이제보니 개발초기에 hydration이 났었네요🤭 https://thrilling-taste-dd3.notion.site/hydration-error-d320e8b5c1f5431d96817e582ac54276?pvs=4
📎 질문
Hydration 에러 발생 시 대처방법이 있나요?
✏ 구술 답변 키워드 [MypetLog]
SSR을 포함한 앱을 개발하다보면, 클라이언트에서만 존재하는 데이터를 사용해야하는 순간이 온다. (예: 유저의 timezone에 맞춘 시간, local storage)
이 데이터들은 서버에서 값을 알 수 없으므로, 클라이언트에서 데이터를 처리해야 한다.
서버에서도 이 내용을 처리하려고 하면, 서버에서 랜더링된 결과와 클라이언트 내용이 달라진다. 즉, hydration mismatch가 날 수 있다.
위 문제를 해결하기 위한 대표적인 방식으로는, useEffect를 활용하는 것이다. useEffect로 컴포넌트가 완전히 랜더링 될 때 까지 실행을 지연시키는 것이다. 하지만 이 방식은 두가지 결함이 있다.
useEffect는 다시한번 랜더링 하여, 상황에 따라 유저는 컨텐츠가 버벅거리는것처럼 보일 수 있다. SSR로 페이지를 접속한 경우가 아닌, CSR로 페이지를 접속한 케이스에서도 useEffect는 불필요하게 돌 수 있다. 이 글에서는 useEffect 문제를 보완할 두가지 방법을 소개한다
다른 방식 1 : suppressHydrationWarning
또, 위 코드처럼 useState를 활용해 초기값을 가져온다면 effect 실행 전, 첫 랜더링 사이클에 즉각적으로 데이터 반영을 할 수 있다. 따라서, 가장 빠르고 덜 어색하다. 이 방법은 주로 단순한 텍스트 내용의 변경에 유용하며, DOM 구조의 변경에는 적용할 수 없습니다. 예를 들어, 서버에서는
Example
B-2: useExternalSyncStore [useExternalStore]라는 리액트 훅을 사용하는 방법이다.
이 훅은 본래 외부 데이터와 내부 리액트 랜더링 라이프사이클을 연결해주는 훅인데, 이 훅의 또 다른 특징은 CSR에서 마운트되었는지 SSR+Hydration에서 마운트 되었는지 구분할 수 있다.
✏ 구술 답변 키워드 [ListyWave]
✏ 서술 답변 [MypetLog]
✏ 서술 답변 [ListyWave]
Hydration 에러 발생 원인
Mismatch Text
,Mismatch attributes
등의 Mismatch에 관한 에러와Hydration failed because the initail UI does not match
에러가 가장 보편적이다.에러 해결 방법
function Header() { // 서버와 클라이언트에서 각각 다른 결과값을 가지는 isLogin()의 결과값을 그대로 분기 조건으로 사용했다. return (
); }
useState를 사용해 만들어진 isUserLogin 상태는 초기값을 false(서버에서 실행했을 때와 같은 값)를 가지며, useEffect는 브라우저에서만 실행된 후 hydration과정을 거치기 때문에 에러가 발생하지 않는다.