🛠️ 처음부터 만드는 Circuit Breaker — 장애를 감지하고 자동으로 차단하기
왜 필요한가?
외부 API가 죽었는데 계속 요청을 보내면? 타임아웃이 쌓이고, 응답은 느려지고, 결국 내 서비스까지 죽습니다. Circuit Breaker는 연속 실패를 감지해 요청 자체를 차단하고, 일정 시간 후 다시 시도합니다.
3가지 상태
구현
```typescript
type State = 'CLOSED' | 'OPEN' | 'HALF_OPEN';
function createCircuitBreaker
fn: T,
{ threshold = 3, resetTimeout = 5000 } = {}
) {
let state: State = 'CLOSED';
let failures = 0;
let nextAttempt = 0;
return async (...args: Parameters
if (state === 'OPEN') {
if (Date.now() < nextAttempt) {
throw new Error(`Circuit OPEN — ${Math.ceil((nextAttempt - Date.now()) / 1000)}s 후 재시도`);
}
state = 'HALF_OPEN';
}
try {
const result = await fn(...args);
// 성공 → 초기화
failures = 0;
state = 'CLOSED';
return result;
} catch (err) {
failures++;
if (failures >= threshold) {
state = 'OPEN';
nextAttempt = Date.now() + resetTimeout;
}
throw err;
}
};
}
```
사용 예시
```typescript
const safeFetch = createCircuitBreaker(
(url: string) => fetch(url).then(r => {
if (!r.ok) throw new Error(r.statusText);
return r.json();
}),
{ threshold: 3, resetTimeout: 10000 }
);
// 3번 연속 실패 → 이후 요청은 10초간 즉시 차단
await safeFetch('/api/external');
```
핵심 포인트
| 설정 | 역할 |
|------|------|
| `threshold` | 몇 번 실패하면 차단할지 |
| `resetTimeout` | 차단 후 재시도까지 대기 시간 |
실전에서는 이전 글의 Retry와 조합하면 더 강력합니다. Retry가 개별 요청의 일시적 실패를 처리하고, Circuit Breaker가 시스템 수준의 장애를 감지합니다.
> 📎 참고: [Martin Fowler — Circuit Breaker](https://martinfowler.com/bliki/CircuitBreaker.html)