Suspensive vs 다른 Error Boundary 라이브러리
@suspensive/react가 react-error-boundary, @sentry/react, DIY 클래스 컴포넌트와 어떻게 다른지 — 그리고 Suspensive가 ErrorBoundary 이상으로 무엇을 제공하는지 비교합니다.
기능 비교
| @suspensive/react | 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) | ✓ | ✗ | ✗ | ✗ |
Suspensive만의 차별점
shouldCatch — 원하는 에러만 잡기
다른 어떤 솔루션과 달리, @suspensive/react는 ErrorBoundary가 어떤 에러를 잡을지 필터링할 수 있습니다. Error 생성자, 콜백 함수, 또는 boolean 값을 전달하세요:
// 이 바운더리에서는 유효성 검증 에러만 잡기
<ErrorBoundary shouldCatch={ZodError} fallback={<ValidationErrorUI />}>
<Form />
</ErrorBoundary>
// 네트워크 에러를 제외한 모든 에러 잡기
<ErrorBoundary
shouldCatch={(error) => !(error instanceof NetworkError)}
fallback={<GeneralErrorUI />}
>
<App />
</ErrorBoundary>이를 통해 부모와 자식 바운더리가 서로 다른 에러 타입을 처리하는 계층화된 에러 처리 전략을 구현할 수 있습니다. 이는 react-error-boundary에서는 불가능한 기능입니다.
ErrorBoundaryGroup — 여러 바운더리를 한 번에 리셋
여러 에러 바운더리를 관리하고 계신가요? react-error-boundary에서는 모든 바운더리에 props를 통해 resetKeys를 전달해야 합니다. Suspensive에서는 ErrorBoundaryGroup으로 감싸기만 하면 됩니다:
<ErrorBoundaryGroup>
<ErrorBoundaryGroup.Consumer>
{({ reset }) => <button onClick={reset}>Reset All</button>}
</ErrorBoundaryGroup.Consumer>
<ErrorBoundary fallback={<UserError />}>
<UserSection />
</ErrorBoundary>
<ErrorBoundary fallback={<PostsError />}>
<PostsSection />
</ErrorBoundary>
</ErrorBoundaryGroup>prop drilling이 필요 없습니다. 상태 관리도 필요 없습니다. 모든 바운더리가 함께 리셋됩니다.
안전한 Fallback 에러 처리
react-error-boundary에서는 fallback 컴포넌트가 에러를 던지면 재귀적 catch 루프가 발생합니다. @suspensive/react에서는 fallback 컴포넌트에서 던져진 에러가 부모 ErrorBoundary로 전달되어, 예측 가능하고 안전한 에러 전파를 제공합니다.
useErrorBoundaryFallbackProps — Fallback에서 Prop Drilling 없이 사용
fallback 트리 내부 어디서든 props를 전달하지 않고도 error와 reset에 접근할 수 있습니다:
const DeepFallbackChild = () => {
const { error, reset } = useErrorBoundaryFallbackProps()
return <button onClick={reset}>{error.message}</button>
}이 기능은 콜백 함수를 props로 전달할 수 없는 React Server Component 환경에서 특히 유용합니다.
ErrorBoundary 그 이상
Suspensive는 더 나은 ErrorBoundary만이 아닙니다. 프로덕션에서는 에러 처리 외에도 로딩 상태, SSR 하이드레이션, 데이터 페칭까지 관리해야 합니다. 이 문제들은 서로 연결되어 있고, Suspensive는 하나의 패키지에서 모두 해결합니다.
clientOnly가 포함된 Suspense — SSR 문제 해결
React의 <Suspense>는 Next.js 같은 SSR 환경에서 깨집니다. Suspensive의 clientOnly prop으로 한 줄로 해결:
<Suspense fallback={<Skeleton />} clientOnly>
<DataComponent />
</Suspense>dynamic(() => import(...), { ssr: false })도, useEffect 가드도 필요 없습니다.
Delay — 로딩 깜빡임 방지
빠른 응답에 스피너를 보여줄 필요 없습니다. <Delay>는 로딩 UI가 실제로 필요할 때만 보여줍니다:
<Suspense
fallback={
<Delay ms={200}>
{({ isDelayed }) => (
<Spinner
style={{ opacity: isDelayed ? 1 : 0, transition: 'opacity 200ms' }}
/>
)}
</Delay>
}
>
<Content />
</Suspense>DefaultPropsProvider — 전역 기본값
앱 전체의 <Suspense>와 <Delay>에 기본 fallback을 설정합니다:
const defaultProps = new DefaultProps({
Suspense: { fallback: <Spinner /> },
Delay: { ms: 200 },
})
<DefaultPropsProvider defaultProps={defaultProps}>
<App />
</DefaultPropsProvider>SuspenseQuery — 선언적 데이터 페칭
@suspensive/react-query와 함께 훅이나 래퍼 컴포넌트 없이 JSX로 데이터를 페칭합니다:
<ErrorBoundary fallback={<ErrorUI />}>
<Suspense fallback={<Skeleton />}>
<SuspenseQuery {...userQueryOptions()}>
{({ data: user }) => <UserProfile user={user} />}
</SuspenseQuery>
</Suspense>
</ErrorBoundary>useSuspenseQuery 훅 제약 없이, 컴포넌트 분리 없이, 모든 것이 같은 depth에서.
코드 비교
@suspensive/react
import { ErrorBoundary } from '@suspensive/react'
const App = () => (
<ErrorBoundary
fallback={({ error, reset }) => (
<div>
<p>{error.message}</p>
<button onClick={reset}>Retry</button>
</div>
)}
onError={(error) => logToService(error)}
>
<MyComponent />
</ErrorBoundary>
)react-error-boundary에서 마이그레이션
마이그레이션은 간단합니다. API가 의도적으로 유사하게 설계되었습니다:
| react-error-boundary | @suspensive/react |
|---|---|
fallbackRender / FallbackComponent / fallback | fallback (unified) |
resetErrorBoundary | reset |
withErrorBoundary() | ErrorBoundary.with() |
useErrorBoundary() | useErrorBoundary() |
npm install @suspensive/react자세한 문서는 ErrorBoundary API 레퍼런스를 참고하세요.