Files
2nd/10_Wiki/Topics/Frontend/Error Boundaries.md
T
koriweb d8a80f6272 chore(wiki): dangling 링크 canonical 정규화 (768파일/1200건)
이름만 다른(표기 변형) [[위키링크]]를 대상 문서의 canonical 제목으로 치환해
끊겼던 1,200개 링크를 연결. 제목/파일명 정규화 일치만 적용하고 별칭 매칭은
과병합 위험으로 제외(애매성 가드). 원본은 _link_reconcile_backup/ 에 백업.
도구: Datacollect/scripts/link_reconcile_apply.mjs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:24:15 +09:00

6.1 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-error-boundaries Error Boundaries 10_Wiki/Topics verified self
React Error Boundary
ErrorBoundary
getDerivedStateFromError
none A 0.9 applied
react
error-handling
frontend
resilience
2026-05-10 pending
language framework
TypeScript React

Error Boundaries

매 한 줄

"매 React subtree 의 의 render-time error 의 catch 의 의 fallback UI 의 의 swap 의 component". 의 entire app crash 의 의 prevent 의 — 의 try/catch 의 declarative React equivalent. 2026 의 의 매 React 19 의 still 의 class component 의만 의 의 Error Boundary 의 author 의 가능 (의 hook API 의 X).

매 핵심

매 catch 의 X 의 것

  • Event handler error (의 try/catch 의 사용).
  • Async code (setTimeout, promise) — Error Boundary 의 의 reach 의 X.
  • SSR (의 server-side throw 의 의 catch 의 의 X).
  • Boundary itself 의 throw.

매 catch 의 것

  • 의 child render error.
  • 의 lifecycle method error.
  • 의 constructor error.

매 hierarchy strategy

  • Top-level: 의 fallback "Something went wrong" 의 의 entire app crash 의 의 prevent.
  • Route-level: 의 broken page 의만 의 의 fail.
  • Widget-level: 의 chart, embed, third-party 의 의 isolate.

매 응용

  1. Production crash 의 reporting (Sentry, Datadog).
  2. Third-party widget 의 isolation.
  3. Per-route error UI (Next.js error.tsx).
  4. Suspense fallback combo (loading + error).

💻 패턴

기본 class boundary

import { Component, type ReactNode, type ErrorInfo } from "react";

type Props = { fallback: ReactNode; onError?: (e: Error, info: ErrorInfo) => void; children: ReactNode };
type State = { hasError: boolean };

export class ErrorBoundary extends Component<Props, State> {
  state: State = { hasError: false };

  static getDerivedStateFromError(): State {
    return { hasError: true };
  }

  componentDidCatch(error: Error, info: ErrorInfo) {
    this.props.onError?.(error, info);
  }

  render() {
    return this.state.hasError ? this.props.fallback : this.props.children;
  }
}

react-error-boundary 라이브러리

import { ErrorBoundary } from "react-error-boundary";

function Fallback({ error, resetErrorBoundary }: { error: Error; resetErrorBoundary: () => void }) {
  return (
    <div role="alert">
      <p>Something went wrong:</p>
      <pre>{error.message}</pre>
      <button onClick={resetErrorBoundary}>Retry</button>
    </div>
  );
}

<ErrorBoundary
  FallbackComponent={Fallback}
  onError={(error, info) => Sentry.captureException(error, { extra: info })}
  onReset={() => queryClient.resetQueries()}
>
  <Dashboard />
</ErrorBoundary>

Suspense + ErrorBoundary 의 combo

<ErrorBoundary FallbackComponent={Fallback}>
  <Suspense fallback={<Skeleton />}>
    <UserProfile id={userId} />
  </Suspense>
</ErrorBoundary>

의 async event 의 의 manual rethrow

function useAsyncError() {
  const [, setState] = useState();
  return useCallback((e: unknown) => {
    setState(() => { throw e; });
  }, []);
}

function MyComponent() {
  const throwAsync = useAsyncError();
  const onClick = async () => {
    try {
      await fetchData();
    } catch (e) {
      throwAsync(e);  // 매 ErrorBoundary 의 의 reach
    }
  };
  return <button onClick={onClick}>Load</button>;
}

Next.js App Router error.tsx

"use client";

export default function Error({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
  useEffect(() => { console.error(error); }, [error]);
  return (
    <div>
      <h2>Something went wrong</h2>
      <button onClick={reset}>Retry</button>
    </div>
  );
}

Per-route boundary

<Routes>
  <Route path="/" element={<Layout />}>
    <Route index element={
      <ErrorBoundary FallbackComponent={HomeFailed}><Home /></ErrorBoundary>
    } />
    <Route path="dashboard" element={
      <ErrorBoundary FallbackComponent={DashFailed}><Dashboard /></ErrorBoundary>
    } />
  </Route>
</Routes>

의 Sentry 의 integrate

import * as Sentry from "@sentry/react";

const SentryBoundary = Sentry.withErrorBoundary(MyApp, {
  fallback: ({ error, resetError }) => <Fallback error={error} resetErrorBoundary={resetError} />,
  showDialog: true,
});

Reset on prop change

<ErrorBoundary
  FallbackComponent={Fallback}
  resetKeys={[userId]}   // 의 userId 의 의 change 의 시 의 boundary reset
>
  <UserProfile id={userId} />
</ErrorBoundary>

매 결정 기준

상황 Approach
Top-level app crash 의 prevent Single root boundary + telemetry
Route 의 isolation Per-route boundary (Next.js error.tsx)
Third-party widget Tight boundary 의 의 widget 의만
Async data fetch TanStack Query 의 throwOnError + boundary
Form validation Inline error UI — 매 boundary 의 X

기본값: root + per-route + per-widget — 매 layered boundaries.

🔗 Graph

🤖 LLM 활용

언제: production crash protection, third-party widget isolation, Suspense + error 의 combo. 언제 X: form-level validation — 매 inline UI 의 더 적합.

안티패턴

  • No top-level boundary: 의 single child throw 의 의 entire app white-screen.
  • Boundary over async without rethrow: 의 catch 의 의 fail — 의 manual rethrow 의 필요.
  • Eat error silently: 매 telemetry 의 의 X — debug 의 impossible.
  • Reset 의 의 X: user 의 의 retry 의 의 way 의 X — stuck 의 fallback.
  • getDerivedStateFromError 의 의 side effect: pure function 의 의 의 violation.

🧪 검증 / 중복

  • Verified (React 19 docs, react-error-boundary, Next.js App Router error handling, Sentry React SDK).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — async rethrow + Next.js error.tsx + reset key 추가