Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.

잭슨·2025년 5월 2일

트러블슈팅

목록 보기
7/9

Next.js에서 리액트 쿼리의 QueryClientProvider 세팅중에 다음과 같은 에러가 발생했다

에러 내용을 직역해보자면 다음과 같다.

오직 순수 객체 그리고 몇몇 빌트인 객체만 서버 컴포넌트로 부터 클라이언트 컴포넌트에 전달될 수 있습니다. 클래스 또는 null 프로토타입은 지원되지 않습니다.

이는 서버에서 클라이언트 컴포넌트로 직렬화할 수 없는 데이터를 전달했기 때문에 발생한 에러다. Next.js 공식문서에는 이와 관련해 다음과 같은 내용이 적혀있다.

Next.js 공식문서

만약 당신이 서버컴포넌트에서 데이터를 불러온다면, 클라이언트 컴포넌트에게 불러온 데이터를 props로 전달하고 싶을 수 있습니다. 서버에서 클라이언트 컴포넌트로 전달되는 props는 리액트에 의해 직렬화될 수 있어야 합니다.

만약 클라이언트 컴포넌트가 직렬화되지 않는 데이터에 의존하는 경우, 서드파티 라이브러리를 이용해 클라이언트 측에서 데이터를 불러오거나, 라우트 핸들러를 이용해 서버에서 데이터를 불러올 수 있습니다.

위 문서에서 나오는 직렬화 될 수 있는 요소는 리액트 공식문서를 살펴보면 나와있다.

리액트 공식문서

이를 바탕으로 에러의 원인을 다시 정리해보자면, 서버에서 클라이언트 컴포넌트로 직렬화할 수 없는 데이터를 props로 내려보냈기 때문에 에러가 발생했다고 볼 수 있다.

이제 코드를 보자.

const queryClient = new QueryClient();

export default function RootLayout({
    children,
}: Readonly<{
    children: React.ReactNode;
}>) {
    return (
        <html lang="en">
            <body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
                <QueryClientProvider client={queryClient}>
                    <div className="min-h-screen bg-[#f9fafb]">
                        <Navbar />
                        {children}
                    </div>
                    <ReactQueryDevtools />
                </QueryClientProvider>
            </body>
        </html>
    );
}

RootLayout는 서버 컴포넌트고, QueryClientProvider 는 내부적으로 클라이언트 컴포넌트로 구현되어있다.

TanStackQuery | QueryClientProvider 코드

QueryClientProvider에 props로 전달한 queryClientQueryClient 클래스에 의해 생성된 객체, 즉 직렬화할 수 없는 객체다.

하지만 리액트 쿼리를 사용하려면 QueryClientProviderQueryClient객체를 전달해줘야 한다. 어떻게 에러 없이 이를 구현할 수 있을까?

"use client";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const queryClient = new QueryClient();

export default function Provider({ children }: { children: React.ReactNode }) {
    return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
}

queryClient 는 서버 컴포넌트인 RootLayout에 종속될 필요가 없다. 따라서 위와 같이 Provider라는 클라이언트 컴포넌트를 만들고 여기서 queryClient를 생성한 다음 이를 QueryClientProvider에 전달해주자.

그런 다음 원래 QueryClientProvider가 있던 위치에 QueryClientProvider대신 Provider를 사용하면 에러가 해결된다.

export default function RootLayout({
    children,
}: Readonly<{
    children: React.ReactNode;
}>) {
    return (
        <html lang="en">
            <body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
                <Provider>
                    <div className="min-h-screen bg-[#f9fafb]">
                        <Navbar />
                        {children}
                    </div>
                    <ReactQueryDevtools />
                </Provider>
            </body>
        </html>
    );
}

참고자료

profile
지속적인 성장

2개의 댓글

comment-user-thumbnail
2025년 5월 12일

굉장히 복잡한 작업을 해낸 잭슨씨!
멋있네요!

1개의 답글