-
Spring Boot 고급 시리즈 8화 – 비동기 프로그래밍과 @Async의 활용 전략기술과 산업/언어 및 프레임워크 2025. 7. 2. 11:15728x90
Spring Boot에서 비동기 프로그래밍은 단순한 성능 최적화 기술을 넘어, 대규모 서비스 아키텍처의 핵심 구성 요소가 되었습니다. 특히 @Async 애노테이션은 명확하고 간결한 방식으로 비동기 처리를 가능하게 해주며, Spring의 쓰레드 풀과 연동하여 유연한 확장성까지 제공합니다. 이번 글에서는 @Async의 작동 방식부터 실무에서 주의해야 할 활용 전략까지 깊이 있게 분석해봅니다.
@Async의 동작 방식 이해
@Async는 내부적으로 TaskExecutor를 통해 메서드 호출을 별도의 쓰레드에서 처리합니다. 호출하는 쪽은 즉시 반환되며, 해당 메서드는 별도 쓰레드에서 비동기로 실행됩니다.
@Async public void sendNotification(String message) { // 외부 API 호출 등 시간이 오래 걸리는 작업 }
이 기능이 작동하기 위해선 @EnableAsync 설정이 필요하며, 이를 통해 Spring은 비동기 처리를 위한 프록시 객체를 생성하게 됩니다.
@Configuration @EnableAsync public class AsyncConfig { }
비동기 메서드의 반환값: Future, CompletableFuture
비동기 메서드는 void뿐 아니라 Future<T> 또는 CompletableFuture<T>를 반환할 수도 있습니다. 특히 CompletableFuture는 자바 8 이후 비동기 스트림 처리와 잘 어울리기 때문에 실무에서도 선호되는 타입입니다.
@Async public CompletableFuture<String> fetchData() { String data = remoteService.call(); return CompletableFuture.completedFuture(data); }
ThreadPool 설정 전략
비동기 처리는 쓰레드 풀 전략이 동반되지 않으면 오히려 성능 저하를 유발할 수 있습니다. 기본 TaskExecutor는 성능에 최적화되지 않았기 때문에, 반드시 커스텀 설정을 통해 스레드 수와 큐 용량을 명확히 지정하는 것이 바람직합니다.
@Configuration public class ExecutorConfig { @Bean(name = "taskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setMaxPoolSize(20); executor.setQueueCapacity(100); executor.setThreadNamePrefix("AsyncExecutor-"); executor.initialize(); return executor; } }
그리고 @Async("taskExecutor")로 지정해주는 방식으로 실행 풀을 명확하게 연결할 수 있습니다.
트랜잭션과의 관계
비동기 메서드는 호출 시점과 실행 시점이 다르기 때문에 트랜잭션 전파에 주의해야 합니다. 예를 들어, @Transactional이 적용된 메서드 내에서 비동기 메서드를 호출하는 경우, 그 트랜잭션은 비동기 메서드에 전파되지 않습니다.
실무에서는 다음과 같은 문제가 자주 발생합니다:
- 비동기 메서드가 DB 커넥션을 사용하는 경우, 트랜잭션 종료 전에 실행되면 예외 발생
- 호출 대상 클래스가 프록시 기반으로 생성되지 않은 경우, @Async가 작동하지 않음
이러한 이유로 비동기 메서드는 항상 별도의 컴포넌트로 분리하고, 트랜잭션 종료 후 실행되도록 설계하는 것이 바람직합니다.
실무 적용 사례
비동기 프로그래밍은 다음과 같은 영역에서 효과적으로 활용됩니다:
- 알림/메일 전송
- 외부 API 호출
- 대량 데이터 처리
- 이미지/영상 변환 등 I/O 중심 작업
실제로 쇼핑몰 플랫폼에서는 주문 완료 후 ‘결제 연동’, ‘재고 업데이트’, ‘SMS 전송’ 등을 비동기 처리로 구성해 성능과 안정성을 동시에 확보한 사례가 많습니다.
정리하며
Spring Boot에서의 @Async는 단순한 편의 기능이 아닌, 명확한 목적과 제약을 가진 아키텍처 구성 요소입니다. 쓰레드 풀 전략, 트랜잭션 분리, 예외 처리 등 실무적인 고려사항 없이는 오히려 장애의 원인이 될 수 있기에, 체계적인 설계와 철저한 테스트가 필수입니다.
728x90'기술과 산업 > 언어 및 프레임워크' 카테고리의 다른 글
Spring Boot 고급 시리즈 9화 – 메트릭 수집과 Micrometer 통합 전략 (6) 2025.07.18 Spring Framework 시리즈 15화 – 스프링 MVC의 예외 처리 전략과 @ExceptionHandler 사용법 (1) 2025.07.18 Spring Framework 시리즈 14화 – Model, ModelAndView, ResponseEntity 비교와 활용법 (0) 2025.07.02 Spring AI 시리즈 9화 - Milvus와 Spring AI 통합 전략 (1) 2025.06.26 FastAPI 시리즈 15화 - 보안 관점에서 보는 FastAPI 최적 설정 가이드 (0) 2025.06.22