suspense

상세 매물을 보는 상황에서 id 가 url 파라미터로 있는 바람에 오류가 생겼다. ai 가 코드 짜주면서 이거 하나 못잡더라.. 그래서 에러 코드 찾아서 넣어 주었더니 suspense를 달아주어야 한다고 한다.

Next.js Suspense


1. Suspense란?

React에서 비동기 작업(데이터 로딩 등)이 완료될 때까지

대체 UI를 보여주기 위한 기능이다.

<Suspense fallback={<div>Loading...</div>}>
  <MyComponent />
</Suspense>

MyComponent가 준비되기 전까지 fallback UI가 렌더링된다.


2. 기존 방식과의 차이

기존 방식:

if (isLoading) return <Loading />;
  • 로딩 상태를 컴포넌트 내부에서 직접 관리해야 함
  • 코드가 분산되고 UI 흐름이 끊김

Suspense 방식:

<Suspense fallback={<Skeleton />}>
  <DataComponent />
</Suspense>
  • 로딩 상태를 외부에서 제어
  • UI 흐름이 자연스럽고 구조가 깔끔함

3. Next.js(App Router)에서 중요한 이유

Next.js에서는 Suspense가 단순 옵션이 아니라 핵심 개념이다.

1) Streaming UI

서버에서 페이지를 한 번에 보내는 것이 아니라

준비된 부분부터 먼저 렌더링한다.

  • 빠른 초기 렌더링
  • 이후 데이터가 준비되면 점진적으로 채워짐

2) useSearchParams 관련 문제

다음과 같은 에러가 발생할 수 있다.

useSearchParams() should be wrapped in a suspense boundary

이유:

  • useSearchParams는 클라이언트에서만 값이 확정됨
  • 서버 렌더링 시점에는 값이 없음

해결 방법

import { Suspense } from 'react';
import SearchPage from './SearchPage';

export default function Page() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <SearchPage />
    </Suspense>
  );
}

4. 언제 사용해야 하는가

필수 사용

  • useSearchParams
  • usePathname
  • useRouter 기반 UI

권장 사용

  • API 데이터 로딩
  • 검색 결과 리스트
  • 상세 페이지 데이터 fetch

5. loading.tsx와의 차이

Next.js App Router 구조:

app/
 └─ search/
     ├─ page.tsx
     └─ loading.tsx

loading.tsx

  • 페이지 전체에 대한 fallback
  • 자동으로 적용됨

Suspense

  • 특정 컴포넌트 단위 fallback
  • 직접 제어 가능

비교

구분 loading.tsx Suspense
범위 페이지 전체 컴포넌트
제어 자동 수동
세밀함 낮음 높음

6. 실전 구조 예시

검색 + 지도 구조일 경우

<Suspense fallback={<SearchSkeleton />}>
  <SearchOverlay />
</Suspense>

<Suspense fallback={<MapSkeleton />}>
  <MapComponent />
</Suspense>

각 영역의 로딩 상태를 분리해서 관리할 수 있다.


7. 자주 하는 실수

1) Suspense 없이 useSearchParams 사용

  • 빌드 에러 발생

2) 너무 큰 범위로 감싸기

<Suspense>
  <EntirePage />
</Suspense>
  • 로딩 제어가 의미 없어짐
  • UX가 나빠짐

3) fallback 생략

  • 로딩 중 빈 화면이 보일 수 있음

8. 핵심 정리

Suspense는

비동기 UI의 로딩 상태를 자연스럽게 처리하기 위한 경계 역할을 한다.


9. 실무에서 중요한 포인트

다음 에러는 구조 문제다.

useSearchParams() should be wrapped in a suspense boundary

해결 방법:

  • 해당 컴포넌트를 Suspense로 감싸거나
  • 클라이언트 컴포넌트로 분리 후 Suspense 적용

10. 추천 구조

// page.tsx (Server Component)
import { Suspense } from 'react';
import ClientPage from './ClientPage';

export default function Page() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <ClientPage />
    </Suspense>
  );
}
// ClientPage.tsx
'use client';

import { useSearchParams } from 'next/navigation';

results matching ""

    No results matching ""