| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
- java
- 공통모듈
- PostgreSQL
- GitHub Packages
- dockercompose
- 컨테이너
- 도커
- 분산시스템
- GCP
- github actions
- SpringCloud
- MSA
- Java 8
- 마이크로서비스아키텍처
- 마이그레이션
- 자바
- 아키텍처
- 트러블슈팅
- 백엔드
- gradle
- Flyway
- springboot
- 마이크로서비스
- ci/cd
- 멀티모듈
- docker
- 인프라
- Database
- 백엔드면접준비
- CS
- Today
- Total
NYO_O
연쇄 장애 방지: 서킷 브레이커(Circuit Breaker) 아키텍처 본문
연쇄 장애(Cascading Failure)
이전 포스팅들을 통해 서비스 디스커버리를 활용하여 타겟 인스턴스의 IP를 찾고, 로드 밸런싱을 통해 트래픽을 골고루 분산시키는 아키텍처를 구축했습니다.
2026.05.26 - [Tech/MSA] - 분산 통신 환경의 트래픽 최적화: 로드 밸런싱(Load Balancing) 아키텍처
분산 통신 환경의 트래픽 최적화: 로드 밸런싱(Load Balancing) 아키텍처
지난 포스팅에서는 동적으로 변하는 IP 환경에서 서비스들이 서로의 위치를 찾아낼 수 있도록 돕는 '서비스 디스커버리(Service Discovery)' 아키텍처에 대해 알아보았습니다. 이제 주문 서비스는 서
ddangnyo.tistory.com
마이크로서비스 아키텍처(MSA)와 같은 분산 시스템에서는 네트워크를 통한 '동기 호출(Synchronous Call)'이 시스템의 가장 취약한 고리가 됩니다. 주문 서비스가 결제 서비스의 API를 호출했을 때, 결제 서비스 인스턴스가 완전히 다운(Down)되어 즉시 'Connection Refused' 에러를 뱉는다면 다행입니다. 클라이언트(주문 서비스)는 에러를 인지하고 즉각적인 예외 처리를 할 수 있기 때문입니다.
진짜 치명적인 문제는 결제 서비스의 데이터베이스에 데드락(Deadlock)이 발생하거나, 무거운 쿼리로 인해 스레드가 블로킹(Blocking)되어 응답을 주지 않은 채 무한정 대기 상태에 빠질 때 발생합니다. 결제 서비스의 응답을 기다리는 주문 서비스의 스레드들 역시 함께 대기 상태에 빠지게 되고, 결국 주문 서비스의 톰캣(Tomcat) 커넥션 풀마저 순식간에 고갈됩니다. 이렇게 하나의 마이크로서비스에서 발생한 병목이 자신을 호출한 상위 서비스들로 도미노처럼 번져나가 시스템 전체를 마비시키는 현상을 연쇄 장애(Cascading Failure)라고 부릅니다.
타임아웃(Timeout)만으로는 부족한 이유
이러한 무한 대기를 막기 위해 개발자들은 통상적으로 API 호출 시 타임아웃(Timeout)을 설정합니다. "3초 안에 응답이 없으면 연결을 끊어라"와 같은 설정입니다.
물론 타임아웃은 필수적인 방어 기제이지만, 대규모 트래픽이 몰리는 상황에서는 타임아웃만으로 연쇄 장애를 막기 벅찹니다. 타임아웃이 3초로 설정되어 있더라도, 초당 수천 건의 요청이 들어온다면 스레드가 3초 동안 묶여있는 것만으로도 시스템 자원은 급속도로 고갈됩니다. 더군다나 이미 장애가 발생해 회복을 시도 중인 결제 서비스에게, 주문 서비스가 타임아웃 후에도 끊임없이 재시도(Retry) 요청을 쏟아붓는다면 결제 서비스는 부하를 견디지 못하고 영원히 복구되지 못할 수 있습니다.
서킷 브레이커(Circuit Breaker)
이러한 분산 시스템의 취약점을 해결하기 위해 '서킷 브레이커(Circuit Breaker)' 아키텍처 패턴이 등장했습니다.
전기 회로에 과전류가 흐르면 차단기가 스스로 '탁' 하고 내려가 회로를 끊어버려 화재를 막듯, 소프트웨어의 서킷 브레이커 역시 타겟 서비스(결제 서비스)로 향하는 API 호출의 실패율이나 지연율을 실시간으로 모니터링합니다. 만약 실패율이 설정된 임계치(예: 최근 10초간 에러율 50% 이상)를 초과하면, 서킷 브레이커는 즉시 작동하여 결제 서비스로 향하는 모든 후속 요청을 물리적으로 차단(Fail-Fast)해 버립니다.
요청이 차단된 상태에서는 주문 서비스가 결제 API를 호출하려 할 때 네트워크 통신 자체를 시도하지 않고 즉시 에러를 반환하거나 미리 정의된 Fallback(대체) 로직을 실행합니다. 이를 통해 주문 서비스의 스레드 풀 고갈을 방지(장애 격리)하고, 부하에 시달리던 결제 서비스에게는 스스로 복구할 수 있는 시간을 벌어주게 됩니다.
서킷 브레이커의 3가지 상태(State) 전이 모델
서킷 브레이커는 네트워크 상태에 따라 3가지의 논리적인 상태(State)를 오가며 동작합니다.
- CLOSED (정상 상태): 회로가 닫혀 있어 전기가 정상적으로 흐르는 상태입니다. 모든 API 요청이 타겟 서비스로 정상적으로 라우팅됩니다. 이때 서킷 브레이커는 백그라운드에서 지속적으로 응답의 성공/실패 여부를 수집하고 통계를 냅니다.
- OPEN (장애/차단 상태): 수집된 에러율이나 타임아웃 비율이 설정한 임계치를 넘어가면 상태가 OPEN으로 전환됩니다. 회로가 끊어진 상태이므로 타겟 서비스로의 호출은 일절 발생하지 않으며, 모든 요청은 즉시 실패(Fail-Fast) 처리되거나 Fallback 로직으로 우회됩니다.
- HALF-OPEN (반개방/테스트 상태): OPEN 상태가 되고 일정 시간(Wait Duration)이 흐르면, 서킷 브레이커는 타겟 서비스가 복구되었는지 확인하기 위해 상태를 HALF-OPEN으로 변경합니다. 이때 들어오는 제한된 수의 요청(예: 10개)만 타겟 서비스로 흘려보냅니다. 이 테스트 요청들이 성공하면 서비스가 복구되었다고 판단하여 다시 CLOSED로 돌아가고, 여전히 실패한다면 다시 OPEN 상태로 되돌아가 차단을 유지합니다.
Spring Cloud와 Resilience4j
과거 자바 진영에서는 넷플릭스가 개발한 Hystrix가 서킷 브레이커의 사실상 표준이었습니다. 하지만 Hystrix가 유지보수 모드로 전환되면서, 현재는 함수형 프로그래밍에 기반하고 훨씬 가벼운 구조를 가진 Resilience4j가 Spring Cloud 생태계의 공식 서킷 브레이커 구현체로 자리 잡았습니다.
Spring Boot 프로젝트에 Resilience4j를 도입하면 @CircuitBreaker 어노테이션과 YAML 설정 몇 줄만으로 강력한 장애 격리 환경을 구축할 수 있습니다. 슬라이딩 윈도우(Sliding Window) 크기, 실패율 임계치, HALF-OPEN으로 전환되기 전 대기 시간 등을 서비스의 특성에 맞게 매우 세밀하게 튜닝할 수 있으며, Fallback 메서드를 지정하여 결제 시스템 장애 시 "일시적인 오류입니다. 잠시 후 다시 시도해 주세요"라는 정제된 응답을 클라이언트에게 안정적으로 내려줄 수 있습니다.
마무리
오늘은 분산 시스템의 고질적인 문제인 연쇄 장애를 원천적으로 차단하고, 시스템의 회복 탄력성(Resilience)을 보장하는 핵심 패턴인 서킷 브레이커에 대해 알아보았습니다. 이제 우리의 MSA 환경은 타겟 서비스의 장애를 빠르게 인지하고 격리할 수 있는 튼튼한 방어선을 갖추게 되었습니다.
지금까지 우리는 서비스 디스커버리, 로드 밸런싱, 서킷 브레이커를 거치며 개별 마이크로서비스들이 서로를 찾고 안전하게 통신하는 방법에 집중했습니다. 그런데 이렇게 마이크로서비스들이 점점 늘어나 20개, 30개가 된다면, 애플리케이션의 설정 정보(DB 비밀번호, 라우팅 룰, 타임아웃 값 등) 관리는 어떻게 해야 할까요?
설정값을 바꾸기 위해 30개의 소스 코드를 일일이 수정하고 30번을 새로 배포해야 한다면 MSA의 민첩성은 산산조각 날 것입니다. 다음 포스팅에서는 흩어진 수많은 서비스들의 설정 파일들을 한 곳에서 중앙 집중적으로 관리하고 동적으로 반영할 수 있게 해주는 인프라 기술, 'Spring Cloud Config'에 대해 자세히 다루어 보겠습니다.
'Architecture > MSA' 카테고리의 다른 글
| 분산 시스템의 단일 진입점과 공통 관심사 통제: API 게이트웨이(API Gateway) 아키텍처 (0) | 2026.05.27 |
|---|---|
| 파편화된 설정 정보의 중앙 집중화: Spring Cloud Config 아키텍처 (0) | 2026.05.27 |
| 분산 통신 환경의 트래픽 최적화: 로드 밸런싱(Load Balancing) 아키텍처 (0) | 2026.05.26 |
| MSA 환경에서 서비스 디스커버리(Service Discovery)가 필요한 이유 (0) | 2026.05.26 |
| 모놀리식의 한계와 마이크로서비스 아키텍처(MSA)의 등장 배경 (0) | 2026.05.26 |