프로젝트를 진행하면서 외부 서비스에 Get 요청을 보내야할 일이 생겼다. 보통 서버를 request가 오면 response를 보내는 일로 사용하는데 외부 서비스에 Get을 보내는 경우가 다소 생소했다.
다만 생각해보면 Server가 DB와 커넥션을 맺고 데이터를 보내거나 받는것, redis를 활용하는걸 생각해보면 생소한 건 아니다.
Spring은 WebClient를 사용해서 HTTP request를 보내도록 권장하고 관련 레퍼런스를 제공하고 있다.
WebClient
WebClient는 Reactive 방식의 비동기 요청으로 처리된다.
[문제 조건]
1. 1월부터 12월까지의 기상 정보를 받아서 최고 값과 최소 값의 차이를 구해야 함.
2. 한 번의 요청당 1개월의 데이터만 받을 수 있음.
해당 프로젝트에서는 1월부터 12월까지의 12개월의 데이터가 필요했는데 외부 서비스에서는 한번에 12개월의 데이터를 받을 수 없고 12번의 요청을 보내야만 했다.
한 번의 요청당 약 2초 가량의 시간이 소요됐는데 동기적으로 보낼 경우 총 24초가 걸렸다. WebClient의 비동기 특징을 활용해 12월까지의 데이터 요청을 한번에 보냈다. 모든 데이터를 한 번에 비교해야 했다. 때문에 비동기 요청이 다 처리되면 이후 작업을 진행하기 위해서 Mono<List>꼴로 데이터를 처리했다.
public Mono<List<WeatherAPIResponseDTO>> fetchWeatherDataWithMonth(float lat, float lon) {
Flux<Integer> months = Flux.range(1, 12);
Mono<List<WeatherAPIResponseDTO>> responses = months.flatMap(month -> webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/month")
.queryParam("lat", lat)
.queryParam("lon", lon)
.queryParam("month", month)
.queryParam("appid", weatherAPI)
.build())
.retrieve()
.bodyToMono(WeatherAPIResponseDTO.class))
.collectList() // 모든 결과를 리스트로 모음
.doOnSuccess(dataList -> {
System.out.println("Received Data: " + dataList);
})
.doOnError(error -> {
System.out.println("Error occurred: " + error.getMessage());
});
return responses;
}
Mono<List>로 받은 데이터라서 streamAPI를 활용해서 최대값과 최소값을 구했다.
StreamAPI가 익숙치 않아서 두 번에 걸쳐서 데이터를 모았는데 한번의 Stream에서 최대 최소를 비교하면 더 나을 것 같다.
public Mono<Double> getAnnualTemperatureRange( Mono<List<WeatherAPIResponseDTO>> weatherDat) {
Mono<Double> annualTemperatureRange = weatherDat.map(dataList -> {
double highest = dataList.stream().map(data -> data.getResult().getTemp().getRecordMax())
.max(Comparator.naturalOrder()).orElse(Double.NEGATIVE_INFINITY);
double lowestTemperature = dataList.stream()
.map(weatherData -> weatherData.getResult().getTemp().getRecordMin())
.min(Comparator.naturalOrder())
.orElse(Double.POSITIVE_INFINITY);
return highest - lowestTemperature;
});
return annualTemperatureRange;
}