useSuspenseQueries에는 반드시 Suspense Boundary 필요
docs/reference/shop-app-suspense-boundary.md
title: Shop App — useSuspenseQueries와 Suspense Boundary created: 2026-03-28
useSuspenseQueries에는 반드시 Suspense Boundary 필요
문제
Shop 앱에서 auth/me API가 ERR_INSUFFICIENT_RESOURCES로 무한 호출되며, 모든 클라이언트 interaction(hover, navigate, modal 등)이 동작하지 않는 치명적 버그 발생.
원인
PageRenderer에서 useResourcesData() → useSuspenseQueries()를 사용하는데, 이를 감싸는 <Suspense> boundary가 없었음.
// ❌ BAD — Suspense boundary 없이 useSuspenseQueries 사용 <HydrationBoundary state={dehydrate(queryClient)}> <PageRenderer ... /> {/* 내부에서 useSuspenseQueries 호출 */} </HydrationBoundary>
왜 무한 루프가 되는가
useSuspenseQueries는 데이터가 없으면 Promise를 throw함 (React Suspense 프로토콜)- React는 가장 가까운
<Suspense>boundary를 찾아서 fallback을 렌더링해야 함 <Suspense>가 없으면 React는 전체 컴포넌트 트리를 다시 렌더하려 시도- 다시 렌더 → 다시 throw → 다시 렌더 → 무한 루프
- 수백~수천 번 반복 → 브라우저 리소스 고갈 →
ERR_INSUFFICIENT_RESOURCES
왜 SSR은 정상이었는가
SSR에서는 prefetchShopResources()가 QueryClient에 데이터를 미리 넣어둠. HTML은 정상 렌더링됨. 문제는 클라이언트 hydration 시점에서만 발생.
해결
// ✅ GOOD — Suspense boundary로 감싸기 <HydrationBoundary state={dehydrate(queryClient)}> <Suspense> <PageRenderer ... /> </Suspense> </HydrationBoundary>
규칙
useSuspenseQuery 또는 useSuspenseQueries를 사용하는 컴포넌트는 반드시 상위에 <Suspense> boundary가 있어야 한다.
이건 React 공식 규칙이지만, SSR + HydrationBoundary 조합에서 놓치기 쉽다. SSR prefetch가 데이터를 미리 채워주면 개발 중에는 문제가 안 보이다가, 캐시 미스/만료/첫 로드 시에만 터진다.
관련 파일
apps/shop/app/[[...slug]]/page.tsx— Suspense 추가apps/shop/src/features/resource-loader/hooks.ts—useResourcesData(useSuspenseQueries 사용)apps/shop/src/shared/providers/PageRenderer.tsx—useResourcesData호출