-
WIL - 장애 허용 시스템 구축Develop/WIL 2025. 8. 22. 21:52
주간 학습 회고 (Weekly I Learned)
이번 주의 핵심 미션은 "결제 시스템의 안정성을 극한으로 끌어올리는 것"이었습니다. 외부 PG(Payment Gateway) 연동 시 발생할 수 있는 예측 불가능한 장애에 효과적으로 대응하기 위해, 서킷 브레이커 패턴을 중심으로 재시도(Retry) 로직과 상황별 폴백(Fallback) 전략을 시스템에 체계적으로 적용했습니다.
🧠 이번 주 학습 성과
1. 재시도와 서킷 브레이커: 2중으로 구성한 안전망
단순한 재시도를 넘어, 장애가 지속될 때 시스템 전체를 보호하는 서킷 브레이커를 함께 구현하며 2단계 방어 체계를 구축했습니다.
// 1차 방어선: @Retry로 일시적인 오류 극복 시도 // 2차 방어선: @CircuitBreaker로 지속적인 장애 전파 차단 @CircuitBreaker(name = "pgClient", fallbackMethod = "requestFallback") @Retry(name = "pgClient") @Override public PaymentInfo.transaction request(PgClientDto.PgPaymentRequest request) { // PG 호출 로직 }- 학습 포인트: Resilience4j를 활용해 어노테이션만으로 선언적인 장애 대응 로직을 구현했습니다. 재시도가 모두 실패했을 때 서킷 브레이커가 동작하는 흐름을 명확히 이해하게 되었습니다.
2. 똑똑한 예외 처리: 모든 실패는 같지 않다
어떤 예외를 '실패'로 간주할지 명확히 정의하는 것이 서킷 브레이커의 오작동을 막는 핵심임을 깨달았습니다.
# 실패로 간주하여 서킷 카운트를 올릴 예외들 recordExceptions: - HttpServerErrorException # 5xx 서버 오류 - TimeoutException # 타임아웃 # 실패로 간주하지 않고 무시할 예외들 ignoreExceptions: - CoreException # 의도된 비즈니스 로직 예외- 핵심 인사이트: 일시적인 기술적 장애(5xx, Timeout)와 예측 가능한 비즈니스 예외(4xx, 유효성 검사)를 분리하여, 정말 필요한 순간에만 서킷 브레이커가 동작하도록 정교하게 설계했습니다.
3. 비즈니스를 이해하는 폴백: 상황에 맞는 최적의 플랜 B
장애 발생 시 무조건 에러를 반환하는 대신, 각 API의 비즈니스적 특성에 따라 최적의 대체 응답(Fallback)을 제공하는 전략을 수립했습니다.
[결제 요청 실패 시] 사용자에게 명확한 실패를 알려야 합니다.
return new PaymentInfo.transaction( // ... TransactionStatus.FAILED, "PG 통신 실패" );[거래내역 조회 실패 시] 실제 상태를 알 수 없으므로, 재조회 여지를 남겨둡니다.
return new PaymentInfo.transaction( // ... TransactionStatus.PENDING, "PG 통신 실패" );- 설계 원칙: 사용자 경험과 데이터 정합성을 최우선으로 고려하여, 각기 다른 비즈니스 맥락에 맞는 폴백 로직을 설계하는 원칙을 체득했습니다.
4. 최종 일관성 확보: 스케줄러를 이용한 상태 동기화
폴백 로직으로 PENDING 상태가 된 결제 건들을 그대로 두지 않고, 스케줄러를 통해 주기적으로 PG사에 상태를 재확인하여 최종적인 데이터 일관성을 확보하는 메커니즘을 구현했습니다.
public void verifyPendingPayments() { List<Payment> pendingPayments = paymentRepository.findByStatus(PENDING); for (Payment payment : pendingPayments) { try { // 스케줄러가 주기적으로 PG사에 상태를 재문의하여 동기화 verifyAndSyncPaymentStatusByScheduler(payment); } catch (Exception e) { // 개별 결제 건의 동기화 실패가 전체 스케줄러에 영향을 주지 않도록 격리 } } }- 핵심 학습: 실시간 처리가 실패하더라도, 배치(Batch) 작업을 통해 시스템의 상태를 결국 올바른 상태로 맞춰가는 최종 일관성(Eventual Consistency) 패턴의 중요성을 이해했습니다.
🤔 아쉬웠던 점과 개선할 부분
- 콜백 비동기 처리 및 결제 비즈니스 도메인 학습 미숙
- 문제점: 결제 시스템의 비동기 콜백 처리 방식과 복잡한 비즈니스 로직에 대한 이해도가 부족하여 초기 설계에 어려움을 겪었습니다.
- 개선 방안: 관련 기술 문서와 실제 결제 성공/실패 사례를 분석하여 도메인 지식을 보강하고, 비동기 처리에 대한 학습을 별도로 진행할 계획입니다.
- 다양한 장애 시나리오에 대한 회복 전략 부족
- 문제점: 현재는 기본적인 폴백과 재시도 전략에 집중했지만, 더 복잡한 시나리오에 대한 회복 전략은 충분히 고려하지 못했습니다.
- 개선 방안: 다음에는, 다양한 시나리오에 대응할 수 있는 견고한 회복 전략을 수립하겠습니다.
- 학습 내용 정리 미완료
- 문제점: 배운 내용이 방대해 블로그 포스팅을 완료하지 못했습니다. 지식의 체계화와 공유가 이루어지지 않아 아쉬웠습니다.
- 개선 방안: 학습과 동시에 핵심 포인트를 메모하는 습관을 들이고, 주제별로 작은 단위로 나누어 포스팅을 꾸준히 진행하겠습니다.
🚀 다음 주 도전 과제
- 애플리케이션 이벤트 기반으로 유스케이스 분리
- 현재 강하게 결합된 로직들을 애플리케이션 이벤트를 통해 분리하여, 시스템을 더 느슨하고 확장 가능하게 리팩토링하는 작업을 진행할 예정