webclient
Spring WebFlux에서 제공하는 리액터 기반 비동기 형식으로 사용 가능한 API
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-webflux'
WebClientConfig
timeout, 버퍼 메모리(기본이 256KB), 기본 header를 설정해줬다.
webClient의 설정을 변경하고 싶으면 mutate를 사용해 복사해서 사용하면 된다.
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient(){
HttpClient httpClient = HttpClient.create().option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10 * 100)
.responseTimeout(Duration.ofMillis(60 * 1000L));
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(1*1024*1024)) //1M 설정, 기본 256KB
.defaultHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36")
.build();
}
}
WebClientRequester
매 요청 시마다 Webclient의 post 설정을 변경하면서 사용하는 것보다 하나의 Requester를 만들어 사용하는 것이 좋을 것 같았다. 또, response로 받는 class와 request로 들어가는 dto가 매번 변경되기 때문에 제네릭을 사용했다.
post는 response의 body값만 필요한 경우로 retrieve()를 사용해 body만을 가져오도록 했다. postGetHeader는 response의 header 값만 필요한 경우로 exchangeToMono를 이용해 response를 받은 후 headers를 반환하게 만들었다. body와 header가 모두 필요한 경우는 아직 없어서 만들지 않았지만 필요하다면 postGetAll과 같은 메서드를 만들 예정이다.
@RequiredArgsConstructor
public class WebClientRequester {
private final WebClient webClient;
@Value("${webclient.origin}")
private String ORIGIN_URI;
protected <T, V> T post(String url, String cookieString, V requestDto, Class<T> responseDtoClass) {
return webClient.post()
.uri(url)
.header("Cookie", cookieString)
.header("Origin", ORIGIN_URI)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(requestDto)
.retrieve()
.onStatus(httpStatus -> httpStatus.is4xxClientError(),
clientResponse -> Mono.error(new RequestApiException("webclient 요청을 실패했습니다.")))
.onStatus(httpStatus -> httpStatus.is5xxServerError(),
clientResponse -> Mono.error(new RequestApiException("오류로 응답값을 받아올 수 없습니다.")))
.bodyToMono(responseDtoClass)
.block();
}
protected <T> ClientResponse.Headers postGetHeader(String url, T requestDto) {
ClientResponse clientResponse = webClient.post()
.uri(url)
.header("Origin", ORIGIN_URI)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(requestDto)
.exchangeToMono(response -> {
if (response.statusCode().is4xxClientError()) {
return Mono.error(new RequestApiException("webclient 요청을 실패했습니다."));
} else if (response.statusCode().is5xxServerError()) {
Mono.error(new RequestApiException("오류로 응답값을 받아올 수 없습니다."));
}
return Mono.just(response);
})
.block();
return clientResponse.headers();
}
}
사용
post 사용
@Slf4j
@Service
public class OrderService extends WebClientRequester{
private final String ORDER_REQUEST_URI;
private final String query;
public OrderService(WebClient webClient,
@Value("${webclient.order.uri}") String orderRequestUri,
@Value("${webclient.order.query}") String query) {
super(webClient);
this.ORDER_REQUEST_URI = orderRequestUri;
this.query = query;
}
public List<ResponseOrderDto> orderListRequester(String cookie, Integer startDate, Integer endDate) {
RequestOrderDto requestOrderDto = new RequestOrderDto(query, startDate, endDate);
String responseJson = post(ORDER_REQUEST_URI, cookie, requestOrderDto, String.class);
return parseOrderList(responseJson);
}
}
postGetHeader 사용
@Slf4j
@Service
public class AuthService extends WebClientRequester{
private final String LOGIN_URI;
public AuthService(WebClient webClient,
@Value("${webclient.login}") String loginUri) {
super(webClient);
this.LOGIN_URI = loginUri;
}
public String login(RequestLoginDto dto) {
ClientResponse.Headers headers = postGetHeader(LOGIN_URI, dto);
List<String> cookie = headers.header("Set-Cookie");
return cookie.toString();
}
}
WebFlux와 리액티브에 대해서는 공부가 더 필요하다. 지금은 대충 Mono와 Flux의 차이점 정도만 알고.. WebClient를 사용했지만 더 공부해서! WebFlux에 대한 포스팅을 쓸 것이다
참고 블로그