NEXT DOCS > React Hydration Error
로그인을 한 경우 로그아웃 버튼을 노출하고,
로그인하지 않은 경우에는 로그아웃 버튼을 보여주지 않는 헤더를 작성하려고 했다.
의도는 이러한데 에러가 ㅠ...
"Uncaught Error: Hydration failed because the initial UI does not match what was rendered on the server."
꽤 오랜 시간을 해맸지만, 에러의 이유는 위와 같다. 서버에서의 UI와 브라우저에서의 UI가 맞지않은 것. nextjs는 기본적으로 ssr을 hydrate가 발생할 것이며 React는 렌더링된 콘텐츠가 서버와 클라이언트 간에 동일할 것으로 예상한다. 그런데 어느 조건에 의해 사전렌더링된 React트리와 브라우저에서의 React tree가 달라지게 될 경우, 개발모드에서 hydration에서 에러가 발생한다. 모든 마크업 유효성을 검사하는데 많은 비용이 들기 때문에 성능상 이유로도 중요한 부분이다.
로그인의 조건으로 cookie에 토큰이 있는지의 여부였다.
//utils auth
export const isLogin = () => {
if(typeof window !== undefined) {
return Boolean(getCookie(TOKEN_KEY));
}
return false;
}
그리고 버튼을 보여줄지 말지를 헤더 컴포넌트에 조건으로 걸어두었다.
import React, {useEffect, useState} from 'react';
import { useRouter } from 'next/router';
import {deleteCookie} from "cookies-next";
import {isLogin, TOKEN_KEY} from "../utils/auth";
function Header() {
const router = useRouter();
const handleClickLogout = () => {
deleteCookie(TOKEN_KEY)
router.push('/auth')
};
return (
<div>
<p>TodoChallenge</p>
{isLogin() && (
<button onClick={handleClickLogout}>logout</button>
)}
</div>
);
}
export default Header;
로그인한 상태에서 isLogin() 값이 서버에서는 false, browser에서는 true로 변경될 수 있기 때문에 React tree구조가 달라진다.
이 에러를 해결하기 위해서는 useEffect의 사용이 제안되었는데,
이 useEffect는 브라우저에서만 실행되고, 수화과정에서 발생되기 때문에 원하는 데이터를 만드는 목적으로 활용하게 된다.
function Header() {
const router = useRouter();
const [isUserLogin, setIsUserLogin] = useState(false)
const handleClickLogout = () => {
deleteCookie(TOKEN_KEY)
router.push('/auth')
};
useEffect(()=>{
setIsUserLogin(isLogin())
},[isLogin()])
return (
<div>
<p>TodoChallenge</p>
{isUserLogin && (
<button onClick={handleClickLogout}>logout</button>
)}
</div>
);
}
export default Header;
useEffect에서 cookie값으로 isUserLogin값을 정해주어 hydreate과정에서의 tree 불일치를 해결했다.