---
id: wiki-2026-0508-error-handling-and-stability
title: Error Handling and Stability
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [Frontend Error Boundaries, Error Recovery]
duplicate_of: none
source_trust_level: A
confidence_score: 0.9
verification_status: applied
tags: [errors, stability, observability, sentry]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
  language: TypeScript
  framework: React/Vue
---

# Error Handling and Stability

## 매 한 줄
> **"매 frontend stability = error 의 isolation + telemetry + graceful degradation."**. 매 single uncaught error가 매 SPA 의 전체 white-screen 의 유발 — 매 ErrorBoundary, global handlers (`window.onerror`, `unhandledrejection`), 매 Sentry-class telemetry 가 필수. 매 2026의 트렌드는 React 19 ErrorBoundary + Sentry + Replay + AI-driven root-cause clustering.

## 매 핵심

### 매 layers
- **Component**: React ErrorBoundary, Vue `errorCaptured`, Svelte `<svelte:boundary>`.
- **Async**: try/catch, Promise `.catch`, `unhandledrejection` listener.
- **Global**: `window.onerror`, `window.onunhandledrejection`.
- **Network**: fetch retry, circuit breaker, AbortController.

### 매 telemetry 기둥
- Capture (stack, breadcrumb, source map).
- Group (fingerprint, dedup).
- Alert (threshold, regression).
- Replay (Sentry / LogRocket — DOM reconstruction).

### 매 응용
1. SPA route-level boundary — 매 chunk load fail 의 reload prompt.
2. Form submission retry with exponential backoff.
3. Feature flag fallback when remote config unavailable.
4. Service Worker offline shell.

## 💻 패턴

### 1. React ErrorBoundary (class)
```typescript
import { Component, type ReactNode } from 'react';

interface State { error?: Error }

export class ErrorBoundary extends Component<{ fallback: ReactNode; children: ReactNode }, State> {
  state: State = {};
  static getDerivedStateFromError(error: Error): State { return { error }; }
  componentDidCatch(error: Error, info: React.ErrorInfo) {
    Sentry.captureException(error, { extra: { componentStack: info.componentStack } });
  }
  render() {
    return this.state.error ? this.props.fallback : this.props.children;
  }
}
```

### 2. react-error-boundary (functional)
```typescript
import { ErrorBoundary } from 'react-error-boundary';

<ErrorBoundary
  fallbackRender={({ error, resetErrorBoundary }) => (
    <div role="alert">
      <p>Failed: {error.message}</p>
      <button onClick={resetErrorBoundary}>Retry</button>
    </div>
  )}
  onReset={() => location.reload()}
>
  <App />
</ErrorBoundary>
```

### 3. Global handlers
```typescript
window.addEventListener('error', (e) => {
  Sentry.captureException(e.error ?? new Error(e.message));
});
window.addEventListener('unhandledrejection', (e) => {
  Sentry.captureException(e.reason);
});
```

### 4. Chunk load failure recovery
```typescript
const lazyWithRetry = <T,>(load: () => Promise<{ default: T }>) =>
  React.lazy(async () => {
    try {
      return await load();
    } catch (err) {
      if (!sessionStorage.getItem('chunk-retried')) {
        sessionStorage.setItem('chunk-retried', '1');
        location.reload();
      }
      throw err;
    }
  });
```

### 5. Fetch retry with backoff
```typescript
async function fetchRetry(url: string, retries = 3, delay = 500): Promise<Response> {
  try {
    const res = await fetch(url);
    if (!res.ok && res.status >= 500) throw new Error(`HTTP ${res.status}`);
    return res;
  } catch (err) {
    if (retries === 0) throw err;
    await new Promise((r) => setTimeout(r, delay));
    return fetchRetry(url, retries - 1, delay * 2);
  }
}
```

### 6. AbortController on unmount
```typescript
useEffect(() => {
  const ac = new AbortController();
  fetch('/api/x', { signal: ac.signal }).catch((e) => {
    if (e.name !== 'AbortError') report(e);
  });
  return () => ac.abort();
}, []);
```

### 7. Sentry init (2026)
```typescript
import * as Sentry from '@sentry/react';

Sentry.init({
  dsn: import.meta.env.VITE_SENTRY_DSN,
  integrations: [
    Sentry.browserTracingIntegration(),
    Sentry.replayIntegration({ maskAllText: false }),
  ],
  tracesSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0,
  release: import.meta.env.VITE_RELEASE,
});
```

### 8. Vue 3 errorHandler
```typescript
import { createApp } from 'vue';
const app = createApp(App);
app.config.errorHandler = (err, instance, info) => {
  Sentry.captureException(err, { extra: { info } });
};
```

## 매 결정 기준
| 상황 | Approach |
|---|---|
| Component-local fail | ErrorBoundary at route level. |
| Async fail | try/catch + telemetry. |
| Chunk 404 (deploy mid-session) | lazyWithRetry + reload. |
| Transient 5xx | Exponential backoff retry. |
| Auth expired | Refresh token interceptor. |

**기본값**: Sentry + Replay + per-route ErrorBoundary + global handlers.

## 🔗 Graph
- 부모: [[Observability]]
- 변형: [[Circuit Breaker]]
- 응용: [[Sentry]] · [[Logrocket]]
- Adjacent: [[Source Maps]] · [[Feature Flags]]

## 🤖 LLM 활용
**언제**: ErrorBoundary scaffolding, retry helper 작성, Sentry config.
**언제 X**: 매 root-cause analysis from minified stack — 매 source map upload 필수.

## ❌ 안티패턴
- **Swallow errors**: 매 `catch {}` 빈 — 매 silent fail.
- **No source map**: 매 prod stack 의 `a.b.c` — 매 debug X.
- **Boundary at root only**: 매 한 component fail이 매 entire app crash.
- **Console.error as monitoring**: 매 user 의 console 의 도달 X — telemetry 필수.

## 🧪 검증 / 중복
- Verified (Sentry docs, react.dev error boundary).
- 신뢰도 A.

## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — Error boundary + Sentry 2026 patterns |
