2025-06-09
비동기와 병렬 처리
비동기와 병렬 처리
모던 백엔드 애플리케이션에서는 지연 시간을 줄이고 리소스 활용을 극대화하기 위해 비동기 및 병렬 처리가 필수적입니다. 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 - 논블로킹 리액티브 처리
관련 글
벡터 유사도 기반76% 일치
Kafka 기초: 메시지 큐 vs 이벤트 스트리밍
메시지 큐와 이벤트 스트리밍의 차이, Kafka 핵심 개념, Spring Boot에서의 Producer/Consumer 구현까지 정리한다.
74% 일치Java 8에서 25까지 — 단계적 마이그레이션으로 보일러플레이트 900줄 제거한 이야기
Java 8 + Spring Boot 2.x 레거시를 Java 25 + Spring Boot 3.5로 단계적 마이그레이션하며 겪은 실전 경험을 공유합니다.
71% 일치Flix 프로젝트: 개발 기록 - 5
로그 수집 시스템 개선과 Spring Cloud Config를 활용한 중앙 설정 관리 구축에 대한 개발 기록입니다.