어제 오늘 내일

[Troubleshooting] WebFlux 도입 전 꼭 알아야 할 주의사항 (BlockHound 사용법) 본문

IT/SpringBoot

[Troubleshooting] WebFlux 도입 전 꼭 알아야 할 주의사항 (BlockHound 사용법)

hi.anna 2026. 4. 4. 08:24

 
MVC에서는 쓰레드 하나가 막혀도(Blocking) 다른 쓰레드 199개는 잘 돌아갑니다.
하지만 WebFlux는 쓰레드가 고작 CPU 코어 수(예: 4개)만큼밖에 없습니다. 하나가 막히면 서버 성능의 25%가 날아가는 셈이죠.

문제는 실수로 JDBC를 쓰거나, Thread.sleep을 써도 컴파일 에러가 안 난다는 것입니다. 배포하고 나서야 서버가 죽는 걸 보게 되죠.
이걸 막아주는 수호신, BlockHound를 소개합니다.


1. BlockHound: "블로킹 코드가 보이면 에러를 뱉어라!"

Reactor 팀이 만든 도구로, 논블로킹 쓰레드(Reactor Thread)에서 블로킹 작업이 감지되면 즉시 예외(BlockingOperationError)를 던져서 테스트를 실패하게 만듭니다.

① 의존성 추가

dependencies {
    testImplementation 'io.projectreactor:reactor-test' // 여기에 포함되어 있음
}

② 설정 ( 메서드나 테스트 설정에 추가)

public static void main(String[] args) {
    // ★ 앱 시작 시 최상단에 선언!
    BlockHound.install();

    SpringApplication.run(MyApplication.class, args);
}

③ 효과 확인

이제 아래 코드를 실행하면 에러가 납니다.

Mono.delay(Duration.ofSeconds(1))
    .doOnNext(it -> {
        try {
            Thread.sleep(100); // ❌ BlockHound가 잡아서 에러 냄!
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    })
    .subscribe();

개발/테스트 단계에서 Thread.sleep, InputStream.read, JDBC 호출 등을 원천 봉쇄할 수 있습니다. (필수 적용 권장!)


2. 디버깅: "스택 트레이스가 이상해요..."

WebFlux에서 에러가 나면 스택 트레이스(Stack Trace)가 수백 줄이 나오는데, 정작 내 코드는 안 보이고 Reactor 내부 클래스만 잔뜩 보입니다.
비동기라서 "누가 호출했는지" 정보가 런타임에 사라지기 때문입니다.

🛠 해결책 1:

의심 가는 부분에 .checkpoint("설명")를 붙이세요.

flux.map(x -> logicA(x))
    .checkpoint("logicA-after") // 에러 나면 이 지점을 알려줌
    .flatMap(x -> logicB(x))
    .checkpoint("logicB-after")
    .subscribe();

에러 로그에 Assembly trace from producer [reactor.core.publisher.FluxMap] : ... logicA-after라고 친절하게 뜹니다.

🛠 해결책 2:

개발 환경(dev)에서는 앱 시작 시 이 옵션을 켜두면, 모든 연산자의 스택 트레이스를 기록합니다. (단, 운영 환경에선 성능 저하가 심하므로 끄세요!)


3. 언제 WebFlux를 쓰면 안 되나요?

WebFlux가 만능은 아닙니다. 오히려 이 되는 경우도 많습니다.

❌ 1. CPU를 많이 쓰는 작업 (암호화, 동영상 인코딩)

이벤트 루프는 "주문 받고 넘기는 일(I/O)"에 특화되어 있습니다.
주방장(Event Loop)이 직접 요리(CPU 연산)를 하면 주문을 못 받습니다.
이런 작업은 별도 쓰레드 풀이나 MVC가 낫습니다.

❌ 2. 팀원들의 러닝 커브

JPA도 모르는데 R2DBC와 flatMap까지 배우라고 하면 퇴사자가 속출할 수 있습니다.
단순한 CRUD 서비스라면 생산성이 높은 MVC + JPA가 정답입니다.

❌ 3. 복잡한 트랜잭션 로직

쇼핑몰 결제처럼 "A 호출하고, 실패하면 B 호출하고, C 업데이트하고..." 로직이 복잡하면, WebFlux의 콜백 지옥(Callback Hell)보다 더한 Operator Hell을 맛보게 됩니다. 코드를 읽을 수가 없습니다.
 
 

반응형
Comments