0Pricing
Java Academy · 강의

비동기 요청

sendAsync와 CompletableFuture를 알아봅니다

비동기 요청은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

비동기 요청

모든 HTTP 호출에서 차단하면 스레드가 낭비됩니다. JDK HTTP 클라이언트는 sendAsync를 제공하며, 이 메서드는 즉시 CompletableFuture를 반환하고 백그라운드 실행기에서 작업을 수행합니다.

이를 통해 많은 요청을 동시에 시작하고 스레드를 직접 관리하지 않으면서 결과를 조합할 수 있습니다.

send와 sendAsync 비교

두 메서드는 반환하는 값이 다릅니다.

  • send는 HttpResponse<T>를 반환하고 차단하며, IOException과 InterruptedException을 선언합니다.
  • sendAsync는 CompletableFuture<HttpResponse<T>>를 반환하고 절대 차단하지 않으며, 오류는 퓨처를 통해 나타납니다.

기본 sendAsync 호출

sendAsync는 send와 동일한 요청과 BodyHandler를 받습니다. 반환된 퓨처는 응답이 준비되면 완료됩니다.

import java.net.URI;
import java.net.http.*;
import java.util.concurrent.CompletableFuture;

public class Main {
    public static void main(String[] args) {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://example.com"))
            .build();
        CompletableFuture<HttpResponse<String>> future =
            client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("Request sent, future created");
    }
}

thenApply로 변환하기

결과가 도착하면 thenApply를 사용해 결과를 변환합니다. 예를 들어 응답에서 본문을 추출할 수 있습니다.

이 작업은 이전 단계가 완료된 스레드에서 동기적으로 실행됩니다.

import java.util.concurrent.CompletableFuture;

public class Main {
    public static void main(String[] args) throws Exception {
        CompletableFuture<String> future =
            CompletableFuture.completedFuture("OK 200")
                .thenApply(body -> "Length=" + body.length());
        System.out.println(future.get());
    }
}

thenAccept로 소비하기

부수 효과만 필요하고 반환 값은 필요하지 않을 때는 thenAccept를 사용합니다. 이 메서드는 Consumer를 받고 CompletableFuture<Void>를 반환합니다.

import java.util.concurrent.CompletableFuture;

public class Main {
    public static void main(String[] args) throws Exception {
        CompletableFuture<Void> done =
            CompletableFuture.completedFuture("payload")
                .thenAccept(b -> System.out.println("Received: " + b));
        done.get();
    }
}

join으로 차단하기

main에서 비동기 결과를 기다리려면 join()(검사되지 않음) 또는 get()(검사됨)을 호출합니다. 실제 서버에서는 차단하는 대신 단계를 연결합니다.

join()은 실패를 검사되지 않은 CompletionException으로 감쌉니다.

import java.util.concurrent.CompletableFuture;

public class Main {
    public static void main(String[] args) {
        CompletableFuture<Integer> f =
            CompletableFuture.supplyAsync(() -> 21).thenApply(n -> n * 2);
        int result = f.join();
        System.out.println("Result: " + result);
    }
}

exceptionally로 오류 처리하기

exceptionally는 단계가 실패했을 때 대체 값을 제공하여 처리 흐름을 복구합니다.

이렇게 하면 네트워크 오류가 발생해도 비동기 HTTP 흐름이 중단되지 않습니다.

import java.util.concurrent.CompletableFuture;

public class Main {
    public static void main(String[] args) {
        CompletableFuture<String> f = CompletableFuture
            .<String>supplyAsync(() -> { throw new RuntimeException("boom"); })
            .exceptionally(ex -> "fallback: " + ex.getMessage());
        System.out.println(f.join());
    }
}

두 요청 결합하기

thenCombine은 서로 독립적인 두 퓨처를 기다린 후 결과를 병합합니다. 비동기 HTTP에서는 두 엔드포인트를 병렬로 조회하고 본문을 결합할 수 있습니다.

import java.util.concurrent.CompletableFuture;

public class Main {
    public static void main(String[] args) {
        CompletableFuture<String> a = CompletableFuture.supplyAsync(() -> "Hello");
        CompletableFuture<String> b = CompletableFuture.supplyAsync(() -> "World");
        String combined = a.thenCombine(b, (x, y) -> x + " " + y).join();
        System.out.println(combined);
    }
}

여러 요청을 병렬로 시작하기

요청 묶음을 실행하려면 각 퓨처를 목록에 모은 다음 CompletableFuture.allOf(...)를 사용해 모든 요청을 기다립니다.

allOf는 CompletableFuture<Void>를 반환하므로 이후에 개별 결과를 읽어야 합니다.

import java.util.*;
import java.util.concurrent.CompletableFuture;

public class Main {
    public static void main(String[] args) {
        List<CompletableFuture<Integer>> futures = new ArrayList<>();
        for (int i = 1; i <= 3; i++) {
            int n = i;
            futures.add(CompletableFuture.supplyAsync(() -> n * n));
        }
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        for (var f : futures) System.out.println(f.join());
    }
}

비동기 패턴 요약

일반적인 비동기 HTTP 처리 흐름은 다음과 같습니다.

  • client.sendAsync(req, BodyHandlers.ofString())
  • 본문을 가져오려면 .thenApply(HttpResponse::body)
  • 본문을 소비하려면 .thenAccept(System.out::println)
  • 복구하려면 .exceptionally(ex -> ...)

네트워크를 기다리느라 스레드가 차단되는 일은 없습니다.

비동기를 선택할 시점

동시에 많은 호출을 수행하거나 여러 서비스로 분산 호출하거나 반응형/비차단 서버 내부에서 실행할 때는 sendAsync를 선택합니다. 차단해도 괜찮은 간단한 스크립트와 일회성 호출에는 send를 선택합니다.

빠른 확인

비동기 요청에 대한 이해도를 확인해 보십시오.

복습

비차단 HTTP 호출을 수행하는 방법을 배웠습니다.

  • sendAsync는 차단하는 대신 CompletableFuture를 반환합니다.
  • thenApply / thenAccept는 결과를 변환하고 소비합니다.
  • exceptionally는 실패 시 대체 값을 제공합니다.
  • thenCombine은 두 병렬 호출의 결과를 병합하고, allOf는 요청 묶음이 완료될 때까지 기다립니다.
  • 비동기 방식은 높은 동시성이 필요한 반응형 서버에 적합하고, 동기 방식은 간단한 스크립트에 적합합니다.

자주 묻는 질문

“비동기 요청” 강의는 무료인가요?

네 — “비동기 요청” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“비동기 요청”에서 뭘 배우나요?

sendAsync와 CompletableFuture를 알아봅니다 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Java Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“비동기 요청” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. java.net.http.HttpClient
  2. 비동기 요청
  3. 요청 본문과 헤더
  4. 응답 처리
← Java Academy(으)로 돌아가기