@Async 반환 타입은 왜 void, future, completableFuture만 가능할까?

헨도·2025년 11월 26일

SpringBoot

목록 보기
38/41
post-thumbnail

궁금

비동기 메서드에 관해 공부하다가 스프링에서 사용하는 @Async 어노테이션이 붙은 비동기 메서드의 반환 타입이 3가지로 구성이 된다는 것에 왜 그런지 궁금했다.

반환 타입

  • void
  • future
  • completableFuture

결론은 Spring의 비동기 동작 원리가 AOP 프록시이기 때문이다


@Async는 사실 "프록시가 대신 실행하는 구조"

@Async를 메서드에 붙이면 스프링은 그 클래스의 프록시 객체를 생성한다.
즉, 우리가 호출하는 것은 실제 객체가 아니라 프록시 객체이다.

우리 코드 -> 프록시 -> (비동기 스레드풀로 작업 전달) -> 메서드 실행

여기서 문제점이 있는데, "메서드는 비동기로 실행되는데, 호출자는 "바로 리턴 값"을 받을 수 없는 것이다.
-> 실제 메서드는 다른 스레드에서 아직 실행 중이기 때문이다.

프록시는 결과를 즉시 리턴해야 한다

AOP 프록시는 우리 코드의 메서드 호출을 가로채고 다음 중 하나를 수행해야 합니다.

1. 바로 결과를 리턴
2. future 같은 비동기 핸들러를 리턴
3. 아예 결과를 리턴하지 않거나 -> void

하지만 프록시는 스레드풀에서 비동기 실행이 끝날 때까지 기다리지 않습니다.
-> 그럼 비동기 의미가 없어지기 때문

main thread -> Async Proxy -> 스레드풀에 작업 던지고 빠져나옴

따라서, 아직 실행되지 않은 메서드의 실제 값(String, int 등)을 프록시가 즉시 리턴할 수 없기 때문이다.

그래서 프록시가 리턴할 수 있는 타입은 정해져 있다

1. void

  • "난 결과 필요없어"
  • 프록시는 그냥 null 반환하고 끝
  • 작업은 스레드풀에서 따로 실행

2. Future<T>

  • 미래에 완성될 값을 감싸는 표준 타입
  • 프록시는 "지금은 없지만 나중에 생길 값"이라는 Future를 만들어 반환할 수 있음

3. CompletableFuture<T>

  • 비동기 연산을 조합할 수 있는 더 현대적인 Future
  • 스프링이 값이 준비되면 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)만 허용한다.

profile
Junior Backend Developer

0개의 댓글