Next.js 15에서 클라이언트 훅이 포함된 컴포넌트를 서버 컴포넌트에서 import하면 생기는 빌드에러와 해결법

김현준·2025년 6월 25일
0

넥스트JS 이모저모

목록 보기
18/23

Next.js에서 App Router를 사용할 때, 흔히 다음과 같은 컴포넌트를 작성한다:

"use client";
import { useSearchParams } from "next/navigation";

export default function Example() {
  const params = useSearchParams();
  return <div>{params.get("query")}</div>;
}

이 자체는 문제가 없다. 하지만 이 컴포넌트를 서버 컴포넌트에서 직접 import하면, Next.js 15에서는 다음과 같은 빌드 에러가 발생할 수 있다:

useSearchParams() should be wrapped in a suspense boundary at page "/"

또는

Export encountered an error on /page: /, exiting the build.

원인

클라이언트 훅이 있는 컴포넌트를 서버에서 직접 import했기 때문

Next.js 15부터는 useSearchParams, useRouter, usePathname 같은 CSR 전용 훅이 감지되면, 해당 컴포넌트가 서버에서 직접 사용될 경우 에러를 발생시킨다.

이러한 훅들은 Next.js 내부에서 "CSR Bailout 훅"이라 부르며, SSR 중에는 사용할 수 없기 때문에 에러가 발생하는 것이다.

포함된 훅 종류서버 import 가능 여부
useState, useEffect 등 일반 훅가능
useSearchParams, useRouter불가 (Suspense 필요)

해결법

방법 1: Suspense로 감싸기 (권장)

가장 공식적이고 SSR 성능까지 보장하는 방법은 <Suspense>로 해당 컴포넌트를 감싸는 것이다.

// app/page.tsx (서버 컴포넌트)
import { Suspense } from "react";
import Example from "@/components/Example";

export default function Page() {
  return (
    <main>
      <Suspense fallback={<div>로딩 중...</div>}>
        <Example />
      </Suspense>
    </main>
  );
}
  • 이 방식은 빌드 에러 없이 작동하며, 클라이언트 훅도 정상적으로 동작

방법 2: dynamic + ssr: false

Suspense 대신 next/dynamic으로 감쌀 수도 있다.

import dynamic from "next/dynamic";

const Example = dynamic(() => import("@/components/Example"), {
  ssr: false,
  loading: () => <div>로딩 중...</div>,
});
  • 이 방식도 빌드 에러는 피할 수 있지만 초기 렌더링 성능(LCP)에는 불리할 수 있다.

방법 3: 클라이언트 훅을 별도 컴포넌트로 분리

서버 컴포넌트 내에서 클라이언트 훅을 사용하는 부분만 따로 분리할 수도 있다

// ServerComp.tsx
import ClientOnlyHandler from "./ClientOnlyHandler";

export default function SearchForm() {
  return (
    <form>
      <input />
      <ClientOnlyHandler />
    </form>
  );
}

// ClientOnlyHandler.tsx
"use client";
import { useRouter } from "next/navigation";

export default function ClientOnlyHandler() {
  const router = useRouter();
  return <button onClick={() => router.push("/search")}>검색</button>;
}
  • 이 방식은 전체 컴포넌트를 SSR로 유지하면서도 안전하게 클라이언트 훅을 사용할 수 있다.

참고

profile
기록하자

0개의 댓글