서킷 브레이커는 MSA 아키텍처를 공부하다보면 자연스럽게 만나게 되는 디자인 패턴이다.
예를 들어, 쇼핑몰에서 결제 시스템에 장애가 발생하여 응답이 지연되고, 사용자에게는 무한 로딩이 걸리는 상황이 있다고 하자.
쇼핑몰 서버에는 이런 요청들이 쌓여 결국 쇼핑몰 앱 전체 서버가 마비 되어, 구경하던 다른 사용자들도 앱이 튀기는 문제가 발생하게 된다. 이렇게 한 서비스의 장애가 서비스 전체로 장애가 전파되는데 이런 문제를 방지하는 패턴이 바로 서킷 브레이커라고 한다.

서킷 브레이커 (장애 전파)
- 서킷브레이커(Circuit Breaker)란?
- 특정 서비스 호출이 계속 실패할 때, 일시적으로 호출을 차단해서 장애가 다른 서비스로 번지는 것(연쇄 장애)를 막는 패턴
- 왜 필요한가?
- MSA는 서비스가 서로 호출하는 구조라 하나가 느려지면 전체가 연쇄적으로 느려짐
- 장애 서비스로 계속 호출이 들어가면 → 요청 대기 증가 → 스레드/커넥션 풀 고갈 → 전체 시스템 장애 발생
- 장애가 난 서비스에 불필요한 로드를 주지 않기 위해
- 장애 난 서비스도 과도한 트래픽을 받으면 더 빨리 죽음
- 서킷브레이커가 호출을 차단하면→ 장애 서비스가 안정적으로 회복할 시간 확보
- 회복력을 높여주는 구조
- 서킷브레이커는 실패가 반복되면 즉시 차단하여 전체 시스템 보호
- 동작 방식
- Closed(정상): 평소처럼 배포된 pod를 호출
- Open(차단): 실패(연결실패, 타임아웃, 오류응답 등)가 임계치(최근 N번 요청 중 실패율이 X%) 이 상이면 즉시 실패 처리(Fail Fast)로 전환 or fallback
- Half-Open(시험): 일정 시간이 지나면 소량만 테스트 호출, 성공하면 Closed로 복귀, 실패하면 Open유지
적용 예시
1) 결제 서비스(Payment)에서 외부 PG(VAN) 연동
- 문제 상황 :
→ PG사가 장애나면 응답이 수 초 이상 지연
→ 모든 결제 API가 타임아웃으로 장애 확산 - 서킷브레이커 적용 시:
→ PG 장애 감지 즉시 차단(Open)
→ 내부 fallback으로 “결제 대기 상태” 처리
→ 전체 쇼핑몰/API가 중단되지 않음
※ Fallback : 원래 호출하려던 서비스가 장애나 느린 응답 때문에 사용할 수 없을 때, 대신 제공하는 대체 응답 또는 처리 로직
2) 주문(Order) 서비스가 재고 서비스를 호출하는 경우(내부가 내부를 호출하는 경우)
- 문제 상황 :
→ 재고 서비스가 느려지면
→ 주문 생성 API 전체가 대기 상태 - 서킷브레이커 적용 시:
→ 재고 조회 실패 시 즉시 실패 처리
→ 메시지 큐에 “재고 확인 후 처리” 형태의 비동기 패턴 사용 가능 - MSA 간 연쇄 장애 방지
3) 검색(Search) 서비스에서 추천 서비스(Recommendation) 호출
- 문제 상황 :
→ 추천 서비스는 ML 모델/AI 엔진이어서 종종 느려짐 - 서킷브레이커 적용 시:
→ 추천 API 느리면 즉시 차단
→ “추천 없음” fallback으로 대체
→ 검색 기능은 완전히 정상 유지
4) 외부 지도 API, 외부 주소 API, 외부 인증 API 등 Third-party 연동
- 문제 상황 :
→ 외부 API 장애는 내부 시스템에서는 제어가 불가
→ 하지만 내부 서비스는 연쇄적으로 타임아웃 발생 - 서킷브레이커 적용 시:
→ 문제되는 외부 API로의 호출을 차단
→ 캐시 데이터 또는 기본 응답 제공
→ 전체 시스템 보호
5) 마이크로서비스 내부에서 특정 서비스만 부하가 집중되는 경우
- 문제 상황 :
→ “상품 상세 조회”는 트래픽이 몰림
→ 내부의 작은 서비스 하나가 느려져도 전체 API가 막힘 - 서킷브레이커 적용 시:
→ 장애 조기 감지
→ 차단을 통해 시스템 부하 관리
→ 정상 서비스 보호 - 사실 트래픽 제어는 서킷 브레이커가 아닌 LoadBalancer를 통해 해결하는 것이 좋음.
Resilience4j을 활용한 서킷 브레이커 실습
서킷 브레이커 패턴은 다양한 방식으로 구현할 수 있다. 물론 직접 처음부터 구현하는 방식도 있지만, 넷플릭스의 Hystrix 혹은 Resilience4j 와 같은 라이브러리를 사용할 수 있다. 또는 인프라 관점에서 Nginx, Envoy 와 같은 프록시를 사용하여 구현해볼수도 있다. Hystrix는 공식문서에 의하면, 현재 개발이 중단된 상황이다.
이번 실습에서는 Resilience4j를 활용해 서킷 브레이커가 동작 되는 모습을 직접 확인해보려고한다.
이번 실습은 사용자가 주문 요청을 보냈을 때 특정 확률에 따라 결제가 실패되도록 만들려고한다.
이는 주문 서비스에서 결제 서비스로부터의 영향으로 먹통이 되는 부분인데,
이를 Resilience4j를 활용해서 결제 서비스의 에러로, 주문 서비스가 결제로 흐름이 움직이는 것을 막아보겠다.
1. 의존성 추가
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-aop'
implementation 'io.github.resilience4j:resilience4j-spring-boot3:2.2.0'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
}
2. yaml파일 수정
resilience4j:
circuitbreaker:
instances:
myCircuitBreaker:
slidingWindowSize: 5 # 5개의 요청을 감시
failureRateThreshold: 60 # 그 중 60%(3개) 이상 실패하면 Open
waitDurationInOpenState: 10000ms # Open 후 10초간 대기
permittedNumberOfCallsInHalfOpenState: 2 # Half-open 상태에서 2번 테스트
registerHealthIndicator: true # Actuator에서 상태 확인 가능하게 설정
# Actuator 설정 (서킷 상태 모니터링용)
management:
endpoints:
web:
exposure:
include: health, circuitbreakers
endpoint:
health:
show-details: always
3. 서비스 로직 구현
@Service
public class OrderService {
private static final String CIRCUIT_BREAKER_NAME = "myCircuitBreaker";
@CircuitBreaker(name = CIRCUIT_BREAKER_NAME, fallbackMethod = "getFallbackResponse")
public String callExternalPayment() {
System.out.println(">>> 외부 결제 서비스 호출 시도...");
// 실습을 위해 70% 확률로 예외(장애) 발생
if (Math.random() < 0.7) {
throw new RuntimeException("결제 서버 타임아웃!");
}
return "결제 성공!";
}
// Fallback: 장애 시 호출될 메서드
public String getFallbackResponse(Throwable t) {
return "[알림] 현재 결제가 지연되고 있습니다. 나중에 다시 시도해주세요. (사유: " + t.getMessage() + ")";
}
}
4. 컨트롤러 로직 구현
@RestController
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@GetMapping("/order")
public String createOrder() {
return orderService.callExternalPayment();
}
}
5. 테스트 진행
5-1. 주문 요청을 반복적으로 진행한다
http://localhost:8080/order
5-2. 70% 확률로 실패가 되고 Resilience4j 설정에 따라 5회 중 3번 이상 실패라면 Clossed상태를 Open상태로 만들어
모든 요청에 즉시 에러 메세지를 띄워준다.
서비스 상태 확인은 http://localhost:8080/actuator/health 요청을 통해 확인해보면
정상일 때는 Closed 상태이며, 반복 에러 발생 시 Open 상태임을 확인할 수 있다.
추가로 10초 대기 후 Half-Open 상태가 되며 소규모 테스트를 진행해 문제가 없다면 Closed상태가 된다.




결국 MSA 환경에서 중요한 것은 어떻게 성공시키느냐보다 어떻게 우아하게 실패하느냐(Fail Fast)라는 것을 배울 수 있었다.
결제 서비스의 장애가 앱 전체의 마비로 이어지지 않도록 방어막을 쳐주는 서킷 브레이커는, 단순히 에러를 막는 도구가 아니라 시스템의 생존 전략이 아닐까 생각이 들었다.