개발/Spring Boot
Spring Boot 시리즈 21편 – 비동기 처리와 병렬 프로그래밍: @Async, CompletableFuture, Reactor 실전 사용법
B컷개발자
2025. 4. 30. 12:41
728x90
SMALL
Spring Boot에서 @Async, CompletableFuture, Reactor를 활용한 비동기 및 병렬 처리 전략을 소개합니다. 서비스 성능을 높이는 실전 적용 예시 포함.
Spring Boot 시리즈 21편 – 비동기 처리와 병렬 프로그래밍: @Async, CompletableFuture, Reactor 실전 사용법
애플리케이션 성능 병목의 90%는 IO 지연 또는 외부 시스템 호출에서 발생합니다.
이럴 때 가장 효과적인 전략은 비동기화와 병렬 처리입니다.
이번 글에서는 Spring Boot에서 @Async, CompletableFuture, Project Reactor를 활용해
실제로 서비스 성능을 높일 수 있는 실전 비동기 처리 전략을 소개합니다.
📌 1. 비동기 처리란?
비동기(Asynchronous)란 요청과 응답을 기다리지 않고 작업을 위임하거나 병렬로 처리하는 방식입니다.
비교 항목 동기(Sync) 비동기(Async)
응답 대기 | 호출자가 결과를 기다림 | 결과를 기다리지 않고 다음 작업 수행 |
쓰레드 점유 | 대기 중에도 점유됨 | 쓰레드 효율적 사용 가능 |
장점 | 코드 직관적 | 응답 지연 감소, 병렬 처리 가능 |
✅ 2. @Async 기반 비동기 처리
1️⃣ 설정
@SpringBootApplication
@EnableAsync
public class MyApp {}
2️⃣ 서비스에 @Async 적용
@Service
@Slf4j
public class NotificationService {
@Async
public void sendEmail(String to, String content) {
log.info("Sending email to {}...", to);
// 실제 이메일 발송 코드
}
}
- 호출자는 즉시 리턴되며, 이메일 발송은 별도 쓰레드에서 수행됨
3️⃣ 비동기 메서드의 반환값 – Future or CompletableFuture
@Async
public CompletableFuture<User> getUserInfo(Long id) {
return CompletableFuture.completedFuture(userRepository.findById(id).orElseThrow());
}
⚙️ 3. CompletableFuture 활용
병렬 호출 및 결과 병합 예시
public CompletableFuture<User> getUser(Long id) { ... }
public CompletableFuture<Order> getOrder(Long userId) { ... }
public UserDashboard loadDashboard(Long userId) {
CompletableFuture<User> userFuture = getUser(userId);
CompletableFuture<Order> orderFuture = getOrder(userId);
return userFuture.thenCombine(orderFuture, (user, order) ->
new UserDashboard(user, order)
).join(); // 결과 대기
}
- thenCombine: 두 결과를 병합
- join(): 결과를 기다림 (주의: 블로킹 발생 가능)
🔥 4. Project Reactor – 비동기 스트림 처리
기본 개념
개념 설명
Mono | 0 또는 1개의 비동기 결과 |
Flux | N개의 스트림 비동기 결과 |
non-blocking | Netty 등 비동기 HTTP 클라이언트 기반 (WebFlux에서 사용됨) |
Mono 예시
public Mono<User> findUser(String id) {
return Mono.fromCallable(() -> userRepository.findById(id).orElseThrow());
}
Flux 예시
public Flux<Product> getPopularProducts() {
return Flux.fromIterable(productRepository.findTop10ByOrderByHitsDesc());
}
WebClient와 함께 비동기 외부 API 호출
public Mono callExternalApi(String query) {
return WebClient.create()
.get()
.uri("https://api.example.com/search?q=" + query)
.retrieve()
.bodyToMono(ExternalResponse.class);
}
- Spring WebFlux 기반의 비동기 클라이언트
- Netty 기반의 non-blocking I/O 구조로 대규모 트래픽에 적합
🧠 실무 적용 전략
항목 전략
@Async | 간단한 비동기 위임 처리에 적합 |
CompletableFuture | 비동기 병렬 작업 간 결과 조합에 효과적 |
WebClient + Reactor | 외부 API 다수 호출, 고성능 비동기 처리에 적합 |
반환값 관리 | 비동기 Future는 적절한 예외 처리 필수 (handle, exceptionally) |
리소스 관리 | 별도 TaskExecutor 설정 권장 (@Async thread pool 조절) |
Executor 커스터마이징 예시
@Configuration
public class AsyncConfig {
@Bean(name = "asyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("Async-");
executor.initialize();
return executor;
}
}
✅ 마무리 요약
항목 요약
기본 비동기 | @Async + @EnableAsync로 쉽게 구현 가능 |
병렬 처리 | CompletableFuture의 thenCombine, allOf 등 사용 |
고급 처리 | Project Reactor의 Mono, Flux로 비동기 스트림 처리 |
외부 호출 | WebClient로 비동기 HTTP 연동 가능 |
실무 주의 | 예외처리, 스레드풀 조절, 결과 병합 시점 명확히 설계 |
📌 다음 편 예고
Spring Boot 시리즈 22편: API 버전 관리 전략 – URL, Header, Accept 기반 버전 설계 Best Practice
728x90
LIST