@senspond

>

개발>프론트 엔드

NextJS useSearchParams() should be wrapped in a suspense boundary at page 오류해결

등록일시 : 2025-02-28 (금) 01:59
업데이트 : 2025-02-28 (금) 02:12
오늘 조회수 : 1
총 조회수 : 7

    NextJS useSearchParams() should be wrapped in a suspense boundary at page 오류해결




    문제발단

    Next.js 15 버전을 사용하여 개발 중, 이전에 rgbitcode 블로그에서는 발생하지 않았던 오류를 확인했습니다. 정확히 어떤 버전부터 이 변화가 일어났는지 확인할 필요가 있지만, rgbitcode 블로그에서는 useSearchParams를 사용하지 않고, URL을 /blog/content/?id=2와 같은 쿼리 파라미터 형식이 아니라 /blog/content/2와 같은 경로 형식으로 처리하고 있었습니다.


    app router 방식의 디렉토리 기반 페이지 라우팅




    그래서 rgbitcode 블로그는 https://rgbitcode.com/blog/senspond/1 와 같은 경로로 라우팅됩니다.


    쿼리 파라미터 기반 페이지 라우팅


    하지만 이번에 Next.js로 새 프로젝트를 진행하면서 useSearchParams를 사용했는데, 개발 모드 dev에서는 오류가 발생하지 않다가, 빌드를 수행하니 오류가 발생하는 문제를 경험하게 되었습니다.


    오류 메시지 : useSearchParams() should be wrapped in a suspense boundary at page [페이지 url]


    아래는 /project/?id=13 와 같은 형식으로 접근할 수 있는 페이지입니다.

    "use client"
    export default function UploadAtProject() {
      const searchParams = useSearchParams(); // `useSearchParams`를 사용하여 query 파라미터 받아옴
      const projectId = searchParams.get("id"); // URL에서 'projectId' 가져오기
      const [projects, setProjects] = useState<IProject[]>([]);
     const [loading, setLoading] = useState<boolean>(false); // 로딩 상태
      useEffect(() => {
        const loadProjects = async () => {
          setLoading(true); // 데이터 로딩 시작
          try {
            const response = await fetch(`/api/projects/${projectId}`); // `projectId`를 포함하여 요청
            if (!response.ok) throw new Error('프로젝트를 불러오는 데 실패했습니다.');
            const data = await response.json();
            setProject(data.data); // 프로젝트 데이터 저장
          } catch (error) {
            console.error('프로젝트 로드 실패:', error);
          } finally {
            setLoading(false); // 데이터 로딩 완료
          }
        };
        loadProjects();
      }, [projectId]);
    
      if (loading) {
        return <div>로딩 중...</div>;
      }
    
      if (!project) {
        return <div>프로젝트를 찾을 수 없습니다.</div>;
      }	
    
      return (
        <div>
          <h1>{project.name}</h1>
          <p>{project.description}</p>
        </div>
      );
    }


    Next.js의 App Router 방식에서는 기본적으로 모든 페이지가 서버 사이드 렌더링(SSR)으로 작동합니다.

    빌드 과정에서는 서버에서 먼저 페이지를 렌더링하고, 그 후 클라이언트에서 추가적인 데이터 로딩이나 인터랙션을 처리하게 됩니다.


    하지만 useSearchParams()는 클라이언트 사이드에서만 작동하는 훅이기 때문에, 서버 사이드 렌더링 시에는 정상적으로 동작하지 않습니다. 이로 인해 페이지가 처음 렌더링될 때, 클라이언트에서 쿼리 파라미터를 읽지 못해 빈 페이지가 표시됩니다. 이후 클라이언트 사이드에서 데이터를 fetch를 통해 불러오고 화면에 렌더링되지만, 그 전까지는 빈 페이지가 잠깐 보이게 됩니다.


    이 문제를 해결하려면 useSearchParams()를 클라이언트 컴포넌트로 분리하거나, 서버 사이드에서 사용할 수 있는 다른 방법을 적용해야 합니다.


    해결방법

    page.tsx에서 useSearchParams()을 사용하는 클라이언트 컴포넌트를 Suspense 로 감싸주기


    
    import {Suspense} from "react";
    import UploadAtProject from "@/app/upload/upload";
    
    
    export default function UploadPage() {
        return (
            <Suspense>
                <UploadAtProject/>
            </Suspense>
        )
    }


    이 해결방법은 NextJS 공식 페이지에 안내되어 있습니다.

    https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout


    Suspense는 React 18 버전부터 더 본격적으로 지원되며, 서버 사이드 렌더링(SSR)과 클라이언트 사이드 렌더링(CSR) 모두에서 데이터를 비동기적으로 로드할 수 있게 도와주는 중요한 기능입니다.


    Suspense를 사용하면, 클라이언트 사이드에서 데이터가 로드되기 전에 빈 페이지가 보이는 문제를 해결할 수 있습니다. 구체적으로, Suspense는 데이터를 로딩하는 동안 로딩 스피너나 다른 대체 UI를 보여주고, 데이터가 준비되면 실제 UI를 렌더링하는 방식으로 작동합니다.


    React 18에서의 Suspense 사용 예시

    import React, { Suspense } from 'react';
    
    // 클라이언트 컴포넌트에서 데이터를 fetch하는 컴포넌트
    const ContentComponent = React.lazy(() => import('./ContentComponent'));
    
    function BlogPage() {
      return (
        <Suspense fallback={<div>Loading...</div>}>
          <ContentComponent />
        </Suspense>
      );
    }


    React 18에서 SuspenseuseTransition을 활용하면, 비동기 데이터 로딩을 더 잘 처리할 수 있습니다. 이 방법을 사용하여 useSearchParams()와 같은 클라이언트 전용 훅을 사용할 때, 빌드 시 빈 페이지가 보이지 않도록 해결할 수 있습니다.


    Reference

    https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout

    https://ko.react.dev/reference/react/Suspense



    senspond

    안녕하세요. Red, Green, Blue 가 만나 새로운 세상을 만들어 나가겠다는 이상을 가진 개발자의 개인공간입니다.

    댓글 ( 0 )

    카테고리내 관련 게시글

    현재글에서 작성자가 발행한 같은 카테고리내 이전, 다음 글들을 보여줍니다

    @senspond

    >

    개발>프론트 엔드

    • [현재글] NextJS useSearchParams() should be wrapped in a suspense boundary at page 오류해결

      NextJS useSearchParams() should be wrapped in a suspense boundary at page 오류해결
        2025-02-28 (금) 01:59
      1. 타입스크립트의 클로저, 클래스, 데코레이터로 싱글톤 패턴 구현하기

        타입스크립트의 클로저, 클래스, 데코레이터로 싱글톤 패턴 구현하기 정리를 해봤습니다.
          2024-05-22 (수) 07:59
        1. NextJS App router 방식으로 다국어 설정 간단하게 하기 next-intl

          NextJS App router 방식으로 다국어 설정 하기 next-intl 를 통해 간단하게 적용
            2024-09-18 (수) 03:38