하이드레이션 불일치요?

레이나·2025년 3월 19일

프로젝트

목록 보기
14/15

[2025.03.19 수요일]

[Next.js 개인과제 - 트러블슈팅]

🚨 문제 발생

  • 개인과제 도전기능중 다크모드 구현이 있었다. 찾아보다가 next-themes 라이브러리를 사용하여 구현하였다. 오~ 생각보다 쉽게 모드구현이 되서 라이브러리가 좋구만! 하고있었는데, 새로고침을 하고 나니까 화면 가능 에러가 표출!

Hydration failed 라고나오면서, 콘솔창에도 오류가 발생하였다.

이게 대체 무슨 오류이지?
에러 관련 하여 next.js 공식문서에서 제공하는 부분이 있어서 먼저 살펴보았다.


원인 추론

  • 서버에서 렌더링한 HTML과 클라이언트에서 렌더링한 결과가 달라서 발생하는 "Hydration mismatch" 라고 한다.
  • 콘솔창의 경고에서도 html과 RootLayout을 지목하고 있는데, 아마도 테마모드를 바꾸면서 html의 class 값이 next.js에서 미리 렌더링한 부분과 맞지 않아서 생기는 오류인것 같았다.

해결 과정

테마의 기본값 주기 ❌

  • 테마의 기본값을 명시적으로 주어서 렌더링 시점의 테마와 이후의 테마를 같게 만들어 주면 되지 않을까? 생각을 하고 <ThemeProvider attribute="class" defaultTheme="light"> 코드를 수정해 주었으나, 동일한 오류가 발생하였다.

suppressHydrationWarning 추가 ❌

  • 이처럼 하이드레이션 불일치 오류를 표기하지 않도록 공식문서에서 제시한 대로suppressHydrationWarning={true} 속성을 주었지만, 여전히 오류 발생.

icon 대신 HTML로만 작성 ❌

  • 구글링해보니 잘못된 html, 또는 icon등으로 인해서 발생한 경우도 있어서 icon을 지우고 html만 사용했을 경우에도 동일한 오류 발생.

mounted 값 설정해주기 ❌

  • 문제를 해결하다보니 처음 마운트되었을 때는 모드변경이 전혀 되지 않다가, 다른 페이지로 이동한 후에 모드를 변경하면 변경이 된점.
  • 새로고침을 한 이후에만 해당 오류가 발생한점.

위와같은 오류가 발생하는 환경을 확인할 수 있게 되었다.
그래서 마운트값을 적용해보기로 하였다.

해당 테마를 설정중인 컴포넌트에 아래와 같은 코드를 추가

 const [mounted, setMounted] = useState(false);

  useEffect(() => {
    setMounted(true);
  }, []);

이 경우에도 여전히 결과가 같았다.
강력새로고침, 캐시를 비우고 빌드.. 모두 오류가 표출되었다.

라이브러리를 버려야 하나? 좀더 방법을 찾아봐야하나?

결과

해결방안

  • 새로고침시에만 오류 표출이되고 있어서, mounted 코드를 버릴수가 없었다. 그 상태에서 경고 무시를 하는 suppressHydrationWarning 속성도 추가해보았다. 혹시!? 하면서..여전히 동일한 오류가 발생하였다.
  • 그리고 또 다른 방법이 있을까? 하고 구글링도 하고 다시 공식문서도 보았다.
    공식문서 아래쪽에 suppressHydrationWarning 는 one level deep에서만 작동한다는 문구가 있어서 해당 속성의 위치를 바꿔 보았다.
  • 테마프로바이더 컴포넌트 상단에 해당속성을 주었더니!! 됐다! 오류가 생성되지 않고 잘 작동되었다!!
  • 그래서 html에 주었던 suppressHydrationWarning 속성을 지웠는데, 왜 안되죠?
  • 결과적으로 내 코드는 html과 body에 각가 suppressHydrationWarning 속성을 주어서 해결이 되었다. 이게 맞는건가? 좀 찜찜한 해결이 되었다.
<html lang="ko" suppressHydrationWarning>
      <body className={`${pretendard.variable} font-pretendard`} suppressHydrationWarning>
          <ThemeProvider attribute="class" defaultTheme="light">
          
              {children}

          </ThemeProvider>
      </body>
    </html>


느낀점

  • 리액트만 할때는 서버렌더링을 접하지 않아서 이런오류를 마주칠 일이 없었다. 이론으로는 알고있었지만 차이를 체감하기는 어려웠는데 이번 오류로 인해서 SSR과 CSR의 차이를 체감한 좋은 기회가 되었다.
    앞으로도 next.js를 사용하면서 종종 만나게 될 오류일 것 같다.
    컬러모드 다크모드 같은 테마들이 여기저기 많이 사용되는데 이렇게 클라이언트 상태를 확인하는 경우는 서버렌더링과의 상호작용(?) 등도 고려해야 하겠구나도 느꼈다.
    이게 서버와 클라이언트의 렌더링 차이구나~
profile
one setp

0개의 댓글