react · high priority
React Error Boundaries
렌더 중 예외를 격리하는 유일한 공식 메커니즘
학습 개요
선행 학습
- react-fiber
- react-concurrent
탄생 배경
멘탈 모델
쉬운 설명
복잡한 개념을 실생활 비유로 설명합니다.
“건물의 방화벽.”
건물 한 층에서 불이 나도 전체 빌딩이 전소되지 않도록 방화벽이 층을 나눈다. Error Boundary 는 React 트리의 방화벽이다 — 한 서브트리가 "불탔어도" (렌더 예외), 부모의 경계가 그 범위에서 불길을 끊고 대신 대피 안내도(fallback UI)를 보여준다. 방화벽 자체에 불이 붙으면 무용지물이듯, Boundary 자신의 렌더에서 throw 하면 효과가 없다.
핵심 개념
Error Boundary 의 공식 정의는 "자식 트리의 렌더 중 예외를 잡는 class 컴포넌트" 다. 여전히 **function 컴포넌트로 구현할 수 없다** — hooks 는 렌더 이후의 예외를 후크할 API 를 제공하지 않기 때문이다. 그래서 대부분의 프로젝트는 react-error-boundary 라이브러리의 ErrorBoundary 래퍼를 재사용한다.
1import { Component, type ErrorInfo, type ReactNode } from 'react';23interface Props { fallback: ReactNode; children: ReactNode }4interface State { hasError: boolean }56export class ErrorBoundary extends Component<Props, State> {7 state: State = { hasError: false };89 // ① state 를 유도한다. 순수 함수 — side effect 금지.10 static getDerivedStateFromError(_error: unknown): State {11 return { hasError: true };12 }1314 // ② side effect — 로깅/트래킹. error 와 info.componentStack 수집.15 componentDidCatch(error: Error, info: ErrorInfo) {16 console.error('[ErrorBoundary]', error, info.componentStack);17 // Sentry.captureException(error, { extra: info });18 }1920 render() {21 return this.state.hasError ? this.props.fallback : this.props.children;22 }23}
getDerivedStateFromError vs componentDidCatch
- **순수 함수** — side effect 불가
- 반환값이 새 state 로 머지됨
- SSR 에서도 호출됨
- "어떤 UI 로 전환할지" 결정
- **side effect 가능** — 로깅 · 분석 전송
- 두 번째 인자
info.componentStack제공 - SSR 에서는 호출되지 않음 (클라이언트만)
- "무슨 일이 일어났는지" 기록
function 컴포넌트로 못 만드는 이유
React 팀은 "hooks 로 Error Boundary 를 표현하려면 렌더 함수 외부에서 state 를 갱신하는 경로가 필요한데, 이는 hooks 모델과 호환되지 않는다" 고 밝혔다. 대안으로 **react-error-boundary** 패키지가 class 구현을 한 번만 제공하고, 사용은 hook (useErrorBoundary) 으로 하는 하이브리드 API 를 표준처럼 쓰고 있다.
트레이드오프
이상적인 사용 사례
실무 적용
어떤 상황에서 사용하는가
토스/당근 수준의 앱에서는 한 페이지에 여러 독립 위젯(추천 카드 · 최근 주문 · 공지 배너)이 있다. 한 위젯의 API 가 500 을 뱉어도 다른 위젯은 정상 노출돼야 한다. 동시에 전체 앱이 unmount 되는 최악의 케이스는 방지해야 한다.
어떻게 적용하는가
3-계층 Boundary 를 둔다. (1) Root Boundary: 치명적 fallback. (2) Route Boundary: `app/**/error.tsx` (Next.js) 또는 RouterProvider 에 errorElement. (3) Widget Boundary: 각 카드 래퍼에 `react-error-boundary` 의 `<ErrorBoundary>`. 각 위젯의 `useSuspenseQuery` 실패는 해당 카드의 boundary 만 트리거 → 다른 카드는 유지. `onCaughtError` 루트 핸들러로 Sentry 전송. 재시도는 `resetErrorBoundary` + `queryClient.resetQueries(key)` 를 onReset 에 연결.
흔한 실수와 안티패턴
- 이벤트 핸들러에서 throw 했는데 fallback 이 안 나와서 "Boundary 가 안 먹는다" 고 오해 — showBoundary 가 필요.
- Boundary 자체의 fallback JSX 에서 useQuery 같은 suspense 훅을 또 호출 — 이중 suspense/error 흐름으로 깜빡임 버그.
- onReset 에서 쿼리 캐시만 초기화하고 state 를 그대로 둬서 "리셋했는데 같은 에러" 가 반복.
- SSR 중 getDerivedStateFromError 가 호출된다는 걸 잊고 fallback 에 window 참조 코드 삽입 → hydration mismatch.
- 모든 Boundary 에서 Sentry.captureException 을 중복 호출 — 루트 onCaughtError 로 통합하는 게 표준.
흔한 오해
function 컴포넌트 + hook 으로도 Error Boundary 를 만들 수 있다.
교정현재 공식 API 는 class 전용이다. `react-error-boundary` 라이브러리도 내부적으로는 class 를 사용한다.
왜 중요Boundary 는 "자식의 렌더 예외를 부모의 상태 전환으로 바꾸는" 메커니즘이라 hook 실행 모델로는 표현하기 어렵다.
onClick 내부의 예외도 Boundary 가 잡아준다.
교정이벤트 핸들러는 렌더 페이즈 밖이므로 자동 catch 되지 않는다. `try/catch` 로 잡아 `showBoundary(err)` 를 호출해야 fallback 이 뜬다.
왜 중요React 는 이벤트 디스패치를 위임받은 후 핸들러를 직접 호출할 뿐, 그 내부 throw 는 React 트리 밖이다.
Suspense fallback 이 보이면 Boundary 는 필요 없다.
교정Suspense 는 Promise 를 위한 fallback, Boundary 는 Error 를 위한 fallback 이다. 네트워크가 정말 실패하면 Promise 가 reject → Error 가 throw 되므로 두 boundary 가 함께 있어야 한다.
왜 중요둘을 같은 "선언적 상태 전환" 으로 보면 자연스럽다 — 로딩/에러/성공 중 어느 상태를 선택하느냐의 문제.
면접 질문
답변 방향 힌트
"렌더/commit 페이즈가 아니면 못 잡는다" 는 원칙에서 역산. 이벤트 · async · SSR · 자기 자신의 4가지를 꼽고 각 우회책을 제시.
반드시 언급할 키워드
- 이벤트 핸들러 — try/catch + showBoundary
- 비동기 (setTimeout · Promise) — await 감싸서 try/catch
- SSR (renderToString) — 서버 try/catch + 서버 로거
- Boundary 자신의 render — 상위 Boundary 필요
예상 꼬리 질문
- `componentDidCatch` 는 SSR 에서 왜 호출되지 않나요?
- `showBoundary` 를 내부적으로 어떻게 구현하나요?
- App Router 의 `error.tsx` 는 class 컴포넌트인가요?
자기 점검
Error Boundary 가 catch 하는 페이즈는 무엇이고, 못 잡는 경우 4가지를 열거해보세요.
기대 키워드
자주 하는 오해
"모든 JS 에러를 잡는다" 고 생각하는 것.
getDerivedStateFromError 와 componentDidCatch 의 역할 차이를 한 줄로 설명해보세요.
기대 키워드
자주 하는 오해
"둘 다 같은 걸 한다" 고 답하는 것.
Suspense 와 ErrorBoundary 의 배치 순서와 그 이유를 말해보세요.
기대 키워드
React 19 의 onUncaughtError 가 호출되는 상황을 구체적 예로 설명해보세요.
기대 키워드
학습 자료
- React Legacy API — Component.componentDidCatch / getDerivedStateFromErrorError Boundary 의 공식 스펙. catch 대상 · 미포함 케이스 4종 · 사용 패턴까지 정리된 1차 자료.Docreact.dev · 2024
- createRoot — onCaughtError · onUncaughtError · onRecoverableErrorReact 19 의 루트 에러 핸들러 3종 스펙. 각 콜백이 언제 호출되는지 명시.Docreact.dev · 2024
- bvaughn/react-error-boundary사실상 표준 Boundary 라이브러리. `ErrorBoundary` 컴포넌트 + `useErrorBoundary` hook 으로 showBoundary/resetBoundary 를 제공.CodeGitHub · 2024
- Next.js App Router — error.js conventions`app/**/error.tsx` 가 어떻게 세그먼트 단위 Error Boundary 로 컴파일되는지 설명.BlogNext.js Docs · 2024
- TanStack Query — Suspense · useSuspenseQuery 와 ErrorBoundary 패턴Suspense + ErrorBoundary 중첩으로 로딩/에러 UI 를 선언적으로 구성하는 실전 예제.BlogTanStack Query Docs · 2024