TechFeedTechFeed
Next.js런타임

Next.js Hydration 실패 — text content does not match

Hydration failed because the server rendered HTML didn't match the client
한 줄 해결: 서버에서 렌더한 HTML 과 클라이언트 첫 렌더 결과가 달라 발생하며, 렌더 중 브라우저 전용 값(시간·랜덤·localStorage)을 쓰지 않는 것이 핵심입니다.

이런 증상일 때

페이지 로드 직후 콘솔에 "Hydration failed" 또는 "Text content does not match server-rendered HTML" 경고가 뜨고, 화면이 깜빡이거나 일부가 다시 그려집니다. App Router·Pages Router 모두에서 발생합니다.

원인

  • 렌더 중 new Date()·Math.random()·Date.now() 처럼 매 렌더 달라지는 값 사용
  • typeof window 분기나 localStorage/navigator 값을 첫 렌더에 직접 반영
  • 잘못된 HTML 중첩(<p> 안에 <div>, <a> 안에 <a> 등)으로 브라우저가 DOM 을 교정
  • 확장 프로그램이나 darkmode 스크립트가 hydration 전에 DOM 을 수정

해결 방법

1
클라이언트 전용 값은 useEffect 이후로

시간·랜덤·스토리지 값은 첫 렌더에서 빼고, 마운트 후 useEffect 에서 setState 로 채웁니다. 서버/클라 첫 렌더가 같아집니다.

const [t, setT] = useState(null)
useEffect(() => setT(new Date().toLocaleString()), [])
2
suppressHydrationWarning (불가피한 경우)

시간 표시처럼 본질적으로 다를 수밖에 없는 단일 요소엔 이 속성으로 경고를 억제합니다. 남용 금지.

<time suppressHydrationWarning>{t}</time>
3
동적 import 로 SSR 끄기

브라우저 API 에 강하게 의존하는 컴포넌트는 ssr:false 로 클라이언트에서만 렌더합니다.

const Chart = dynamic(() => import('./Chart'), { ssr: false })
4
HTML 중첩 점검

<p> 안의 블록 요소, <a> 중첩 등 잘못된 마크업을 바로잡습니다. React 19 는 이 경우 더 명확한 에러를 표시합니다.

💡 예방: 렌더 함수는 순수하게 유지하고, 환경 의존 값은 항상 마운트 이후에 주입하세요.
Next.jsReacthydrationSSRuse client

관련 에러

관련 가이드

하이드레이션 미스매치, Hydration failed, 서버 클라이언트 불일치 | 콘솔에 빨간 줄이 뜨면 뭐부터 볼까?EADDRINUSE, 포트 이미 사용 중, 프로세스 종료 | 3000번이 안 죽으면 뭐부터 볼까?npm ERESOLVE, 피어 의존성 충돌, legacy-peer-deps, overrides | 설치가 멈추면 뭐부터 볼까?CORS 에러, 프리플라이트, Access-Control-Allow-Origin | 로컬은 되는데 배포하면 막히면?