Spring Boot(또는 Java)에서 Future 패턴은 비동기 프로그래밍을 위한 핵심 디자인 패턴입니다. 쉽게 말해, **“지금 당장 결과를 줄 수는 없지만, 미래의 어느 시점에 작업이 완료되면 결과물을 넘겨주겠다”**는 약속(Promise)과 같습니다.
1. Future 패턴의 핵심 개념
일반적인 동기(Synchronous) 방식에서는 작업이 끝날 때까지 스레드가 아무것도 못 하고 기다려야 합니다(Blocking). 반면, Future 패턴을 사용하면 작업을 별도의 스레드에 맡기고 메인 스레드는 자기 할 일을 계속할 수 있습니다.
주요 특징
- 비차단(Non-blocking): 결과가 나올 때까지 기다리지 않고 다른 작업을 수행할 수 있어 효율적입니다.
- 지연 실행: 결과가 실제로 필요한 시점까지 데이터 계산을 미룰 수 있습니다.
- 결합성: 여러 비동기 작업의 결과를 합치거나, 순차적으로 실행하는 흐름을 만들 수 있습니다.
2. Spring Boot에서의 구현 방식
Spring Boot에서는 주로 CompletableFuture와 @Async 어노테이션을 조합하여 이 패턴을 구현합니다.
① @Async 어노테이션
메서드 위에 이 어노테이션을 붙이면, 해당 메서드는 Spring의 TaskExecutor에 의해 별도의 스레드에서 실행됩니다.
② CompletableFuture<T>
Java 8에서 도입된 Future의 확장판입니다. 단순히 결과를 기다리는 것을 넘어, 작업 완료 후 콜백을 실행하거나 예외 처리를 하는 등 훨씬 강력한 기능을 제공합니다.
코드 예시:
@Service
public class OrderService {
@Async
public CompletableFuture<String> processOrder(Order order) {
// 시간이 오래 걸리는 작업 (예: 결제 처리)
Thread.sleep(2000);
return CompletableFuture.completedFuture("Order Processed: " + order.getId());
}
}
3. Future vs CompletableFuture
| 특징 | Future (기존) | CompletableFuture (권장) |
|---|---|---|
| 결과 확인 | get() 호출 시 결과가 나올 때까지 블로킹됨 |
비동기 콜백(thenAccept) 지원 |
| 예외 처리 | 내부에서 직접 처리해야 함 | exceptionally() 등 전용 메서드 제공 |
| 작업 조합 | 여러 Future를 묶기 어려움 | thenCombine, allOf 등으로 조합 가능 |
4. 주의할 점
- Thread Pool 설정:
@Async를 사용할 때는 기본 설정 대신 커스텀ThreadPoolTaskExecutor를 설정하는 것이 운영 환경에서 안전합니다. - get()의 함정:
future.get()을 호출하는 순간, 결과가 나올 때까지 현재 스레드는 멈춥니다. 가급적 콜백 방식을 사용하는 것이 좋습니다. - Self-invocation: 같은 클래스 내에서
@Async메서드를 호출하면 프록시 패턴 특성상 비동기로 동작하지 않습니다.