자바에서 비동기(Asynchronous) API 호출을 구현하는 가장 일반적이고 현대적인 방법은 CompletableFuture 클래스를 사용하는 것입니다. 이는 Java 8부터 도입되었으며 비동기 프로그래밍을 위한 강력하고 유연한 도구를 제공합니다.
💻 CompletableFuture를 이용한 비동기 API 호출 예제
이 예제에서는 가상의 외부 API 호출을 시뮬레이션하고, CompletableFuture.supplyAsync()를 사용하여 해당 작업을 별도의 스레드에서 비동기적으로 실행합니다.
1. 코드 예제
Java
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class AsyncApiCallExample {
// 가상의 API 호출 메서드 (시간이 오래 걸리는 작업이라고 가정)
public static String callExternalApi(String query) {
System.out.println("🔥 API 호출 시작: " + query + " (Thread: " + Thread.currentThread().getName() + ")");
try {
// API 응답 대기 시뮬레이션
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("✅ API 호출 완료: " + query + " (Thread: " + Thread.currentThread().getName() + ")");
return "API 응답 데이터 for " + query;
}
public static void main(String[] args) {
System.out.println("🚀 메인 프로그램 시작 (Thread: " + Thread.currentThread().getName() + ")");
// 1. CompletableFuture를 사용하여 비동기 호출 시작
// supplyAsync는 별도의 스레드에서 작업을 실행하고 결과를 CompletableFuture에 담아 반환합니다.
CompletableFuture<String> futureResult = CompletableFuture.supplyAsync(() ->
callExternalApi("User Data Request")
);
// 2. 비동기 작업이 진행되는 동안 다른 작업 수행 가능 (논블로킹)
System.out.println("⏳ API 호출 결과를 기다리지 않고 다른 작업 수행 중...");
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("✅ 다른 작업 완료.");
// 3. 비동기 작업이 완료된 후 후속 작업 정의 (콜백)
// thenAccept는 결과가 준비되면 실행될 작업을 정의합니다.
futureResult.thenAccept(result -> {
System.out.println("⬇️ CompletableFuture 콜백 실행 (Thread: " + Thread.currentThread().getName() + ")");
System.out.println("최종 결과 수신: " + result);
});
// 4. 결과가 준비될 때까지 메인 스레드 대기 (블로킹, 예제 실행을 위해 필요)
// 실제 애플리케이션에서는 `.get()`을 호출하는 대신 콜백(`.thenApply`, `.thenAccept` 등)을 주로 사용합니다.
try {
// 이 시점에서 메인 스레드는 futureResult가 완료될 때까지 기다립니다.
// *주의*: 실제 비동기의 이점을 살리려면 메인 스레드에서 바로 `.get()`을 호출하는 것은 피하는 것이 좋습니다.
futureResult.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
System.out.println("🏁 메인 프로그램 종료 (Thread: " + Thread.currentThread().getName() + ")");
}
}
2. 예제 실행 결과 (순서는 다를 수 있음)
🚀 메인 프로그램 시작 (Thread: main)
🔥 API 호출 시작: User Data Request (Thread: ForkJoinPool.commonPool-worker-1)
⏳ API 호출 결과를 기다리지 않고 다른 작업 수행 중...
✅ 다른 작업 완료.
✅ API 호출 완료: User Data Request (Thread: ForkJoinPool.commonPool-worker-1)
⬇️ CompletableFuture 콜백 실행 (Thread: ForkJoinPool.commonPool-worker-1)
최종 결과 수신: API 응답 데이터 for User Data Request
🏁 메인 프로그램 종료 (Thread: main)
✨ 핵심 개념 설명

CompletableFuture.supplyAsync(Supplier<T> supplier):Supplier에 정의된 작업을 비동기적으로 실행합니다. (주로 ForkJoinPool의 CommonPool을 사용)- 이 작업의 결과를 담을 수 있는
CompletableFuture객체를 즉시 반환합니다. 호출한 스레드는 블로킹 없이 다음 코드를 계속 실행합니다.
.thenAccept(Consumer<? super T> action):- 비동기 작업이 성공적으로 완료되었을 때 실행할 후속 작업(콜백)을 정의합니다.
- 이 작업은 결과를 소비(사용)만 하고 별도의 값을 반환하지 않을 때 사용됩니다.
.get():CompletableFuture의 결과가 나올 때까지 현재 스레드를 블로킹합니다.- 비동기 작업의 결과를 기다려야 할 때 사용되지만, 비동기의 이점을 살리려면 가능한 한 콜백 메서드(e.g.,
thenApply,thenAccept)를 사용하여 블로킹을 최소화하는 것이 좋습니다.
Spring Boot와 같은 프레임워크 환경에서는 RestTemplate 대신 **WebClient**를 사용하여 비동기/논블로킹 API 호출을 구현하거나, @Async 어노테이션을 활용하여 메서드를 비동기적으로 실행할 수도 있습니다.
이 영상은 Java에서 비동기 API 호출을 위해 CompletableFuture와 RxJava를 사용하는 방법을 비교하여 보여줍니다: Async API Calls in Java: CompletableFuture vs RxJava (3-Min Demo).