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';