1. 개요

당사 서비스는 종종 다른 REST 서비스와 통신하여 정보를 가져옵니다.

Spring 5부터 우리는 WebClient 를 사용하여 이러한 요청을 반응적이고 차단하지 않는 방식으로 수행합니다. WebClient 는 Project Reactor 위에 구축된 새로운 WebFlux 프레임워크의 일부입니다 . 유창하고 반응적인 API가 있으며 기본 구현에서 HTTP 프로토콜을 사용합니다.

웹 요청을 하면 데이터가 JSON으로 반환되는 경우가 많습니다. WebClient 는 이것을 우리를 위해 변환할 수 있습니다.

이 기사에서는 WebClient 를 사용하여 JSON 배열 Object 의 Java 배열 , POJO 의 배열 및 POJO List 으로 변환하는 방법을 알아봅니다 .

2. 의존성

WebClient 를 사용하려면 pom.xml 에 몇 가지 의존성을 추가해야 합니다 .

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
    <groupId>org.projectreactor</groupId>
    <artifactId>reactor-spring</artifactId>
    <version>1.0.1.RELEASE</version>
</dependency>

3. JSON, POJO 및 서비스

좋아하는 책이 있는 독자 List을 JSON 배열로 반환 하는 엔드포인트 http://localhost:8080/readers 부터 시작하겠습니다.

[{
    "id": 1,
    "name": "reader1",
    "favouriteBook": { 
        "author": "Milan Kundera",
        "title": "The Unbearable Lightness of Being"
    }
}, {
    "id": 2,
    "name": "reader2"
    "favouriteBook": { 
        "author": "Douglas Adams",
        "title": "The Hitchhiker's Guide to the Galaxy"
    }
}]

데이터를 처리 하려면 해당하는 ReaderBook 클래스가 필요합니다.

public class Reader {
    private int id;
    private String name;
    private Book favouriteBook;

    // getters and setters..
}
public class Book {
    private final String author;
    private final String title;

   // getters and setters..
}

인터페이스 구현을 위해 WebClient 를 의존성으로 사용하여 ReaderConsumerServiceImpl 을 작성합니다.

public class ReaderConsumerServiceImpl implements ReaderConsumerService {

    private final WebClient webClient;

    public ReaderConsumerServiceImpl(WebClient webclient) {
        this.webclient = webclient;
    }

    // ...
}

4. JSON 개체 List 매핑

REST 요청에서 JSON 배열을 수신하면 여러 가지 방법으로 Java 컬렉션으로 변환할 수 있습니다. 다양한 옵션을 살펴보고 반환된 데이터를 처리하는 것이 얼마나 쉬운지 살펴보겠습니다. 독자들이 가장 좋아하는 책을 추출해 보겠습니다.

4.1. 모노 VS 플럭스

Project Reactor  는 두 가지 Publisher 구현인 MonoFlux 를 도입 했습니다.

Flux<T> 는 0에서 많거나 잠재적으로 무한한 결과를 처리해야 할 때 유용합니다. Twitter 피드를 예로 생각할 수 있습니다.

사용 사례에서와 같이 결과가 한 번에 모두 반환된다는 것을 알고 있으면 Mono<T> 를 사용할 수 있습니다 .

4.2. 개체 배열  이 있는 WebClient

먼저  WebClient.get 으로 GET  호출을 만들고 Object[] 유형 의 Mono 를  사용 하여 응답을 수집합니다.

Mono<Object[]> response = webClient.get()
  .accept(MediaType.APPLICATION_JSON)
  .retrieve()
  .bodyToMono(Object[].class).log();

다음으로 본문을 Object 배열로 추출해 보겠습니다 .

Object[] objects = response.block();

여기서 실제 개체 는 데이터를 포함하는 랜덤의 구조입니다. 이것을 Reader 개체 의 배열로 변환해 보겠습니다 .

이를 위해서는 ObjectMapper 가 필요합니다 .

ObjectMapper mapper = new ObjectMapper();

여기서는 인라인으로 선언했지만 일반적으로 클래스의 개인용 정적 최종 멤버로 수행됩니다.

마지막으로 독자들이 가장 좋아하는 책을 추출하여 List으로 모을 준비가 되었습니다.

return Arrays.stream(objects)
  .map(object -> mapper.convertValue(object, Reader.class))
  .map(Reader::getFavouriteBook)
  .collect(Collectors.toList());

대상 유형으로 Object 를 생성 하도록 Jackson 디시리얼라이저 에 요청하면 실제로 JSON을 일련의 LinkedHashMap 객체 로 디시리얼라이즈합니다 . convertValue 를 사용한 사후 처리 는 비효율적입니다. 역직렬화 중에 원하는 유형을 Jackson에 제공하면 이를 피할 수 있습니다.

4.3. 리더 배열 이 있는 WebClient

WebClientObject[ ] 대신 Reader[] 를 제공할 수 있습니다 .

Mono<Reader[]> response = webClient.get()
  .accept(MediaType.APPLICATION_JSON)
  .retrieve()
  .bodyToMono(Reader[].class).log();
Reader[] readers = response.block();
return Arrays.stream(readers)
  .map(Reader:getFavouriteBook)
  .collect(Collectors.toList());

여기에서 ObjectMapper.convertValue 가 더 이상 필요하지 않음을 알 수 있습니다 . 그러나 Java Stream API를 사용하고 코드가 List 와 함께 작동하려면 여전히 추가 변환을 수행해야 합니다 .

4.4. 독자 List 이 있는 WebClient

Jackson이 배열 대신 독자 List 을 생성하도록 하려면 생성하려는 List 을 설명해야 합니다 . 이를 위해 익명의 내부 클래스 에서 생성된 ParameterizedTypeReference 를 메서드에 제공합니다.

Mono<List<Reader>> response = webClient.get()
  .accept(MediaType.APPLICATION_JSON)
  .retrieve()
  .bodyToMono(new ParameterizedTypeReference<List<Reader>>() {});
List<Reader> readers = response.block();

return readers.stream()
  .map(Reader::getFavouriteBook)
  .collect(Collectors.toList());

이것은 우리가 작업할 수 있는 List 을 제공합니다.

ParameterizedTypeReference 를 사용해야 하는 이유 에 대해 자세히 살펴보겠습니다 .

Spring의 WebClient는 유형 정보가 런타임에 사용 가능할 때 JSON을 Reader.class 로 쉽게 역직렬화할 수 있습니다.

그러나 제네릭 을 사용하면 List<Reader>.class 를 사용하려고 하면 유형 삭제 가 발생합니다 . 따라서 Jackson은 제네릭의 유형 매개변수를 결정할 수 없습니다.

ParameterizedTypeReference 를 사용 하면 이 문제를 해결할 수 있습니다. 이를 익명 내부 클래스로 인스턴스화하는 것은 제네릭 클래스의 하위 클래스가 유형 삭제 대상이 아니며 리플렉션을 통해 사용할 수 있는 컴파일 타임 유형 정보를 포함한다는 사실을 이용합니다.

5. 결론

이 사용방법(예제)에서는 WebClient 를 사용하여 JSON 개체를 처리하는 세 가지 방법을 살펴보았습니다 . 우리는 Object 배열 유형 과 자체 사용자 정의 클래스를 지정하는 방법을 보았습니다.

그런 다음 ParameterizedTypeReference 를 사용하여 List 을 생성하기 위해 정보 유형을 제공하는 방법을 배웠습니다 .

항상 그렇듯이 이 기사의 코드는 GitHub에서 사용할 수 있습니다 .

HTTPClient footer