Skip to content

Commit 4fd410f

Browse files
committed
Update Technology Post "외부 시스템과의 연동 시 발생하는 장애를 격리하기 위해 서킷 브레이커 적용하기"
- 모니터링 연동 관련 내용 추가 - 이미지 저장하는 폴더 위치 수정
1 parent b58d27b commit 4fd410f

14 files changed

+27
-12
lines changed

_posts/2025-12-06-spring-boot-kotlin-circuit-breaker.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ author: devFancy
2626

2727
## CircuitBreaker란
2828

29-
![](/assets/img/technology/circuit-breaker-concept-and-apply-springboot-kotlin-1.png)
29+
![](/assets/img/technology/circuit-breaker/circuit-breaker-concept-and-apply-springboot-kotlin-1.png)
3030

3131
`CircuitBreaker`(서킷 브레이커)는 문제가 발생한 지점을 감지하여 실패하는 요청을 차단하고(Open),
3232
이를 통해 시스템의 장애 확산을 막고 장애 복구를 도와주는 기능을 제공합니다.
@@ -53,7 +53,7 @@ Resilience4j는 두 가지 타입을 제공합니다.
5353

5454
서킷 브레이커는 일반적으로 3가지 상태를 가지며, 이외에 특수 상태로 2가지를 가집니다.
5555

56-
![](/assets/img/technology/circuit-breaker-concept-and-apply-springboot-kotlin-2.png)
56+
![](/assets/img/technology/circuit-breaker/circuit-breaker-concept-and-apply-springboot-kotlin-2.png)
5757

5858
- `Closed` (정상 상태)
5959
- 평소 정상적으로 요청을 처리하는 상태입니다.
@@ -489,7 +489,7 @@ class CircuitUtilControllerTest {
489489
}
490490
```
491491

492-
![](/assets/img/technology/circuit-breaker-concept-and-apply-springboot-kotlin-3.png)
492+
![](/assets/img/technology/circuit-breaker/circuit-breaker-concept-and-apply-springboot-kotlin-3.png)
493493

494494
> CircuitBreakerUtilsTest.kt
495495
@@ -556,7 +556,7 @@ class CircuitBreakerUtilsTest {
556556
}
557557
```
558558

559-
![](/assets/img/technology/circuit-breaker-concept-and-apply-springboot-kotlin-4.png)
559+
![](/assets/img/technology/circuit-breaker/circuit-breaker-concept-and-apply-springboot-kotlin-4.png)
560560

561561
## 마무리하기
562562

_posts/2025-12-12-spring-boot-kotlin-external-api-circuit-breaker.md

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ author: devFancy
5454

5555
아래는 네이버 쇼핑을 통해서 화장품 제품인 '제로이드 수딩 크림 100ml'를 결제할 때의 사진입니다.
5656

57-
![](/assets/img/technology/springboot-kotlin-external-api-circuit-breaker-naverpay-order.png)
57+
![](/assets/img/technology/circuit-breaker/springboot-kotlin-external-api-circuit-breaker-naverpay-order.png)
5858

5959
이때, '결제하기' 버튼을 클릭했을 때 외부 시스템 PG사로부터 장애가 발생한다면 어떤 일이 발생할까요?
6060

@@ -73,7 +73,7 @@ author: devFancy
7373

7474
코드로 넘어가기 전에, 간단하게 내부 서비스인 '결제'와 외부 서비스인 'PG'사와의 관계를 먼저 살펴보겠습니다.
7575

76-
![](/assets/img/technology/springboot-kotlin-external-api-circuit-breaker-payment-pg.png)
76+
![](/assets/img/technology/circuit-breaker/springboot-kotlin-external-api-circuit-breaker-payment-pg.png)
7777

7878
위 그림은 내부 결제 서비스와 외부 PG사 간의 상호작용을 나타냅니다.
7979
구조적으로 보면, PG사로부터 결제 결과(성공/실패)를 콜백(Callback) 형태로 전달받고,
@@ -413,7 +413,7 @@ Beeceptor Console > Mocking Rules 메뉴에서 아래 규칙을 등록해야 합
413413

414414
테스트를 위해 적용할 API 부분만 아래 그림과 같이 체크 표시를 하면 됩니다.
415415

416-
![](/assets/img/technology/springboot-kotlin-external-api-circuit-breaker-payment-mocking-rule.png)
416+
![](/assets/img/technology/circuit-breaker/springboot-kotlin-external-api-circuit-breaker-payment-mocking-rule.png)
417417

418418
> Case 1: 결제 승인 성공 (Success)
419419

@@ -431,7 +431,7 @@ Beeceptor Console > Mocking Rules 메뉴에서 아래 규칙을 등록해야 합
431431
}
432432
```
433433

434-
![](/assets/img/technology/springboot-kotlin-external-api-circuit-breaker-payment-mocking-200-success.png)
434+
![](/assets/img/technology/circuit-breaker/springboot-kotlin-external-api-circuit-breaker-payment-mocking-200-success.png)
435435

436436
> Case 2: 결제 승인 실패 (Failure)
437437

@@ -447,7 +447,7 @@ Beeceptor Console > Mocking Rules 메뉴에서 아래 규칙을 등록해야 합
447447
}
448448
```
449449

450-
![](/assets/img/technology/springboot-kotlin-external-api-circuit-breaker-payment-mocking-400-fail.png)
450+
![](/assets/img/technology/circuit-breaker/springboot-kotlin-external-api-circuit-breaker-payment-mocking-400-fail.png)
451451

452452
### 2. 테스트용 데이터 작성하기
453453

