Developer Knowledge
개발자가 알아야 할 지식: 서킷 브레이커, 실패한 호출을 계속 반복하지 않는 법
분산 시스템에서 장애 전파를 줄이는 서킷 브레이커 패턴의 원리, 상태 전이, 재시도와의 관계, 실무 적용 체크리스트를 정리한다.
왜 개발자가 알아야 하나
현대 애플리케이션은 혼자 일하지 않는다. 결제 서비스, 인증 서버, 검색 인덱스, 외부 API, 데이터베이스, 메시지 브로커, 추천 모델, CDN까지 수많은 의존성과 연결된다. 문제는 한 의존성이 느려지거나 실패할 때, 그 실패가 호출한 서비스까지 끌고 내려갈 수 있다는 점이다. 장애는 종종 한 지점에서 시작하지만, 무제한 재시도와 대기열 누적으로 전체 시스템으로 번진다.
서킷 브레이커(Circuit Breaker)는 이 전파를 줄이기 위한 패턴이다. 이름 그대로 전기 회로의 차단기처럼, 일정 조건에서 위험한 호출을 잠시 끊는다. 실패한 서비스를 계속 두드리는 대신 빠르게 실패를 반환하고, 일정 시간이 지난 뒤 제한적으로 다시 시도한다. 핵심은 “실패를 없애는 것”이 아니라 “실패가 시스템 전체를 잡아먹지 않게 만드는 것”이다.
개발자가 이 패턴을 알아야 하는 이유는 재시도만으로는 회복력이 생기지 않기 때문이다. 재시도는 일시적 네트워크 흔들림에는 좋다. 하지만 하위 서비스가 이미 과부하 상태라면 모든 호출자가 동시에 재시도하는 순간 더 큰 부하를 만든다. 이때 서킷 브레이커는 호출량을 줄이고, 상위 서비스가 대체 응답이나 graceful degradation으로 버틸 시간을 준다.
핵심 개념
서킷 브레이커는 보통 세 가지 상태를 가진다. 첫째는 Closed 상태다. 정상 상태이며 호출은 평소처럼 하위 서비스로 전달된다. 다만 내부적으로 실패 횟수, 실패율, 타임아웃, 지연 시간 같은 지표를 기록한다. 일정 기간 동안 실패가 임계치를 넘으면 회로가 열린다.
둘째는 Open 상태다. 이 상태에서는 하위 서비스로 호출을 보내지 않는다. 요청이 들어오면 즉시 실패를 반환하거나, 캐시된 값, 기본 응답, 제한된 기능으로 우회한다. 중요한 점은 실패한 시스템을 더 이상 압박하지 않는다는 것이다. 호출자도 긴 타임아웃을 기다리지 않고 빠르게 다음 판단을 할 수 있다.
셋째는 Half-open 상태다. 일정 시간이 지나면 브레이커는 회복 가능성을 확인하기 위해 아주 적은 수의 시험 호출을 허용한다. 이 호출이 성공하면 Closed로 돌아가고, 다시 실패하면 Open으로 되돌아간다. 이 단계가 없으면 회복된 서비스를 영원히 쓰지 못하거나, 반대로 회복되지 않은 서비스에 한꺼번에 트래픽을 쏟아부을 수 있다.
실무에서 서킷 브레이커는 타임아웃, 재시도, 벌크헤드, rate limit과 함께 설계해야 한다. 타임아웃이 없으면 실패를 측정하기 어렵다. 재시도에 예산이 없으면 브레이커가 열리기 전부터 부하가 폭증한다. 벌크헤드가 없으면 한 의존성의 대기가 스레드 풀 전체를 잠식한다. 서킷 브레이커 하나만 붙였다고 시스템이 자동으로 튼튼해지지는 않는다.
작은 예시 또는 체크리스트
결제 API를 호출하는 주문 서비스를 생각해보자. 평소에는 결제 요청을 보내고 2초 안에 응답을 받는다. 그런데 결제 API가 느려져 요청의 60%가 타임아웃을 낸다. 주문 서비스가 모든 요청을 계속 기다리고 재시도하면 스레드와 커넥션이 묶이고, 결국 주문 조회나 장바구니 같은 무관한 기능까지 느려질 수 있다.
이때 서킷 브레이커를 다음처럼 설정할 수 있다.
- 최근 30초 동안 최소 50건 이상 호출이 있었고 실패율이 50%를 넘으면 Open으로 전환한다.
- Open 상태에서는 결제 API 호출을 보내지 않고 “결제 시스템 점검 중” 응답을 빠르게 반환한다.
- 60초 후 Half-open으로 전환해 소수의 시험 호출만 허용한다.
- 시험 호출이 연속으로 성공하면 Closed로 돌아가고, 실패하면 다시 Open 상태를 유지한다.
- 모든 상태 전이는 로그와 메트릭으로 남기고 알림을 보낸다.
이 설정은 정답이 아니라 출발점이다. 임계치와 시간은 서비스 특성에 맞춰 조정해야 한다. 결제처럼 사용자 영향이 큰 기능은 보수적으로 열 수 있고, 추천 API처럼 없어도 핵심 플로우가 가능한 기능은 더 빠르게 차단하고 대체 응답을 줄 수 있다.
실무에서 자주 생기는 오해
-
서킷 브레이커는 장애를 고치는 도구가 아니다. 장애 전파를 줄이고 회복 시간을 벌어주는 도구다. 하위 서비스의 근본 원인은 별도로 해결해야 한다.
-
모든 외부 호출에 같은 설정을 쓰면 안 된다. 검색, 결제, 인증, 추천, 이메일 발송은 실패 비용과 대체 가능성이 다르다. 브레이커 정책도 업무 중요도에 맞게 달라야 한다.
-
재시도와 서킷 브레이커는 경쟁 관계가 아니다. 짧고 제한된 재시도는 도움이 된다. 문제는 무제한 재시도, 동시 재시도, 긴 타임아웃이다. 재시도에는 지수 백오프, 지터, 최대 시도 횟수, 전체 시간 예산이 필요하다.
-
Open 상태에서 무엇을 반환할지 미리 정하지 않으면 사용자 경험이 나빠진다. 캐시, 기본값, 읽기 전용 모드, 부분 응답, 명확한 오류 메시지 같은 대체 전략을 함께 설계해야 한다.
-
브레이커 상태를 관찰하지 않으면 운영 도구가 아니라 숨은 블랙박스가 된다. 상태 전이, 실패율, 거부된 호출 수, Half-open 성공률을 대시보드와 알림에 넣어야 한다.
오늘 바로 적용해보기
먼저 서비스의 외부 의존성 목록을 만들자. 각 의존성에 대해 실패했을 때 사용자 영향, 평균 지연 시간, 허용 가능한 타임아웃, 재시도 필요 여부, 대체 응답 가능성을 적는다. 이 표가 없으면 서킷 브레이커 설정은 감으로 정해진다.
다음으로 타임아웃을 확인한다. 타임아웃 없는 원격 호출은 서킷 브레이커보다 먼저 고쳐야 할 위험 신호다. 호출자가 언제 포기할지 모르면 실패율과 지연 시간을 안정적으로 측정하기 어렵고, 장애 시 대기 자원이 끝없이 쌓인다.
세 번째로 작은 범위부터 적용한다. 핵심 결제 플로우 전체에 바로 넣기보다 추천, 검색 보조 데이터, 비핵심 외부 API처럼 대체 응답이 쉬운 곳에서 먼저 운영 경험을 쌓는 편이 안전하다. 브레이커가 너무 자주 열리는지, 너무 늦게 열리는지, Half-open 전환이 적절한지 실제 트래픽에서 확인해야 한다.
마지막으로 장애 훈련을 해보자. 하위 서비스 응답을 일부러 지연시키거나 실패하게 만들고, 브레이커가 언제 열리는지, 사용자 응답은 어떤지, 알림은 오는지, 회복 후 자동으로 닫히는지 확인한다. 신뢰성 패턴은 문서에 있을 때가 아니라 장애 순간에 작동할 때 의미가 있다.
더 알아보기
- Martin Fowler — CircuitBreaker
- Microsoft Learn — Circuit Breaker pattern
- AWS Prescriptive Guidance — Circuit breaker pattern
- Microsoft Learn — Implement the Circuit Breaker pattern
오늘의 takeaway
좋은 서킷 브레이커는 장애를 숨기지 않고, 실패한 의존성이 회복할 시간을 주면서 나머지 시스템을 살린다.