2025-06-09

비동기와 병렬 처리

SpringJava비동기CompletableFutureWebFlux

비동기와 병렬 처리

모던 백엔드 애플리케이션에서는 지연 시간을 줄이고 리소스 활용을 극대화하기 위해 비동기 및 병렬 처리가 필수적입니다. Spring은 @Async와 CompletableFuture를, 순수 Java는 Thread, ExecutorService, ForkJoinPool을 제공하며, 리액티브 프로그래밍으로 Spring WebFlux까지 확장할 수 있습니다.


1. @Async를 이용한 간편 비동기 처리

  • Spring의 애노테이션 기반 비동기 처리 방식
  • 별도의 스레드 풀에서 메서드를 실행하여 호출자 스레드 블로킹을 방지
@Service
public class NotificationService {
    @Async
    public void sendEmail(String to, String subject, String body) {
        // 이메일 전송 로직 (blocking I/O)
    }
}
 
@Service
public class OrderService {
    private final NotificationService notificationService;
 
    @Transactional
    public void placeOrder(OrderDto dto) {
        // 1) 재고 확인
        // 2) 결제 처리
        // 3) 주문 저장
 
        // 비동기 이메일 알림
        notificationService.sendEmail(dto.getEmail(), "주문 완료",
            "주문이 접수되었습니다.");
    }
}

설정:

  • 애플리케이션 설정에 @EnableAsync 추가
  • AsyncConfigurer 또는 TaskExecutor 빈으로 스레드 풀 커스터마이징

2. CompletableFuture로 유연한 비동기 흐름 제어

Java 8에서 도입되어 체이닝, 예외 처리, 결과 조합이 용이합니다.

public CompletableFuture<OrderResult> processOrderAsync(OrderDto dto) {
    return CompletableFuture
        .supplyAsync(() -> validate(dto))
        .thenCompose(validDto -> CompletableFuture.supplyAsync(() -> charge(validDto)))
        .thenCombine(
            CompletableFuture.supplyAsync(() -> reserveStock(dto)),
            (payment, stock) -> new OrderResult(payment, stock)
        )
        .exceptionally(ex -> {
            // 예외 처리 로직
            return OrderResult.failure(ex);
        });
}

특징:

  • supplyAsync()는 기본 ForkJoinPool에서 실행
  • thenComposethenCombine으로 비동기 흐름 제어

3. Java Concurrency 기본 도구

| 도구 | 용도 | 특징 | |------|------|------| | Thread | 직접 스레드 생성 | 단순하지만 스레드 관리가 복잡 | | ExecutorService | 스레드 풀 관리 | 재사용 가능한 스레드 풀 제공, submit() | | ScheduledExecutorService | 예약/주기적 작업 | Cron과 유사한 스케줄링 | | ForkJoinPool | 분할 정복 병렬 처리 | RecursiveTask/RecursiveAction 기반 |

가이드라인:

  • 직접 Thread 사용 대신 ExecutorService를 사용하여 리소스 낭비 방지
  • ForkJoin은 분해를 통한 데이터 병렬 처리에 탁월

4. Spring WebFlux (리액티브 프로그래밍)

높은 동시성, 대규모 서비스에서 고려할 만한 선택지입니다.

핵심 개념: 비동기 논블로킹 리액티브 I/O

@RestController
@RequestMapping("/orders")
public class OrderController {
    private final OrderService orderService;
 
    @PostMapping
    public Mono<OrderResult> createOrder(@RequestBody OrderDto dto) {
        return orderService.processOrderReactive(dto);
    }
}
 
@Service
public class OrderService {
    public Mono<OrderResult> processOrderReactive(OrderDto dto) {
        return Mono.fromCallable(() -> validate(dto))
            .publishOn(Schedulers.boundedElastic())
            .flatMap(validDto -> Mono.fromCallable(() -> charge(validDto)))
            .zipWith(Mono.fromCallable(() -> reserveStock(dto)))
            .map(tuple -> new OrderResult(tuple.getT1(), tuple.getT2()));
    }
}

특징:

  • Mono와 Flux를 사용한 선언적 데이터 흐름
  • I/O 바운드 작업은 Schedulers.boundedElastic()으로 처리

마무리

비동기 및 병렬 처리는 애플리케이션 성능과 확장성을 향상시키는 핵심 기법입니다.

  • @Async와 CompletableFuture - 간단한 비동기 작업
  • ExecutorService, ForkJoinPool - 세밀한 병렬 처리 제어
  • Spring WebFlux - 논블로킹 리액티브 처리