
비동기 메서드에 관해 공부하다가 스프링에서 사용하는 @Async 어노테이션이 붙은 비동기 메서드의 반환 타입이 3가지로 구성이 된다는 것에 왜 그런지 궁금했다.
voidfuturecompletableFuture결론은 Spring의 비동기 동작 원리가 AOP 프록시이기 때문이다
@Async를 메서드에 붙이면 스프링은 그 클래스의 프록시 객체를 생성한다.
즉, 우리가 호출하는 것은 실제 객체가 아니라 프록시 객체이다.
우리 코드 -> 프록시 -> (비동기 스레드풀로 작업 전달) -> 메서드 실행
여기서 문제점이 있는데, "메서드는 비동기로 실행되는데, 호출자는 "바로 리턴 값"을 받을 수 없는 것이다.
-> 실제 메서드는 다른 스레드에서 아직 실행 중이기 때문이다.
AOP 프록시는 우리 코드의 메서드 호출을 가로채고 다음 중 하나를 수행해야 합니다.
1. 바로 결과를 리턴
2. future 같은 비동기 핸들러를 리턴
3. 아예 결과를 리턴하지 않거나 -> void
하지만 프록시는 스레드풀에서 비동기 실행이 끝날 때까지 기다리지 않습니다.
-> 그럼 비동기 의미가 없어지기 때문
main thread -> Async Proxy -> 스레드풀에 작업 던지고 빠져나옴
따라서, 아직 실행되지 않은 메서드의 실제 값(String, int 등)을 프록시가 즉시 리턴할 수 없기 때문이다.
voidnull 반환하고 끝Future<T>CompletableFuture<T>complete() 해줄 수 있음@Async
public String test() {
return "OK";
}
이걸 프록시 입장에서 보면:
프록시가 test()를 호출 받음
-> 스레드풀에 비동기 작업을 맡김 (아직 실행 안 됨)
-> 그런데 test()는 반드시 String을 즉시 리턴해야 함
-> 근데 결과는 아직 없음
-> 모순
즉...프록시는 아직 계산되지 않은 값을 리턴할 방법이 없다.
그래서 스프링은 아예 이런 반환 타입을 금지해버린 것이다.
(1) 호출
main thread ------------> Async Proxy
|
| (2) 스레드풀에 작업 맡김 (실제 메서드 실행은 아직!)
v
Executor (별도 스레드)
|
v
실제 @Async 메서드 실행
(3) 프록시는 즉시 반환해야 함 → 가능 타입: void / Future / CompletableFuture
@Async 반환 타입 제한 이유
| 이유 | 설명 |
|---|---|
| 프록시가 즉시 반환해야 하기 때문 | 비동기 작업 완료를 기다릴 수 없음 |
| 실제 메서드는 다른 스레드에서 나중에 실행됨 | 즉시 반환할 실제 값(String 등)이 없음 |
| Future/CompletableFuture는 "미래의 결과"를 표현 | 비동기 결과를 감싸기 좋은 타입 |
| void는 아예 결과가 필요 없음 | 백그라운드 작업 전용 |
@Async는 프록시가 메서드를 가로채서 스레드풀에 던지고 바로 빠져나오기 때문에, 즉시 리턴 가능한 타입(void, Future, CompletableFuture)만 허용한다.