본문으로 건너뛰기
Advertisement

실전 고수 팁 — BlockHound로 블로킹 코드 탐지 및 Resilience4j 서킷 브레이커 적용

1. BlockHound — 리액티브 파이프라인의 블로킹 침입 탐지기

WebFlux 파이프라인에서 실수로 블로킹 코드(예: JDBC 호출, Thread.sleep(), File.read())가 섞이면 이벤트 루프 스레드가 점유되어 전체 서버가 응답 불가 상태에 빠질 수 있습니다. BlockHound는 이런 위험한 블로킹 호출을 테스트 및 개발 단계에서 런타임 예외로 즉각 감지해주는 도구입니다.

testImplementation 'io.projectreactor.tools:blockhound:1.0.8.RELEASE'
@SpringBootTest
public class BlockHoundTest {

@BeforeAll
static void setup() {
BlockHound.install(); // JUnit 실행 전에 BlockHound 설치
}

@Test
void 블로킹_코드가_있으면_테스트_실패() {
StepVerifier.create(
Mono.delay(Duration.ofMillis(1))
.doOnNext(it -> {
try {
Thread.sleep(10); // ← BlockHound가 이 블로킹 호출을 감지하고 예외 던짐!
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
})
).verifyError(BlockingOperationError.class); // 예외가 발생해야 테스트 통과 (의도된 테스트)
}
}

2. Resilience4j 서킷 브레이커 — 외부 API 장애 격리

외부 API 서버가 다운된 상황에서 계속 요청을 보내면 우리 서버 스레드 풀도 소진되어 연쇄 장애로 이어집니다. **서킷 브레이커(Circuit Breaker)**는 실패율이 임계치를 초과하면 요청 자체를 차단하고 빠른 실패(Fail-Fast)로 응답하여 장애 전파를 막습니다.

implementation 'io.github.resilience4j:resilience4j-spring-boot3:2.2.0'
implementation 'org.springframework.boot:spring-boot-starter-aop'
resilience4j.circuitbreaker:
instances:
externalApiCb:
failure-rate-threshold: 50 # 50%이상 실패하면 OPEN 상태 전환
wait-duration-in-open-state: 10s # 10초 후 HALF-OPEN으로 전환 (재시도 허용)
sliding-window-size: 10 # 최근 10건의 요청 결과 기준으로 판단
@Service
@RequiredArgsConstructor
public class ExternalPaymentService {

private final WebClient webClient;

@CircuitBreaker(name = "externalApiCb", fallbackMethod = "paymentFallback")
public Mono<PaymentResult> requestPayment(PaymentRequest request) {
return webClient.post()
.uri("/external/pay")
.bodyValue(request)
.retrieve()
.bodyToMono(PaymentResult.class);
}

// 서킷이 열리거나 예외 발생 시 실행되는 대안 메서드
public Mono<PaymentResult> paymentFallback(PaymentRequest request, Throwable t) {
log.error("결제 서버 장애로 Fallback 실행: {}", t.getMessage());
// 고객에게 "잠시 후 다시 시도해달라"는 응답 반환
return Mono.just(PaymentResult.failure("PAYMENT_SERVER_ERROR"));
}
}
Advertisement