본문 바로가기

Reactive Programming

240815 WebClient

1️⃣ WebClient

  • Spring 5부터 지원하는, Spring WebFlux에서 제공하는 비동기식 HTTP 클라이언트
  • 주로 서버 측에서 다른 API나 웹 서비스와 통신하기 위해 사용됩니다.
  • 브라우저에서 동작하는 클라이언트 애플리케이션을 직접 개발하기 위한 도구는 아닙니다.
  • 서버 측에서 HTTP 요청을 보낼 수 있게 해주는 역할을 합니다.
  • Non-Blocking, Blocking HTTP 요청을 모두 지원
    • Spring MVC의 RestTemplate (Blocking)을 대체할 수 있습니다.

WebClient가 필요한 이유

WebClient는 서버 사이드 애플리케이션에서 다른 서비스와 통신할 때 유용합니다.

  1. 마이크로서비스 간 통신:
    • 마이크로서비스 아키텍처에서 서비스 간 통신이 필요할 때, WebClient를 사용해 다른 서비스의 API에 비동기 요청을 보낼 수 있습니다.
  2. 외부 API 호출:
    • 외부의 RESTful API나 웹 서비스를 호출해야 할 때, WebClient를 통해 비동기식으로 데이터를 요청하고 처리할 수 있습니다.
    • 예를 들어, 날씨 API, 금융 정보 API, 소셜 미디어 API 등을 호출할 때 사용할 수 있습니다.

참고 : 비동기와 논블로킹

비동기 : 작업을 처리하는 방식을 설명. 작업의 완료 시점이 현재 흐름에 의존하지 않는다.

논블로킹 : 자원의 동작 방식을 설명. 현재 자원이 대기 상태에 들어가지 않고(블로킹x), 다른 작업을 계속 수행할 수 있는 상태를 유지한다.

2️⃣ 코드 예시 : 도서 정보 요청

| 참고 : 스프링으로 시작하는 리액티브 프로그래밍(황정식)

POST 요청

@Slf4j
@Configuration
public class WebClientExample01 {
    @Bean
    public ApplicationRunner examplesWebClient() {

        return (ApplicationArguments arguments) -> {
            exampleWebClient01();
        };
    }

    private void exampleWebClient01() {
        BookDto.Post requestBody = new BookDto.Post("Java 중급",
                "Intermediate Java",
                "Java 중급 프로그래밍 마스터",
                "Kevin1", "222-22-2222-222-2",
                "2022-03-22");

        WebClient webClient = WebClient.create();
        Mono<ResponseEntity<Void>> response =
                webClient
                        .post()
                        .uri("http://localhost:8080/v10/books")
                        .bodyValue(requestBody)
                        .retrieve()
                        .toEntity(Void.class);

        response.subscribe(res -> {
           log.info("response status: {}", res.getStatusCode());
           log.info("Header Location: {}", res.getHeaders().get("Location"));
        });
    }
}

 

PATCH 요청

@Slf4j
@Configuration
public class WebClientExample01 {
    @Bean
    public ApplicationRunner examplesWebClient() {

        return (ApplicationArguments arguments) -> {
            exampleWebClient02();
        };
    }
    
    private void exampleWebClient02() {
        BookDto.Patch requestBody =
                new BookDto.Patch.PatchBuilder().titleKorean("Java 고급")
                .titleEnglish("Advanced Java")
                .description("Java 고급 프로그래밍 마스터")
                .author("Tom")
                .build();

        WebClient webClient = WebClient.create("http://localhost:8080");
        Mono<BookDto.Response> response =
                webClient
                        .patch()
                        .uri("http://localhost:8080/v10/books/{book-id}", 20)
                        .bodyValue(requestBody)
                        .retrieve()
                        .bodyToMono(BookDto.Response.class);

        response.subscribe(book -> {
            log.info("bookId: {}", book.getBookId());
            log.info("titleKorean: {}", book.getTitleKorean());
            log.info("titleEnglish: {}", book.getTitleEnglish());
            log.info("description: {}", book.getDescription());
            log.info("author: {}", book.getAuthor());
        });
    }
}

GET 요청

@Slf4j
@Configuration
public class WebClientExample01 {
    @Bean
    public ApplicationRunner examplesWebClient() {

        return (ApplicationArguments arguments) -> {
            exampleWebClient03();
            exampleWebClient04();
        };
    }

    private void exampleWebClient03() {
        Mono<BookDto.Response> response =
                WebClient
                        .create("http://localhost:8080")
                        .get()
                        .uri(uriBuilder -> uriBuilder
                                .path("/v10/books/{book-id}")
                                .build(21))
                                .retrieve()
                                .bodyToMono(BookDto.Response.class);

        response.subscribe(book -> {
            log.info("bookId: {}", book.getBookId());
            log.info("titleKorean: {}", book.getTitleKorean());
            log.info("titleEnglish: {}", book.getTitleEnglish());
            log.info("description: {}", book.getDescription());
            log.info("author: {}", book.getAuthor());
        });
    }

    private void exampleWebClient04() {
        Flux<BookDto.Response> response =
                WebClient
                        .create("http://localhost:8080")
                        .get()
                        .uri(uriBuilder -> uriBuilder
                                .path("/v10/books")
                                .queryParam("page", "1")
                                .queryParam("size", "10")
                                .build())
                        .retrieve()
                        .bodyToFlux(BookDto.Response.class);

        response
                .map(book -> book.getTitleKorean())
                .subscribe(bookName -> log.info("book name: {}", bookName));
    }
}