Feign 장애 대응 구조 및 예외 흐름 문제 해결 - Genie-Uss/genieus GitHub Wiki
🧩 문제 상황 (Problem)
MSA 환경에서 주문 서비스는 재고, 쿠폰, 프로모션 등 외부 서비스와의 FeignClient 기반 통신을 통해 주문 처리를 진행합니다. 이 과정에서 다음과 같은 문제에 직면했습니다:
- 외부 서비스 장애(503, 타임아웃 등) 시, 빠른 격리와 복구가 필요했으나 기본 구조로는 장애 전파 및 시스템 전체 오류 발생
- 이를 해결하기 위해 Resilience4j의 서킷브레이커 + 리트라이 조합을 도입했으나,
- fallbackMethod가 호출된 이후에도 예외가 재시도 대상으로 분류되어 리트라이가 반복
FeignClientException(4xx)
과 같은 비즈니스 예외에도 무의미한 재시도 발생
- Fallback 내부에서 예외를 명확하게 던졌음에도, Retry가 이를 감지하지 못하고 예외 흐름이 꼬이는 현상 발생
🔎 원인 분석
@Retry
는 기본적으로 메서드 내 모든 예외를 재시도 대상으로 처리함fallbackMethod
에서 던진 예외가retry.ignoreExceptions
에 등록되어 있지 않으면 → 다시Retry
가 동작- 결과적으로 서킷브레이커와 리트라이가 fallback을 공유하면서 예외 흐름이 꼬임
FeignClientException
(4xx)은 비즈니스 실패로 간주되어야 하는데, 무조건 재시도 되는 구조였음
🎯 선택한 해결 방안 (Decision)
✅ Resilience4j 설정 개선
- 서킷브레이커와 리트라이의 예외 처리를 분리 관리
retryExceptions
:RetryableException
,FeignServerException
,CustomServiceUnavailableException
ignoreExceptions
:FeignClientException
,CustomNotFoundException
- Fallback은 오직 서킷브레이커에서만 적용,
@Retry
는 fallback 없이 실패 시 그대로 서킷브레이커로 예외 전달
✅ 예외 흐름 명확화
FallbackMethod
에서는RuntimeException
대신 **의미 있는 커스텀 예외(ProductServiceFailureException
)**를 명확하게 던짐- 예외 타입에 따라 적절한
log level
(info, warn, error)로 로깅하여 운영 중 원인 추적 용이 FeignClientException
에 대해서는 fallback 내부에서도 그대로 throw하여 비즈니스 실패로 처리
🧾 적용 코드 요약
@Retry(name = "productServiceClient") // 재시도 최대 3회
@CircuitBreaker(name = "productServiceClient", fallbackMethod = "useStockFallback")
public List<ProductClientResponse> useStock(StockRequest request) {
return productFeignClient.useStock(request);
}
resilience4j:
retry:
configs:
default:
retry-exceptions:
- feign.RetryableException
- feign.FeignException.FeignServerException
ignore-exceptions:
- feign.FeignException.FeignClientException
circuitbreaker:
configs:
default:
record-exceptions:
- feign.RetryableException
- feign.FeignException.FeignServerException
ignore-exceptions:
- feign.FeignException.FeignClientException
✅ 적용 효과 (Outcome)
- *비즈니스 예외(4xx)**는 재시도 없이 즉시 실패 처리 → API 낭비 방지
- *서버 장애(5xx, 타임아웃)**는 최대 3회까지 재시도 후 서킷브레이커 fallback 호출 → 장애 전파 방지
- Fallback 내부의 명확한 커스텀 예외 정의로 전체 흐름이 한눈에 파악 가능해지고, 시스템 안정성 향상
- 무한 루프 또는 미묘한 예외 누락 등의 사일런트 실패 방지
🔭 향후 개선 방향 (Next Steps)
- 🔍 Fallback 트리거 시 알림 발송 로직 연동 → Slack, Sentry, PagerDuty 등과 연계 예정
- 📈 서킷브레이커 상태, 재시도 통계 등 메트릭을 Prometheus로 수집 → Grafana 시각화
- 🧪 Retry 성능/효율성 튜닝을 위한 실 트래픽 기반 테스트 수행 예정
💬 요약
“Resilience4j의 Retry와 CircuitBreaker를 동시에 사용할 때 예외 흐름 충돌 문제가 발생했지만, 예외 분리 설정과 fallback 구조 분리, 커스텀 예외 정의를 통해 안정적이고 예측 가능한 장애 대응 흐름을 확보했습니다.”