에러 바운더리(Error Boundary)와 Suspense로 로딩·에러 상태를 선언적으로 처리하는 방법, 프로덕션 수준의 에러 처리 패턴을 배웁니다.
💥 리액트에서 에러는 어디서 잡히는가
컴포넌트 렌더링 중 에러가 발생하면 리액트는 기본적으로 전체 컴포넌트 트리를 언마운트합니다. 사용자는 빈 화면만 봅니다. 이것이 프로덕션 앱에서 에러 바운더리(Error Boundary)가 필요한 이유입니다.
에러 바운더리는 자식 트리에서 발생한 렌더링 에러를 잡아서, 크래시 대신 폴백 UI를 보여줍니다. 마치 try-catch를 컴포넌트 트리 수준에서 적용하는 것입니다.
🛡️ Error Boundary 구현
에러 바운더리는 클래스 컴포넌트로만 만들 수 있습니다. 실무에서는 react-error-boundary 라이브러리를 씁니다:
npm install react-error-boundary
import { ErrorBoundary } from 'react-error-boundary'
// 에러 발생 시 보여줄 폴백 컴포넌트
function ErrorFallback({
error,
resetErrorBoundary,
}: {
error: Error
resetErrorBoundary: () => void
}) {
return (
<div className="p-6 bg-red-50 rounded-2xl border border-red-100 text-center">
<p className="text-red-600 font-bold mb-2">오류가 발생했습니다</p>
<p className="text-sm text-red-500 mb-4">{error.message}</p>
<button
onClick={resetErrorBoundary}
className="px-4 py-2 bg-red-600 text-white rounded-lg text-sm"
>
다시 시도
</button>
</div>
)
}
// 사용: 특정 섹션을 ErrorBoundary로 감싸기
function Dashboard() {
return (
<div className="space-y-4">
<ErrorBoundary FallbackComponent={ErrorFallback}>
<UserStats /> {/* 이 안에서 에러 발생해도 나머지 섹션은 정상 */}
</ErrorBoundary>
<ErrorBoundary FallbackComponent={ErrorFallback}>
<RecentPosts />
</ErrorBoundary>
</div>
)
}
에러 바운더리를 세밀하게 배치할수록 에러 영향 범위가 좁아집니다. 전체 페이지가 깨지지 않습니다.
⏳ Suspense — 로딩 상태 선언적 처리
Suspense는 자식이 아직 준비되지 않았을 때 폴백을 보여줍니다. 코드 분할(lazy loading)과 함께 가장 많이 씁니다:
import { lazy, Suspense } from 'react'
// 동적 임포트 — 해당 페이지로 이동할 때만 코드 로드
const HeavyChart = lazy(() => import('./components/HeavyChart'))
const AdminPage = lazy(() => import('./pages/AdminPage'))
function App() {
return (
<Routes>
<Route
path="/admin"
element={
<Suspense fallback={<div className="flex items-center justify-center h-96"><Spinner /></div>}>
<AdminPage />
</Suspense>
}
/>
</Routes>
)
}
처음 방문 시 청크(chunk)를 다운로드하는 동안 Spinner가 표시됩니다. 이후 방문에서는 이미 캐싱된 코드를 사용하므로 즉시 렌더링됩니다.
🔗 ErrorBoundary + Suspense 조합
실무에서는 두 가지를 함께 씁니다:
function PostSection({ postId }: { postId: number }) {
return (
<ErrorBoundary
FallbackComponent={ErrorFallback}
onError={(error, info) => {
// 에러 모니터링 서비스(Sentry 등)로 전송
console.error('에러 발생:', error, info)
}}
>
<Suspense fallback={<PostSkeleton />}>
<PostContent postId={postId} />
</Suspense>
</ErrorBoundary>
)
}
// 스켈레톤 UI — 실제 콘텐츠 형태를 흉내내는 로딩 플레이스홀더
function PostSkeleton() {
return (
<div className="animate-pulse space-y-3">
<div className="h-6 bg-gray-200 rounded w-3/4" />
<div className="h-4 bg-gray-200 rounded" />
<div className="h-4 bg-gray-200 rounded w-5/6" />
</div>
)
}
⚠️ 에러 바운더리의 한계
에러 바운더리가 잡지 못하는 에러:
- 이벤트 핸들러 안의 에러 (try-catch로 직접 처리)
- 비동기 코드 에러 (useEffect 안 fetch 실패 등)
- 서버 사이드 렌더링 에러
- 에러 바운더리 자체에서 발생한 에러
✅ 정리
- 에러 바운더리: 렌더링 에러를 잡아 폴백 UI를 보여줍니다.
react-error-boundary라이브러리 추천. - 세밀한 에러 바운더리 배치로 에러가 전체 페이지에 영향을 주지 않게 합니다.
lazy+Suspense로 페이지 코드를 분할해 초기 로딩 속도를 개선합니다.- 이벤트 핸들러와 비동기 에러는 try-catch로 직접 처리합니다.
관련 주제
- CSS Modules
- 인라인 스타일
- Tailwind CSS
- 컴포넌트 스타일 관리 비교
- 개발·프로그래밍
- 개발·프로그래밍 강의
- 리액트 입문 — 컴포넌트로 만드는 웹
- 무료강의
- 무료 온라인 강의
- NUGUNA
- 누구나
댓글
0/1000
불러오는 중...
