Next.js 애플리케이션을 개발하다 보면 다음과 같은 경고 메시지를 만날 수 있습니다:
⨯ useSearchParams() should be wrapped in a suspense boundary at page "/cases".
Read more: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout
이 경고는 무엇을 의미하며, 왜 Suspense로 감싸야 하는지 알아보겠습니다.
useSearchParams()와 Suspense 경계
문제의 원인
Next.js 13 이상에서는 App Router를 사용할 때 기본적으로 서버 컴포넌트를 사용합니다.
그러나 `useSearchParams()`와 같은 클라이언트 훅을 사용하면 해당 컴포넌트는 클라이언트 컴포넌트로 전환됩니다.
이 과정에서 두 가지 문제가 발생합니다:
1. **하이드레이션 불일치(Hydration Mismatch)**: 서버에서 렌더링된 HTML과 클라이언트에서 렌더링된 컴포넌트 간의 차이가 발생할 수 있습니다.
2. **초기 로딩 지연**: 클라이언트 사이드 렌더링으로 전환되면서 페이지 로딩 시간이 길어질 수 있습니다.
Suspense의 역할
`Suspense`는 React의 기능으로, 컴포넌트가 로딩되는 동안 대체 UI를 보여줄 수 있게 해줍니다. Next.js에서는 이를 활용하여:
1. **점진적 렌더링**: 페이지의 일부분만 클라이언트 사이드에서 렌더링하고, 나머지는 서버에서 렌더링할 수 있습니다.
2. **로딩 상태 관리**: 클라이언트 컴포넌트가 로딩되는 동안 사용자에게 로딩 UI를 보여줄 수 있습니다.
3. **스트리밍 렌더링**: 서버에서 HTML을 점진적으로 스트리밍하여 사용자 경험을 개선할 수 있습니다.
해결 방법
1. 컴포넌트를 Suspense로 감싸기
import { Suspense } from 'react';
import CasesListBox from './CasesListBox';
const CasesListContainer = () => {
return (
<Suspense fallback={<div>로딩 중...</div>}>
<CasesListBox />
</Suspense>
);
};
export default CasesListContainer;
2. 페이지 레벨에서 Suspense 적용하기
// app/cases/page.tsx
import { Suspense } from 'react';
import CasesListContainer from '@/components/cases/CasesListContainer';
export default function CasesPage() {
return (
<Suspense fallback={<div>페이지 로딩 중...</div>}>
<CasesListContainer />
</Suspense>
);
}
Suspense를 사용해야 하는 이유
1. **성능 최적화**: 전체 페이지가 아닌 필요한 부분만 클라이언트에서 렌더링하여 초기 로딩 시간을 단축합니다.
2. **사용자 경험 개선**: 로딩 상태를 명시적으로 보여주어 사용자가 페이지가 로드 중임을 인지할 수 있습니다.
3. **서버 컴포넌트 활용**: 가능한 많은 부분을 서버 컴포넌트로 유지하여 번들 크기를 줄이고 성능을 향상시킵니다.
4. **스트리밍 렌더링**: 서버에서 HTML을 점진적으로 스트리밍하여 Time to First Byte(TTFB)를 개선합니다.
실제 구현 예시
// 잘못된 방식
function BadComponent() {
const searchParams = useSearchParams(); // 클라이언트 훅
// ... 컴포넌트 로직
}
// 올바른 방식
function GoodImplementation() {
return (
<Suspense fallback={<div>로딩 중...</div>}>
<ClientComponent />
</Suspense>
);
}
// 클라이언트 컴포넌트
'use client';
function ClientComponent() {
const searchParams = useSearchParams();
// ... 컴포넌트 로직
}
결론
Next.js의 App Router에서 `useSearchParams()`와 같은 클라이언트 훅을 사용할 때는 반드시 Suspense 경계로 감싸야 합니다.
이는 하이드레이션 불일치를 방지하고, 성능을 최적화하며, 사용자 경험을 개선하는 데 도움이 됩니다.
또한 서버 컴포넌트와 클라이언트 컴포넌트를 효과적으로 분리하여 Next.js의 장점을 최대한 활용할 수 있게 해줍니다.
'프론트엔드 > Next.js' 카테고리의 다른 글
[Next.js] Metadata VS next-seo (0) | 2025.03.31 |
---|---|
[Next.js] Next.js 개발 서버 https 로 실행하기 (0) | 2025.03.27 |
[Next.js] GA(gtag.js) 적용 방법 (0) | 2025.03.13 |
[Next.js] Next15 서버 컴포넌트에서 동적 세그먼트 파라미터 가져오기 | 동적 메타데이터 설정하기 | Next15 파라미터 관련 배포 오류 (0) | 2025.03.05 |
[Next.js] Next15 API Routes 를 이용해 OgData 추출하고 링크 북마크 만들기 (0) | 2025.03.04 |