[Spring boot] web client

YJ KIM·2022년 8월 6일
1

외부 API를 통해 여러 정보를 받아와야 한다.
이때 Rest Template가 아닌 Web client를 사용하여 이를 수행하였다.

이전 프로젝트에서는 rest template을 사용하였지만 deprecated 된다고 하여 web client를 사용하였다.

📍web client는 non-blocking, 즉 동기 방식으로 수행되지만 현재 프로젝트에서는 이를 비동기로 수행하였다. Web client의 사용 이유는 오직 rest template의 deprecated issue이다.

목차
1. web client 특징
2. 사용한 코드와 메소드 설명
3. 발생한 문제와 해결방안

1. web client란?

  • HTTP Request를 수행하는 Client
  • Spring WebFlux에 포함되어 있다.
  • 스레드나 동시성을 처리할 필요 없는 Non-blocking

📌 여기서 Spring WebFlux란?
Spring 5에서 새롭게 추가된 모듈로 클라이언트, 서버에서 reactive 스타일의 어플리케이션 개발을 도와주는 모듈이다.

2. 사용 예시와 method

목적: 외부 API 사용하여 여러 장소 정보를 받아오기 위함

2-1. WebClient 생성

private final WebClient webClient;
private final String BASE_URL="https://dapi.kakao.com";

    public ShopServiceImpl(WebClient.Builder webclientBuilder, @Value("${external.api.key}")String key) {

        DefaultUriBuilderFactory factory=new DefaultUriBuilderFactory(BASE_URL);
        factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.TEMPLATE_AND_VALUES);

        HttpClient httpClient = HttpClient.create()
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                .responseTimeout(Duration.ofMillis(5000))
                .doOnConnected(conn ->
                        conn.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS)));

        this.webClient = webclientBuilder.uriBuilderFactory(factory).baseUrl(BASE_URL)
                .defaultHeader("Authorization",key)
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .build();

    }
  • DefaultUriBuilderFactory를 이용하여 uri 인코딩 설정
  • HttpClient 이용하여 timeout 설정

저는 따로 @Configuration @Bean을 통하여 web client를 bean으로 생성하지 않고 생성자 주입 방식으로 생성하였습니다.

🦷 WebClient.Builder를 DI하는 방식으로 수행합니다.

이렇게 수행한 이유는 제일 직관적이고 편리한 것 같아서 + 공식문서에서 이렇게 하길래 ...
-> Webclient.create()를 수행하면 세부 설정이 불가함.

Webclient로 REST 서비스 호출하기
해당 공식문서를 따라가면 좋다.

2-2. Response 받기

Response res= webClient.get().uri(uriBuilder ->
                        uriBuilder.path("해당하는 uri")
                                .queryParam("query",query)
                                .build()
                ).retrieve().bodyToMono(Response.class).block();

Http get 방식을 사용하여 Response를 받았다.

  • retrive()
    response body를 받아 디코딩하는 메소드

  • bodyToMono(Response.class)
    header 등 여러 정보 필요 없이 body만을 받아 특정 객체로 변환하는 method
    method 인자에 반환받을 객체.class를 넣어줘야 한다.

  • block()
    non-blocking이 아닌 block 형식으로 동기적으로 받기 위한 메소드

📌 block 메소드를 사용하였기 때문에 이는 비동기가 아닌 동기로 수행합니다.

3. 발생한 문제와 해결방안

2번에 작성한 코드가 이미 이를 고친 것이긴 하지만,
원래는 api key를 @value로 주입할 때 생성자가 아닌 멤버 변수로 주입하였다.

하지만 web client 생성 시에 defaultheader에 authorization을 넣어줘야 했고 @value 어노테이션은 이보다 뒤에 수행되기 때문에 key가 null값으로 들어가게 되었다.

그래서 생성자에서 @value annotation 인자로 받아오게 되었다.

4. 느낀점, 참고한 포스트

spring이나 spring boot를 이론을 파면서 공부한 적이 많이 없어서 그런지 어려움이 많은 것 같다. Web Flux나 spring framework 위에서의 동기/비동기에 대한 개념이 없는 걸 알게 되었다.

단지 rest template이 deprecated 된다는 이유로 web client를 사용하기는 하였지만 스트리밍 같은 서비스에 사용하면 정말 효과적일 것 같다.


📂 참고한 포스트

profile
하루하루는 성실하게 인생 전체는 되는대로

0개의 댓글