백엔드 면접 용어 사전
시스템 설계circuit breaker

서킷 브레이커

실패가 임계치를 넘으면 호출을 차단해 장애 전파를 막는 패턴. Closed·Open·Half-Open 상태를 갖는다.

장애가 난 대상에 대한 호출을 일정 기간 아예 차단해 연쇄 장애를 막는 패턴. 전기 회로의 두꺼비집에서 이름을 따 왔다.

무엇을 막는가 — 연쇄 장애

결제 서비스가 느려짐 (응답 30초)
   ↓
주문 서비스의 스레드들이 결제 응답을 기다리며 쌓임
   ↓
주문 서비스의 스레드 풀 고갈 → 주문 서비스도 응답 불가
   ↓
주문을 호출하던 API 게이트웨이도 막힘
   ↓
서비스 전체 마비   ← 원인은 결제 하나였는데

한 곳의 장애가 전체로 번지는 것을 막는 것이 목적이다.

세 가지 상태

다이어그램 로딩 중…
상태동작
CLOSED (정상)요청을 그대로 통과시키며 실패율을 집계
OPEN (차단)호출 자체를 하지 않고 즉시 실패하거나 폴백 반환
HALF_OPEN (반개방)소수의 시험 호출만 보내 회복됐는지 확인

왜 즉시 실패가 나은가 — 직관과 반대되는 부분

"차단하면 그 기능을 못 쓰는 것 아닌가?" 맞다. 하지만

차단 안 함 : 30초 기다렸다가 실패 + 그동안 스레드 점유 + 전체로 번짐

  • 차단함 — : 1ms 만에 실패 + 스레드 즉시 반납 + 다른 기능은 정상

어차피 실패할 호출이라면 빨리 실패하는 것이 자원을 지킨다. 이것을 fail-fast라 한다.

회복을 돕는다 — 죽어 가는 서비스에 요청을 계속 퍼부으면 회복할 여유조차 없다. 차단은 상대에게 숨 쉴 틈을 준다.

설정 값

CircuitBreakerConfig.custom()
    .slidingWindowSize(100)              // 최근 100건 기준
    .failureRateThreshold(50)            // 실패율 50% 넘으면 OPEN
    .waitDurationInOpenState(ofSeconds(30))  // 30초 후 HALF_OPEN
    .permittedNumberOfCallsInHalfOpenState(10)
    .slowCallRateThreshold(50)           // 느린 호출도 실패로 간주 ★
    .slowCallDurationThreshold(ofSeconds(2))
    .build();

★ 느린 호출을 실패로 세는 것이 중요하다. 에러를 안 내고 느리기만 한 서비스가 실제로는 더 위험하다 — 스레드를 붙잡아 두기 때문이다.

임계값 설정 감각

  • 너무 민감하면 일시적 스파이크에 불필요하게 차단
  • 너무 둔감하면 이미 번진 뒤에 차단 → 의미 없음
  • 보통 실패율 50%, 최소 호출 수 20건 이상에서 시작해 조정

폴백 설계 — 차단했을 때 무엇을 줄 것인가

  • 추천 목록 실패 — → 인기 상품 목록 (품질 저하, 서비스는 유지)
  • 캐시 조회 실패 — → DB 직접 조회
  • 결제 실패 — → 폴백 없음. 명확히 실패시켜야 한다 ★

★ 모든 것에 폴백이 있어야 하는 것은 아니다. 결제를 "성공한 척"하면 훨씬 큰 사고다. 부가 기능은 degrade, 핵심 기능은 fail이 원칙이다.

함께 쓰는 패턴

패턴역할
타임아웃서킷의 전제. 타임아웃이 없으면 실패를 감지조차 못 한다
재시도 + 백오프일시적 실패 흡수. 단, 서킷이 OPEN이면 재시도하지 않는다
벌크헤드자원 자체를 격리해 전파를 물리적으로 차단

재시도와 서킷을 함께 쓸 때 순서가 중요하다 — 재시도가 서킷 안쪽에 있으면 실패 1건이 3건으로 부풀려져 서킷이 과도하게 열린다. 보통 서킷 → 재시도 순으로 감싼다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 시스템 설계·분산 — CAP·복제·샤딩·Saga·서킷브레이커