2.7 비동기 처리 (@Async)
메일 발송·외부 API 호출·알림처럼 응답을 기다리지 않아도 되는 작업은 비동기로 처리하면 요청 스레드를 빨리 놓아줄 수 있습니다. 스프링은 @Async로 메서드를 비동기 실행할 수 있게 해줍니다.
작성 기준: Spring Boot 3.2.x
1. @EnableAsync 및 기본 사용
@SpringBootApplication
@EnableAsync
public class Application { ... }
@Service
public class NotificationService {
@Async
public void sendEmail(String to, String subject, String body) {
// 메일 발송 로직 — 별도 스레드에서 실행
mailSender.send(to, subject, body);
}
}
- @Async가 붙은 메서드는 다른 스레드에서 실행됩니다.
- 같은 빈 내부에서
this.sendEmail(...)처럼 호출하면 프록시를 타지 않아 비동기로 동작하지 않습니다. 다른 빈에서 호출해야 합니다.
2. 스레드 풀 설정
기본은 SimpleAsyncTaskExecutor(매번 새 스레드)이므로, 운영에서는 스레드 풀을 지정하는 것이 좋습니다.
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
executor.initialize();
return executor;
}
}
특정 메서드만 다른 풀을 쓰려면 **@Async("customExecutor")**처럼 빈 이름을 지정하고, 해당 Executor 빈을 정의하면 됩니다.
3. 반환값 — CompletableFuture
비동기 결과를 나중에 쓰려면 CompletableFuture<T>를 반환합니다.
@Async
public CompletableFuture<OrderResult> createOrderAsync(OrderRequest request) {
OrderResult result = orderService.create(request);
return CompletableFuture.completedFuture(result);
}
호출부에서는 .get() 또는 .thenAccept(...) 등으로 결과를 사용할 수 있습니다.
4. 예외 처리
@Async 메서드에서 던진 예외는 호출 스레드로 전파되지 않습니다. AsyncUncaughtExceptionHandler를 설정하거나, 반환 타입이 Future/CompletableFuture이면 .get() 시 ExecutionException으로 감싸져 전달됩니다.
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (ex, method, params) ->
log.error("Async error @{} {}", method.getName(), params, ex);
}
팁
무거운 작업만 @Async로 분리하고, 스레드 풀 크기와 큐 용량을 부하에 맞게 조정하세요. DB/외부 API 호출이 많은 비동기 메서드는 타임아웃·재시도(Retry)와 함께 사용하는 경우가 많습니다.