Next.js

[Next.js] Next.js 13 - 클라이언트 컴포넌트의 onClick 이벤트 문제 해결하기

수방방 2024. 6. 25. 14:34

🔴 문제 상황

'use client'

import { useState } from 'react'

export default function Navbar() {
  const [showMenu, setShowMenu] = useState<boolean>(false)

  return (
    <button
      type="button"
      onClick={() => setShowMenu((prev) => !prev)}
      className="flex align-middle gap-3 rounded-full border border-gray-20 shadow-sm px-4 py-3 my-auto hover:shadow-lg"
    >
      <AiOutlineMenu />
      <AiOutlineUser />
    </button>
  )
}

 

`'use client'`로 선언한 클라이언트 컴포넌트에서 button의 `onClick` 이벤트가 작동하지 않을 뿐더러

 

Uncaught Error: invariant expected app router to be mounted
    at useRouter (navigation.js:131:15)
    at HotReload (hot-reloader-client.js:367:46)
    at renderWithHooks (react-dom.development.js:11021:18)
    at mountIndeterminateComponent (react-dom.development.js:16782:13)

 

브라우저 개발 도구 콘솔에서 위와 같은 오류가 발생하였습니다.

 

또한 vscode 에서 새로운 내용을 작성하여 `command + s` 로 저장하면, 웹 페이지에 자동으로 반영되는 것이 아니라 새로고침을 눌러주어야 반영되는 문제점도 발생하였습니다.

 

🟢 해결책

해결책을 찾는 도중,

https://sentry.io/answers/uncaught-error-invariant-expected-app-router-to-be-mounted/

해당 글을 발견하였습니다.

 

문제점을 살펴보면, 제대로 작동하지 않던 프로젝트의 `app/layout.tsx` 에는 아래와 같이 `<body>` 태그가 생략되어있었습니다.

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
    	<NextLayout>{children}</NextLayout>
    </html>
  )
}

Next.js 13 버전은 `app directory` 구조를 사용합니다.

따라서 앱 디렉토리 구조를 사용할 경우 `RootLayout`에 `<html>` 태그 뿐만 아니라 `<body>` 태그가 반드시 포함되어야 합니다.

기존에 `_app.tsx` 와 `_document.tsx` 를 대체해준다고 합니다.

 

-> 해결 코드

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <NextLayout>{children}</NextLayout>
      </body>
    </html>
  )
}

 

위의 코드와 같이 `<body>` 태그를 `<html>` 태그 하단에 넣어주었더니 발생하던 문제가 전부 해결되었습니다.