Spring WebClient
스프링으로 서버를 띄우는 경우가 대부분 이지만 다른 서버로 요청을 보내 데이터를 가져오는 클라이언트의 역할을 하는 부분이 필요할 수 있다
Spring에서는 WebClient를 통해 클라이언트 요청을 보낼 수 있다
WebClient
스프링 5.0버전에 추가된 Non-Blocking 방식의 HTTP 클라이언트이다
기존의 Multi Threading Blocking 방식의 RestTemplate
을 대체한다. (deprecated)
설정
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux'
}
WebClientConfig
- WebClient 객체를 필요할 때 마다 생성할 수도 있지만 빈으로 등록해서 사용할 수 있다
-
여러 설정을 한 객체를 빈으로 등록해 재활용 한다
-
baseUrl(url)
: 요청을 보낼 URL 설정 -
defaultCookie(key, value)
: 쿠키 설정 -
defaultHeader(key, value)
: 헤더 설정 - MaxInMemorySize : 어플리케이션 메모리 문제를 피하기 위해 256KB로 기본 설정된 메모리 크기를 변경할 수 있다
- Timeout : Connetion, Read, Write 등의 타임아웃을 설정할 수 있다
- Filter : Reqeust, Response 데이터를 조작하거나 추가작업을 필터를 통해 할 수 있다
@Slf4j
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient() {
// 메모리 설정
ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(1024 * 1024 * 50))
.build();
// Timeout 설정
HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.doOnConnected(conn -> conn
.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS))
.addHandlerLast(new WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS)))
.headers(header -> header.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
);
return WebClient.builder()
.exchangeStrategies(exchangeStrategies)
.clientConnector(new ReactorClientHttpConnector(httpClient))
.filter(logRequest())
.filter(logResponse())
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
}
// Request Log Filter
private static ExchangeFilterFunction logRequest() {
return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
log.info("Client Request : [{}, {}]", clientRequest.method(), clientRequest.url());
return Mono.just(clientRequest);
});
}
// Response Log Filter
private static ExchangeFilterFunction logResponse() {
return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
log.info("Client Response : {}", clientResponse.statusCode());
return Mono.just(clientResponse);
});
}
}
사용
리액터
- Mono : 단일 객체를 반환
- Flux : 여러 객체(Collection)를 반환
RequestClient
-
mutate()
: immutable한 빈 객체의 설정을 변경해서 사용한다 -
get()
,post()
: HTTP Method -
bodyValue(object)
: Body에 요청 보낼 데이터 값을 담는다 -
onStatus()
: HTTP 결과 코드에 따라 Exception 처리 -
toEntity(object)
: 결과를 status, headers, body를 갖는ResponseEntity
로 반환 -
bodyToMono(object)
,bodyToFlux(object)
: 결과 데이터 맵핑
@Slf4j
@Component
@RequiredArgsConstructor
public class RequestClient {
private final WebClient webClient;
public RequestResponse request(String url, RequestRequest request) {
return webClient.mutate()
.baseUrl(url) // URL 설정
.build()
.post() // POST
.accept(MediaType.APPLICATION_JSON)
.bodyValue(request) // Body
.retrieve() // 결과 획득
.onStatus(status -> status.is4xxClientError() || status.is5xxServerError(),
response -> response.bodyToMono(String.class)
.map(body -> new RumtimeException(response.statusCode().toString())))
.bodyToMono(StockSyncResponse.class) // 결과데이터 맵핑
.blockOptional();
}
}
Controller
@RestController
@RequiredArgsConstructor
@RequestMapping("/client")
public class clientController() {
private final RequestClient requestClient;
@PostMapping
public RequestResponse request(String url, RequestRequest request) {
return requestClient.request(url, reuquest);
}
}