동기가 있으면 비동기도 있기 마련
- 대용량 트래픽 환경에서 성능 개선 원리
- 사용자 경험(UX) 향상을 위한 응답시간 최적화
- 동기(Synchronous), 비동기(Asynchronous) 처리의 차이
- Java 비동기 처리와 Spring 비동기 처리의 차이
- Spring @Async 기능에 대한 기초 개념, @EnableAsync 및 ThreadPool에 대해
Spring 비동기 처리를 왜 해야하는가?
오늘날 서비스는 수천 ~ 수만건의 요청을 '동시에' 처리해야함
쇼핑몰, SNS, 게임 서버, 예약 시스템 등에서 요청량 증폭과 응답 속도 저하가 빈번하게 발생함
이럴 때 Spring을 통한 비동기 처리 기능은 성능 개선과 사용자 경험 향상을 동시에 제공하는 핵심 기술이다.
1. 대용량 트래픽 환경에서 성능 개선
일반적으로 사용자의 요청은 '요청 - > 처리 - > 응답'의 흐름으로 진행된다
만약 한 요청이 DB조회, 외부 API 호출 등으로 인해 수초가 걸린다면, 해당 스레드는 그 수초동안 아무일도 하지 않고 대기한다.
이걸 스레드 낭비라고 부르며, 이러한 스레드 낭비가 대용량 트래픽 환경에서 심각한 성능 저하로 이어질 수 있다.
위와 같은 문제를 비동기(Asynchronous) 처리로 해결한다.
요청을 백그라운드 스레드에게 위임하고, 메인 스레드는 즉시 다음 요청을 처리한다.
스레드의 대기 시간이 줄어들고, 서버는 훨씬 많은 요청을 동시에 처리할 수 있다
2. 사용자 경험(UX) 향상을 위한 응답 시간 최적화
사용자는 빠른 응답을 가장 먼저 체감한다. 생각해보자. 느린 웹사이트 쓰고 싶은가? 아니요?
비동기 처리를 통해 서버는 '응답'과 '처리'를 분리하여 사용자에게 즉시 피드백을 제공해줄 수 있다.
처리하면서 응답을 동시에 해준다면, 우리가 처리하는게 아니지만 '처리됐구나' 라고 사용자가 생각할 수 있다. 좋은것임
| 동기 | 비동기 | |
| 사용자 관점 | 대기 시간 발생 | 즉시 피드백 제공 |
| 시스템 관점 | 스레드가 대기상태 | 스레드 재활용 가능 |
| 결과 전달 방식 | 결과를 기다린 후 응답해줌 | 요청 후 별도 프로세스에서 응답처리 |
비동기처리는 빠른 응답 + 백그라운드 처리를 통해 UX와 성능을 동시에 개선해준다.
3. 예제
1) 동기
public class SyncExample {
public static void main(String[] args) {
System.out.println("요청 1 시작");
process();
System.out.println("요청 1 완료");
}
private static void process() {
try {
Thread.sleep(2000);
System.out.println("처리 완료");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
우리가 기존에 돌리던 main 메서드임, 모든 순서가 순차적으로 실행된다
2) 비동기
import java.util.concurrent.CompletableFuture;
public class AsyncExample {
public static void main(String[] args) {
System.out.println("요청 1 시작");
CompletableFuture.runAsync(() -> { // CompletableFuture.runAsync()를 통해 비동기처리
try {
Thread.sleep(2000);
System.out.println("비동기 처리 완료");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("요청 1 완료 (즉시 반환)");
}
}
4. Spring 비동기 처리의 역할
단순 자바 수준의 병렬 실행을 넘어서, 서비스 계층 전체에서 비즈니스 로직 단위로 비동기 실행을 제어할 수 있다고 함
| Java CompletableFuture | Spring @Async | |
| 스레드 관리 | 직접 Executor 작성 필요 | Spring이 ThreadPoolTaskExecutor로 관리 |
| 코드 구조 | 코드 내부에 Future 체인 필요 | 메서드에 @Async 추가하면됨 |
| 예외처리 | try-catch exceptionally() 직접 처리 | AsyncUncaughtExceptionHandler 자동 관리 |
| 통합성 | 독립실행 | 트랜잭션, 보안, AOP 통합가능 |
5. 실전에서 대용량 이메일 발송할 때..
| 시나리오 | 동기처리시 문제점 | 비동기 처리효과 |
| 1만명의 사용자에게 메일 발송 | 한명씩 순차발송 - > 응답시간 5분 이상 | 요청 즉시 응답후 백그라운드 발송 |
| 외부 SMTP API 호출지연 | API 대기시간 누적 | 별도 스레드에서 병렬 실행 |
| 사용자 응답속도 | 매우 느림 | 즉시 "요청 접수됨" 표시 |
팁
- 데이터 정합성이 중요한 트랜잭션의 경우 반드시 동기 처리해야함
- 이메일, 알림, 로그, 외부 연동 등은 비동기 처리를 통해 효율을 극대화할 수 있음
- 비동기 예외는 메인 스레드로 전파되지 않기 때문에 AsyncUncaughtExceptionHandler를 설정해야함
- 스레드풀의 크기와 큐 용량을 환경에 맞게 조정해야함
- @Async를 사용하려면 반드시 @EnableAsync 설정이 필요함
정리
| 항목 | 요약 |
| 비동기 처리의 목적 | 대용량 트래픽에서 성능을 높이고, 응답속도를 개선함 |
| UX 향상 포인트 | 사용자가 기다리지 않도록 즉시 피드백 제공 |
| Spring의 역할 | 비즈니스 단위에서 간단하게 비동기 실행을 제어 |
| Java와의 차이 | 코드 단순화 + 스레드 관리 자동화 |
'Spring Boot > 비동기 처리' 카테고리의 다른 글
| 비동기 처리 : @Async 활용 (0) | 2025.11.02 |
|---|---|
| 비동기 처리 : 비동기 처리 방식 (1) | 2025.11.02 |
| Spring Event : 분산 환경으로의 확장 가능성 (0) | 2025.10.29 |
| Spring Event : 이벤트 기반 비동기 처리 활용사례 (0) | 2025.10.29 |
| Spring Event : 비동기 이벤트 처리 구현 (0) | 2025.10.25 |