| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- IntelliJ
- vscode
- 배열
- Eclipse
- 단위테스트
- 문자열
- ArrayList
- java테스트
- html
- 인텔리제이
- 자바문법
- list
- 자바
- HashMap
- SpringBoot
- CSS
- junit5
- string
- Visual Studio Code
- Array
- javascript
- junit
- 정규식
- input
- js
- 스프링부트
- 테스트자동화
- 자바스크립트
- math
- Java
- Today
- Total
어제 오늘 내일
[Spring Cloud] 외부 서버가 죽어도 내 서버는 산다? 서킷 브레이커(Resilience4j) 패턴 본문
[Spring Cloud] 외부 서버가 죽어도 내 서버는 산다? 서킷 브레이커(Resilience4j) 패턴
hi.anna 2026. 3. 27. 00:51
집에서 드라이기랑 에어컨을 동시에 켰다가 전기가 팍! 나간 적 있으신가요?
이때 두꺼비집(누전 차단기)이 내려가서 전기를 끊어주지 않았다면, 과열로 인해 집 전체에 불이 났을지도 모릅니다.
소프트웨어에서도 똑같습니다. 외부 API가 에러를 뿜어내고 있는데 계속 호출하면 내 서버까지 불이 납니다. 이때 "잠깐 연결 끊어!"라고 해주는 기술이 바로 서킷 브레이커입니다.
1. 서킷 브레이커의 3가지 상태
이 기술의 핵심은 상태(State)를 관리하는 것입니다.
- CLOSED (닫힘 - 정상): 전기가 통하는 상태. API 호출이 정상적으로 잘 됩니다.
- OPEN (열림 - 차단): 에러가 너무 많이 나서 회로를 끊어버린 상태. API 호출을 아예 안 하고 바로 에러를 뱉습니다. (Fail Fast)
- HALF-OPEN (반열림 - 간보기): 차단된 지 일정 시간이 지나서, "이제 좀 괜찮아졌나?" 하고 한 번 찔러보는 상태. 성공하면 다시 CLOSED로, 실패하면 다시 OPEN으로 갑니다.
2. 설정하기 (Resilience4j)
예전에는 Netflix Hystrix를 썼지만, 지금은 업데이트가 중단되었습니다. 요즘은 Resilience4j가 표준입니다.
① 의존성 추가 ()
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j'
implementation 'org.springframework.boot:spring-boot-starter-aop' // AOP 필수!
}
② 설정 파일 ()
"10번 호출했는데 50% 이상 실패하면 60초 동안 차단해라!"라는 규칙을 정합니다.
resilience4j:
circuitbreaker:
instances:
my-weather-api: # 서킷 브레이커 이름
registerHealthIndicator: true
slidingWindowType: COUNT_BASED # 횟수 기준 (또는 TIME_BASED)
slidingWindowSize: 10 # 최근 10번 요청을 기록
failureRateThreshold: 50 # 실패율 50% 넘으면 OPEN (차단)
waitDurationInOpenState: 60s # 60초 동안은 호출하지 마!
permittedNumberOfCallsInHalfOpenState: 3 # 반열림 상태에서 3번 테스트해봐
3. 실전 코드: 와 Fallback
이제 서비스 코드에 어노테이션만 붙이면 됩니다.
그리고 가장 중요한 것! 차단되었을 때 대신 실행할 '대안(Fallback)'을 마련해야 합니다.
@Service
@RequiredArgsConstructor
public class WeatherService {
private final WeatherClient weatherClient;
// 1. name: 설정 파일에 적은 인스턴스 이름
// 2. fallbackMethod: 에러 나면 실행할 메서드 이름
@CircuitBreaker(name = "my-weather-api", fallbackMethod = "getFallbackWeather")
public String getSeoulWeather() {
return weatherClient.getForecast("seoul"); // 외부 API 호출
}
// ★ Fallback 메서드 (대안)
// 파라미터와 리턴 타입이 원본 메서드와 똑같아야 함!
// 마지막 파라미터로 Throwable을 받을 수 있음
private String getFallbackWeather(Throwable t) {
log.error("날씨 API가 죽었습니다: {}", t.getMessage());
return "맑음 (기본값)"; // 혹은 DB에 캐시된 데이터 리턴
}
}
결과:
- 날씨 API가 죽어서 에러를 뱉습니다.
- 실패율이 50%를 넘으면 서킷 브레이커가 OPEN 됩니다.
- 이후 요청부터는 날씨 API를 호출조차 하지 않고, 바로
getFallbackWeather가 실행되어 "맑음 (기본값)"을 리턴합니다. - 사용자는 "서버 오류" 화면 대신, 조금 부정확하더라도 날씨 정보를 볼 수 있습니다. (서비스 생존!)
4. 모니터링 (Actuator 연동)
서킷 브레이커가 열렸는지 닫혔는지 어떻게 알까요?
스프링 부트 Actuator를 통해 실시간으로 상태를 볼 수 있습니다.
- 접속 주소:
http://localhost:8080/actuator/health
{
"status": "UP",
"components": {
"circuitBreakers": {
"status": "UP",
"details": {
"my-weather-api": {
"status": "CLOSED", // 현재 상태 (정상)
"failureRate": "-1.0%",
"bufferedCalls": 0
}
}
}
}
}
이걸 프로메테우스+그라파나에 연결하면, "서킷 브레이커 열림 알림"을 슬랙으로 받을 수도 있겠죠?
마치며
오늘의 결론입니다.
- 외부 API는 언제든 죽을 수 있다고 가정해야 한다.
- 서킷 브레이커는 외부 장애가 내 서버로 번지는 것을 막아주는 방화벽이다.
- Fallback(대안)을 잘 준비하면, 장애 상황에서도 최소한의 서비스는 유지할 수 있다.
이제 여러분의 서버는 주변 서버들이 다 죽어나가도 혼자 꿋꿋하게 살아남는 생존왕이 되었습니다.
다음 포스팅에서는 "사용자가 실수로 결제 버튼을 두 번 눌렀어요!" 중복 요청을 막아주는 멱등성(Idempotency) 설계와 Redis 활용법에 대해 알아보겠습니다. (돈과 관련된 중요한 문제입니다!)
도움이 되셨다면 좋아요와 댓글 부탁드립니다! 😊
