React Suspense, complete.
ErrorBoundary, Suspense, Delay 등 — 성공한 케이스에만 집중하세요.
<ErrorBoundary />
fallback, resetKeys, onError, shouldCatch를 갖춘 선언적 에러 처리. 특정 에러만 선택적으로 캐치.
<ErrorBoundary
shouldCatch={NetworkError}
fallback={({ error, reset }) => <ErrorUI error={error} onRetry={reset} />}
>
<App />
</ErrorBoundary>자세히 보기<Suspense clientOnly />
SSR 안전한 Suspense 바운더리. Next.js에서 dynamic()이나 useEffect 가드 없이 하이드레이션 불일치 방지.
자세히 보기Suspense + ive — React Suspense를 둘러싼 모든 것, 하나의 패키지로.
코드로 확인하세요
로딩, 에러, 복구가 어떻게 동작하는지.
대표적인 라이브러리인 TanStack Query로 Suspense 없이 코드를 작성한다면 이렇게 작성합니다.
import { useQuery } from '@tanstack/react-query'const Page = () => {const userQuery = useQuery(userQueryOptions())const postsQuery = useQuery({...postsQueryOptions(),select: (posts) => posts.filter(({ isPublic }) => isPublic),})const promotionsQuery = useQuery(promotionsQueryOptions())if (userQuery.isLoading ||postsQuery.isLoading ||promotionsQuery.isLoading) {return 'loading...'}if (userQuery.isError || postsQuery.isError || promotionsQuery.isError) {return 'error'}return (<Fragment><UserProfile {...userQuery.data} />{postsQuery.data.map((post) => (<PostListItem key={post.id} {...post} />))}{promotionsQuery.data.map((promotion) => (<Promotion key={promotion.id} {...promotion} />))}</Fragment>)}
Suspense를 사용하면 타입적으로 간결해지지만 컴포넌트의 깊이는 깊어집니다.
import { ErrorBoundary, Suspense } from '@suspensive/react'import { useSuspenseQuery } from '@tanstack/react-query'const Page = () => (<ErrorBoundary fallback="error"><Suspense fallback="loading..."><UserInfo userId={userId} /><PostList userId={userId} /><PromotionList userId={userId} /></Suspense></ErrorBoundary>)const UserInfo = ({ userId }) => {const { data: user } = useSuspenseQuery(userQueryOptions())return <UserProfile {...user} />}const PostList = ({ userId }) => {const { data: posts } = useSuspenseQuery({...postsQueryOptions(),select: (posts) => posts.filter(({ isPublic }) => isPublic),})return posts.map((post) => <PostListItem key={post.id} {...post} />)}const PromotionList = ({ userId }) => {const { data: promotions } = useSuspenseQuery(promotionsQueryOptions())return promotions.map((promotion) => (<PromotionListItem key={promotion.id} {...promotion} />))}
Suspensive를 사용하면 모든 것이 같은 뎁스에 유지됩니다.
import { ErrorBoundary, Suspense } from '@suspensive/react'import { SuspenseQuery } from '@suspensive/react-query'const Page = () => (<ErrorBoundary fallback="error"><Suspense fallback="loading..."><SuspenseQuery {...userQueryOptions()}>{({ data: user }) => <UserProfile {...user} />}</SuspenseQuery><SuspenseQuery{...postsQueryOptions()}select={(posts) => posts.filter(({ isPublic }) => isPublic)}>{({ data: posts }) =>posts.map((post) => <PostListItem key={post.id} {...post} />)}</SuspenseQuery><SuspenseQuery{...promotionsQueryOptions()}select={(promotions) => promotions.filter(({ isPublic }) => isPublic)}>{({ data: promotions }) =>promotions.map((promotion) => (<PromotionListItem key={promotion.id} {...promotion} />))}</SuspenseQuery></Suspense></ErrorBoundary>)
이미 react-error-boundary를 사용 중이신가요?
좋은 선택입니다.
react-error-boundary는 React에서 선언적 에러 처리를 실용적으로 만든 훌륭한 라이브러리입니다. Suspensive의 ErrorBoundary는 프로덕션에서 필요했던 것들을 몇 가지 더했습니다 — shouldCatch로 특정 에러만 캐치하고, ErrorBoundaryGroup 으로 여러 바운더리를 한번에 리셋하고, fallback 에러를 안전하게 부모로 전파합니다. 로딩 상태, SSR 하이드레이션, 로딩 깜빡임 UX, 데이터 페칭도 함께 해결해야 했기에 — 이것들도 하나의 패키지에 담았습니다.
| Suspensive | react-error-boundary | @sentry/react | DIY (Class Component) | ||
|---|---|---|---|---|---|
| Error Boundary | shouldCatch | ✓ | ✗ | ✗ | ✗ |
| ErrorBoundaryGroup | ✓ | ✗ | ✗ | ✗ | |
| useErrorBoundaryFallbackProps | ✓ | ✗ | ✗ | ✗ | |
| Safe fallback error propagation | ✓ To parent | ✗ Recursive | ✗ Recursive | ✗ | |
| TypeScript error type inference | ✓ Via shouldCatch | ✗ | ✗ | ✗ | |
| useErrorBoundary hook | ✓ | ✓ | ✗ | ✗ | |
| Fallback UI with error & reset | ✓ | ✓ | ✓ | ✗ | |
| resetKeys | ✓ | ✓ | ✗ | ✗ | |
| onReset callback | ✓ | ✓ | ✓ | ✗ | |
| onError callback | ✓ | ✓ | ✓ | ✗ | |
| HOC support | ✓ ErrorBoundary.with | ✓ withErrorBoundary | ✓ withErrorBoundary | ✗ | |
| Declarative API | ✓ | ✓ | ✓ | ✗ | |
| Async Rendering | SSR-safe Suspense (clientOnly) | ✓ | ✗ | ✗ | ✗ |
| Flash-of-loading prevention (Delay) | ✓ | ✗ | ✗ | ✗ | |
| Global default fallbacks (DefaultPropsProvider) | ✓ | ✗ | ✗ | ✗ | |
| Declarative data fetching (SuspenseQuery) | ✓ | ✗ | ✗ | ✗ | |
| Client-only rendering (ClientOnly) | ✓ | ✗ | ✗ | ✗ |
Playground
코드를 수정하고 Suspensive를 직접 체험해보세요.
에러는 의도된 것입니다 — Retry를 눌러 ErrorBoundary의 복구를 체험해보세요.
설정 없이 바로 시작, 점진적 도입 가능, Next.js 등 모든 React 앱에서 동작.