본문 바로가기
개발 프레임워크/Next.js

[Next.js] Error: There was an error while hydrating this Suspense boundary. Switched to client rendering

by minhyeok.lee 2023. 3. 6.
반응형

Next.js 13에서 Error: There was an error while hydrating this Suspense boundary. Switched to client rendering

- 웹 사이트내에서 useEffect, useState를 사용하는 페이지에서 출력되는 Error

웹 페이지에 명시되는 에러
웹 페이지에 명시되는 에러

 

- 콘솔창에서 useEffect, useState를 사용하는 페이지에서 출력되는 Error

콘솔 창에 명시되는 에러
콘솔 창에 명시되는 에러

서버 HTML이 <body>에 <script>를 포함할 것으로 예상하지 않는다고 Error가 명시되고 있다.

 

Error: There was an error while hydrating this Suspense boundary. Switched to client rendering가 발생할 때

일반적으로 이 문제는 사전 렌더링과 브라우저 간에 다를 수 있는 항목에 의존하는 특정 라이브러리 또는 애플리케이션 코드를 사용하여 발생한다고 한다.

 

그에 따른 예시이다.

1. 이에 대한 예는 window구성 요소의 렌더링에서 사용하는 것이다.

수정 전 코드)

function MyComponent() {
  const color = typeof window !== 'undefined' ? 'red' : 'blue'
  return <h1 className={`title ${color}`}>Hello World!</h1>
}

 - L2의 color는 'window'에 따라 달라진다.

 - 브라우저의 첫 번째 렌더링 중에 'color' 변수가 다르게 할당된다.
 - color가 prop으로 전달되면 서버 측에서 렌더링한 것과 첫 번째 렌더링에서 렌더링한 것이 일치하지 않는다.

 

수정 후 코드)

import { useEffect, useState } from 'react'
function MyComponent() {
  const [color, setColor] = useState('blue')
  useEffect(() => setColor('red'), [])
  return <h1 className={`title ${color}`}>Hello World!</h1>
}

 - 첫 번째 렌더가 다른 것을 방지하기 위해 브라우저에서만 실행된다.

 - hydration 중에 실행되는 "useEffect"를 사용할 수 있다.

 - L3: 기본값은 'blue'이며, 사전 렌더링 시 사용되며 브라우저에서 첫 번째 렌더링(hydration)이 수행된다.

 - L4: hydration중에 'useEffect'를 호출한다.

 - "window"는 "useEffect"에서 사용할 수 있다.

 - 이 경우 브라우저에서 창을 확인할 필요가 없다.

 - 만약 당신이 window에서 무언가를 읽을 필요가 있다면 그것은 괜찮다.

 - "useEffect"에서 "setColor"를 호출하면 hydration 후 렌더가 트리거되어 "브라우저 고유" 값을 사용할 수 있게 된다.

 - 이 color는 경우 '빨간색'이 된다.


 - color는 prop으로 전달된 상태이므로 서버 측에서 렌더링한 것과 첫 번째 렌더링에서 렌더링한 것 사이에 불일치가 없다.

 - useEffect 실행 후 색상이 '빨간색'으로 설정된다.

 

2. 유효하지 않은 HTML, p 내부의 div와 같은 hydration 불일치를 유발할 수 있다. 쉽게 말해 <p> 태그 안에 <div> 태그가 있으면 안된다는 것이다.

수정 전 코드)

export const IncorrectComponent = () => {
  return (
    <p>
      <div>
        This is not correct and should never be done because the p tag has been
        abused
      </div>
      <Image src="/vercel.svg" alt="" width="30" height="30" />
    </p>
  )
}

 - <p> 태그 안에 <div> 태그가 존재하고 있다.

 

수정 후 코드)

export const CorrectComponent = () => {
  return (
    <div>
      <div>
        This is correct and should work because a div is really good for this
        task.
      </div>
      <Image src="/vercel.svg" alt="" width="30" height="30" />
    </div>
  )
}

 - <p>를 <div>로 바꾸어 해결했다.

 

3. 이번에 next.js 13을 하면서 발생한 Error

수정 전 코드)

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        {children}
      </body>
    </html>
  );
}

 - app/layout에 위치하는 layout.tsx파일에서 안에 바로 {children}이 선언되어있다.

 - 위 콘솔에서 명시된 서버 HTML이 <body>에 <script>를 포함할 것으로 예상하지 않는다고 Error가 명시되고 있다.

 - 이 때문에 <body>안에 {children}이 <script>를 포함하지 않더라도 useEffect()나 useState()같은 client-side에서 시행되는 로직이 <script>로 인식된다.

 

 

수정 후 코드)

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <div>{children}</div>
      </body>
    </html>
  );
}

 - {children}을 <div>로 감싸주어 해결할 수 있다.

반응형

댓글