@@ -503,7 +503,7 @@ Content-Type: application/json
503503
13:20:29.626| INFO|{{traceId}},{{spanId}}|i.d.c.c.a.client.BeeceptorPaymentClient |[PAYMENT_CLIENT] 응답 성공 - Result: PaymentResult(externalPaymentKey=payment_key_test, method=CARD, approveNo=12345678, message=결제 성공)
504504
```
505505
506-
## 테스트 코드로 확인해보기
506+
## 테스트 코드로 확인하기
507507
508508
마지막으로, 앞서 Beeceptor에 설정해둔 500 에러 응답 규칙을 기반으로, 실제 코드에서 서킷 브레이커가 정상적으로 동작하는지 테스트 코드로 검증해보겠습니다.
509509
@@ -558,11 +558,11 @@ class BeeceptorPaymentClientTest(
558558

559559
테스트 실행 결과, 아래 그림과 같이 모든 검증 과정을 통과한 것을 확인할 수 있습니다.
560560

561-
![](/assets/img/technology/springboot-kotlin-external-api-circuit-breaker-testcode-success.png)
561+
![](/assets/img/technology/circuit-breaker/springboot-kotlin-external-api-circuit-breaker-testcode-success.png)
562562

563563
Beeceptor Mock 서버의 로그를 확인해보면, 의도한 대로 10번의 500 에러 요청이 발생했음을 알 수 있습니다.
564564

565-
![](/assets/img/technology/springboot-kotlin-external-api-circuit-breaker-payment-500-error.png)
565+
![](/assets/img/technology/circuit-breaker/springboot-kotlin-external-api-circuit-breaker-payment-500-error.png)
566566

567567
마지막으로 애플리케이션 로그를 통해 서킷 브레이커의 상태 변화를 확인할 수 있습니다.
568568
로그를 자세히 보시면 10번째 실패 이후 `CallNotPermittedException`이 발생하며, 이후
@@ -577,6 +577,21 @@ io.github.resilience4j.circuitbreaker.CallNotPermittedException: CircuitBreaker
577577
13:53:43.099| WARN|{traceId}, {spanId}|i.d.c.c.a.client.BeeceptorPaymentClient |[Circuit Open] Beeceptor 결제 서비스 차단됨. 잠시 후 재시도 필요.
578578
```
579579

580+
## 모니터링 연동하기
581+
582+
앞서 테스트 코드를 통해 서킷 브레이커가 정상적으로 동작하는 것을 검증했습니다.
583+
하지만 실제 운영 환경에서는 로그만으로 서킷의 상태 변화나 실패율 추이를 실시간으로 파악하기 어렵습니다.
584+
585+
따라서 장애 발생 시 빠르게 인지하고 대응하기 위해 Prometheus와 Grafana를 활용하여 모니터링 시스템을 구축했습니다.
586+
(연동 코드에 대한 자세한 내용은 [해당 PR](https://github.com/devFancy/kotlin-java-playground/pull/9)을 참고해 주시기 바랍니다.)
587+
588+
Resilience4j는 Micrometer를 통해 서킷 브레이커의 상태(OPEN, HALF_OPEN, CLOSED), 실패율, 호출 횟수 등의 다양한 메트릭을 Prometheus로 노출할 수 있도록 지원합니다.
589+
이를 Grafana 대시보드와 연동하면 아래와 같이 시각화된 데이터를 확인할 수 있습니다.
590+
591+
![](/assets/img/technology/circuit-breaker/circuit-breeaker-monitoring-metrics.png)
592+
593+
이처럼 모니터링을 구축해 두면, 외부 시스템 장애 발생 시 서킷이 정상적으로 차단되었는지, 혹은 회복 상태(Half-Open)로 진입했는지를 대시보드를 통해 직관적으로 확인할 수 있어 운영 안정성을 높일 수 있습니다
594+
580595
## 마무리하기
581596

582597
외부 시스템 연동 시 장애 전파를 방지하고 시스템을 격리하기 위해 서킷 브레이커를 적용할 수 있습니다.

assets/img/technology/circuit-breaker-concept-and-apply-springboot-kotlin-1.png renamed to assets/img/technology/circuit-breaker/circuit-breaker-concept-and-apply-springboot-kotlin-1.png

File renamed without changes.

assets/img/technology/circuit-breaker-concept-and-apply-springboot-kotlin-2.png renamed to assets/img/technology/circuit-breaker/circuit-breaker-concept-and-apply-springboot-kotlin-2.png

File renamed without changes.

assets/img/technology/circuit-breaker-concept-and-apply-springboot-kotlin-3.png renamed to assets/img/technology/circuit-breaker/circuit-breaker-concept-and-apply-springboot-kotlin-3.png

File renamed without changes.

assets/img/technology/circuit-breaker-concept-and-apply-springboot-kotlin-4.png renamed to assets/img/technology/circuit-breaker/circuit-breaker-concept-and-apply-springboot-kotlin-4.png

File renamed without changes.
244 KB
Loading

assets/img/technology/springboot-kotlin-external-api-circuit-breaker-naverpay-order.png renamed to assets/img/technology/circuit-breaker/springboot-kotlin-external-api-circuit-breaker-naverpay-order.png

File renamed without changes.

assets/img/technology/springboot-kotlin-external-api-circuit-breaker-payment-500-error.png renamed to assets/img/technology/circuit-breaker/springboot-kotlin-external-api-circuit-breaker-payment-500-error.png

File renamed without changes.

assets/img/technology/springboot-kotlin-external-api-circuit-breaker-payment-mocking-200-success.png renamed to assets/img/technology/circuit-breaker/springboot-kotlin-external-api-circuit-breaker-payment-mocking-200-success.png

File renamed without changes.

0 commit comments

Comments
 (0)