FE
[Next.js] Next.js에서 Hydration 에러가 발생한 이유와 해결 과정
문제 상황
Next.js에서 모바일 햄버거 메뉴를 만들던 중 아래와 같은 에러가 발생했다.
Error: Hydration failed because the server rendered HTML didn't match the client.처음에는 단순한 UI 문제겠지 싶었는데, 실제로는 서버와 브라우저가 서로 다르게 화면을 그리고 있어서 생긴 문제였다.
Hydration이란?
Next.js는 페이지를 서버에서 먼저 렌더링하고, 브라우저는 그 HTML을 받아서 React 상태와 이벤트를 연결한다.
이 과정을 Hydration(하이드레이션)이라고 부른다.
즉, "서버가 먼저 만든 화면에 브라우저가 생명을 불어넣는 작업"이라고 보면 된다.
이때 브라우저가 생각한 화면 구조와, 서버가 그려준 화면 구조가 다르면, React는 "이거 뭐야, 다르잖아?" 하고 에러를 낸다.
→ 그게 바로 Hydration failed 에러다.
원인 분석
const [open, setOpen] = useState(false);
return (
<div>
<button onClick={() => setOpen(!open)}>메뉴</button>
<div className={open ? '열림' : '닫힘'}>
메뉴 내용
</div>
</div>
);원인 코드를 보면, 햄버거 메뉴의 열림 상태를 useState(false)로 관리하고 있었다. 서버는 open = false 상태로 메뉴가 닫힌 HTML을 먼저 만들어 클라이언트에 보낸다. 문제는 이 다음이다.
브라우저가 HTML을 받아 화면에 보여준 뒤, React가 클라이언트에서 컴포넌트를 다시 실행하면서 Hydration이 시작된다. 이때도 useState(false)라서 open = false인 건 똑같다. 그런데 HTML 구조가 조건부 className이나 aria 속성 등으로 달라질 수 있다.
예를 들어, open 값에 따라 <div className="hidden">이 되기도 하고, <div className="block">이 되기도 한다. React가 Hydration 시점에 className을 다르게 계산하거나, 스타일 라이브러리가 다르게 처리하면서 실제 DOM 구조가 달라질 수 있다.
결과적으로 브라우저가 받은 서버 HTML과, React가 만든 클라이언트 초기 렌더링 결과가 달라지게 되고, 이때 React는 이를 감지하고 에러를 발생시킨다.
이게 핵심이다:
"버튼 클릭으로 상태가 바뀌는 건 문제가 안 된다. 하지만 처음부터 구조가 다르면 Hydration 시점에서 바로 충돌이 난다."
즉, Hydration 에러는 초기 상태의 구조 차이 때문에 발생하는 것이고, 사용자가 클릭하기도 전에 이미 에러가 발생하는 구조라는 점이 중요하다. 다시 말해, 해당 코드 구조에서는 open 값에 따라 div의 className, aria 속성 등이 달라지기 때문에, 서버와 클라이언트가 동일한 결과를 보장할 수 없다.
해결 전략
Hydration 에러를 막으려면, 서버와 클라이언트가 처음 렌더링할 때 동일한 결과를 만들어야 한다. 문제가 되는 UI(메뉴, 모달 등)는
서버에서는 아예 렌더하지 않고, 브라우저에서 마운트된 후에만 렌더하도록 처리하면 된다.
1. 개선 코드
const [open, setOpen] = useState(false);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
return (
<>
<button onClick={() => setOpen(!open)}>메뉴</button>
{mounted && (
<div className={open ? '열림' : '닫힘'}>
메뉴 내용
</div>
)}
</>
);이렇게 하면:
서버는 메뉴를 렌더하지 않음
브라우저는 마운트된 뒤에만 렌더
→ 초기 구조가 일치하므로 Hydration 에러가 사라짐
2. 앞으로의 대응법
상태에 따라 구조가 달라지는 UI는 SSR에서 주의
mounted 분기 패턴은 간단하고 효과적
습관처럼 적용하면 실무에서 안정성 높일 수 있음
정리
Hydration은 서버에서 만든 HTML에 React가 기능을 연결하는 과정이다.
이때 초기 상태나 구조가 다르면 에러가 발생한다.
useState처럼 상태에 따라 렌더링이 달라지는 UI는 브라우저에서만 렌더링해야 안전하다.
가장 실용적인 대응법은 useEffect로 마운트 여부를 체크해서 분기하는 방식이다.