Java 프로그래밍을 하면서 http 외부 통신을 위해서 사용하는 대표적인 방법은
RestTemplat과 Webclient가 있다. 둘 사이에는 동기인지 비동기인지, 멀티스레드인지 아닌지 등의 차이점이 있는데 최근 추세는 Webclient를 더 많이 사용하는것으로 알고 있다. 따라서 금일 포스팅은 Webclient가 무었인지 에 대한 설명을 하고자 한다.
Webclient 를 사용하기 위해서는 webflux 의존성이 필요하다.
implementation 'org.springframework.boot:spring-boot-starter-webflux'
Webclient는 create() 통해서 Static 하게 바로 호출을 해서 사용하는 방법을 사용하거나 builder를 통해서 설정들을 customization 하고 해당하는 Bean을 만들어서 재활용해서 사용하는등의 두 가지 방법 이있다.
// 기본 Option없이 간단하게 생성하여 사용
WebClient.create("http://localhost:8080").get().uri("/test").retrieve()
// 아래와 같이 다양한 설정들을 추가적으로 만들어 줄 수 있다.
WebClient client = WebClient.builder()
.baseUrl("http://localhost:8080")
.defaultCookie("cookieKey", "cookieValue")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultUriVariables(Collections.singletonMap("url", "http://localhost:8080"))
.build();
webclient는 한번 build 하면 상태를 변경 할 수 없다
HttpClient httpClient = HttpClient.create().secure(sslSpec -> ...);
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
Resources
위와 같이 Http 통신에 대한 추가적인 설정을 HttpClient 클레스를 만들어서 넣어주면서 Webclient 에 설정해줄 수 있다.
Connection Pool은 클라이언트와 서버간에 연결을 맺어 놓은 상태(3 Way HandShake 완료상태)를 여러개 유지 하고 필요할때 하나씩 사용하고 반납하는 형태를 말한다. 이렇게 함으로 연결/연결해제에 필요한 HandShake를 하지 않고 더 빠르게 데이터를 주고 받을 수 있다.
ConnectionProvider은 이러한 pool을 생성하기 위한 설정을 하는 클래스 이다.
ConnectionProvider provider = ConnectionProvider.builder("custom-provider")
.maxConnections(100)
.maxIdleTime(Duration.ofSeconds(58))
.maxLifeTime(Duration.ofSeconds(58))
.pendingAcquireTimeout(Duration.ofMillis(5000))
.pendingAcquireMaxCount(-1)
.evictInBackground(Duration.ofSeconds(30))
.lifo()
.metrics(true)
.build();
webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create(provider)
.metrics(true, "custom-api")
.tcpConfiguration(tcpClient -> tcpClient
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
.doOnConnected(connection -> connection
.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS))
.addHandlerLast(new WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS))
)
)
))
.build();
별도의 설정을 하지 않으면 기본 값을 가지고 webclient 가 build 된다.
파라미터
webclinet 에서 응답 값을 받아오는 것에는 두가지 방법이 있다.
Mono<Person> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Person.class);
에러 처리를 하기 위해서는 아래와 같이 onStatus를 활용해서 응답 코드 별로 exception 처리를 할 수 있다.
Mono<Person> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus(HttpStatus::is4xxClientError, response -> ...)
응답을 받아서 처리하는 부분은 아래와 같이 두가지 방법으로 처리할 수 있다.
// case 1
Mono<ResponseEntity<Person>> entityMono = client.get()
.uri("/persons/1")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.toEntity(Person.class);
// case 2
Mono<Person> entityMono = client.get()
.uri("/persons/1")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Person.class);
호출 하단부에 block()를 입력하면 동기로 호출이 동작하게된다.