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에서 실행thenCompose와thenCombine으로 비동기 흐름 제어
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 - 논블로킹 리액티브 처리