공공데이터 Open API를 사용하는 서비스의 흐름을 알기 위해 공부를 하면서 알게 된 내용들을 기록하는 글입니다. 오류나 고쳐야 할 사항들이 있다면 피드백 부탁드리겠습니다!
WebFlux 라이브러리를 사용한 이유
Spring에서 외부 API를 사용할 수 있는 종류는 다음과 같다.
- HttpURLConnection
- RestTemplate
- WebClient
HttpURIConnection은 URLBuilder를 사용하여 코드를 작성한 것으로, 생각보다 코드가 길고 장황하다.
RestTemplate은 Spring에서 지원하는 객체로, 동기식으로 Rest 방식 API를 호출할 수 있는 Spring 내장 클래스이다.
동기적으로 동작하기 때문에 요청/응답 동안 스레드가 차단되는 문제점이 있을 뿐더러, Spring boot 5.x 버전 이후로 Deprecated 되었기에 WebFlux 라이브러리의 WebClient를 사용하기로 결정했다.
WebClient를 사용한 Open API 연결
WebClient를 통한 Http 통신은 다음 과정을 거친다.
- WebClient의 인스턴스를 builder를 통해 생성
- url, query parameter등을 설정
- 결과 값을 받아 처리
1. build.gradle
implementation 'org.springframework.boot:spring-boot-starter-webflux'
webflux를 사용하기 위한 종속성을 추가해준다.
2. WebClientConfig 설정
WebClient.Builder를 Bean으로 의존성 주입이 가능하기 때문에 생성자를 통해 의존성을 주입받는다.
@Configuration
public class WebClientConfig {
@Value("${sample.baseurl}")
private String baseUrl;
@Bean
public WebClient webClient(){
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
return WebClient.builder()
.uriBuilderFactory(factory)
.baseUrl(baseUrl)
.build();
}
}
여기서 중요한 부분은 factory.setEncodingMode이다.
WebClient는 query parameter를 UriComponentsBuilder#encode() 방식을 이용해서 인코딩하기 때문에 쿼리 파라미터의 값이 달라질 수 있다.
따라서 인코딩 방식을 다르게 하거나, 인코딩을 하지 않도록 적용해야 한다.
DefaultUriBuilderFactory() 객체를 생성하여 인코딩 모드를 NONE이나 VALUES_ONLY로 변경한 후, WebClientBuilder에 적용하면 API 통신이 정상적으로 작동할 것이다.
나는 VALUES_ONLY를 적용하여 쿼리 파라미터의 값만 인코딩 하였다.
3. 비즈니스 로직 구현
package bootcamp.sample.openapi.sample;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@Service
@RequiredArgsConstructor
public class SampleService {
private final WebClient webClient;
@Value("${sample.encoding}")
private String encodingKey;
public Mono<CrimeDTO> getCrime(String page, String perPage) {
return webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/3074462/v1/uddi:fe3ae686-8f7d-4d82-8c3a-901a02a0aa75")
.queryParam("page", page)
.queryParam("perPage", perPage)
.queryParam("serviceKey", encodingKey)
.build())
.retrieve()
.bodyToMono(CrimeDTO.class)
.doOnError(e -> {
System.err.println("Error: " + e.getMessage());
});
}
}
- encodingKey : 공공데이터포탈에서 발급해준 인코딩 키이다. webclient url에 넣어주면 된다.
- uriBuilder를 이용하면 url, query parameter를 깔끔하게 설정 가능하다.
- .retrieve() 메서드는 WebClient의 주요 메서드 중 하나인데, 해당 메서드를 호출하면 HTTP 요청을 보내고 그에 대한 응답을 받을 수 있다.
- .bodyToMono()를 통해 응답을 원하는 형태의 객체로 받을 수 있다.
이후에는 .subscribe() 혹은 .block()을 통해 응답을 받을 수 있는데, .block()을 사용하면 응답을 동기적으로 대기하고 받아온다.
<추가>
macOS 환경에서 로컬로 webflux를 사용하려했던 결과 다음과 같은 에러가 발생했다.
Unable to load io.netty.resolver.dns.macos.MacOSDnsServerAddressStreamProvider, fallback to system defaults. This may result in incorrect DNS resolutions on MacOS.
이것은 applie silicon(M1, M2 등)을 사용하면서 netty를 사용하면 발생하는 문제다.
따라서 gradle에 다음 dependency를 추가하면 해결할 수 있다.
runtimeOnly 'io.netty:netty-resolver-dns-native-macos:4.1.104.Final:osx-aarch_64' // 추가
참고
https://m42-orion.tistory.com/124
[Spring WebFlux] WebClient 통해 공공데이터 Open API 호출하기 + 유의점(SERVICE KEY IS NOT REGISTERED ERROR)
이 글은 공부를 하면서 알게 된 내용들을 기록하는 글 입니다. 오류나 고쳐야 할 사항들이 있다면 지적 부탁드립니다! ✅ WebFlux를 이용한 이유 혼자 공부 겸 진행하고 있는 프로젝트에서 공공데
m42-orion.tistory.com
https://effortguy.tistory.com/282
[Spring Webflux] Unable to load io.netty.resolver.dns.macos.MacOSDnsServerAddressStreamProvider, fallback to system defaults. Th
문제 spring webflux 서버를 키니 아래와 같은 에러 로그가 발생했다. Unable to load io.netty.resolver.dns.macos.MacOSDnsServerAddressStreamProvider, fallback to system defaults. This may result in incorrect DNS resolutions on MacOS. 원
effortguy.tistory.com
https://blog.naver.com/hj_kim97/222295259904
[Spring]스프링 RestTemplate
스프링 RestTemplate - RestTemplate란? - RestTemplate의 특징 - RestTemplate 동작 원리 -...
blog.naver.com
https://wpioneer.tistory.com/222
[Spring Boot] WebClient 파라미터 인코딩 하는법
WebClient를 사용해서 그냥 호출하게 되면 인코딩을 하지 않아 API 키가 달라지는 경우가 생길수가 있다. 나같은 경우에 그 문제 때문에 골머리를 앓았는데 아래와 같은 방법으로 해결했다. 일단 Uri
wpioneer.tistory.com
'Back-end > Spring' 카테고리의 다른 글
[Spring] 무한 redirection (0) | 2024.08.14 |
---|---|
[Spring] Security + JWT + Redis를 활용한 로그인 구현 (1) (0) | 2024.07.20 |
[Spring] H2 DataBase 사용법 (0) | 2024.06.27 |
[Spring] Spring만의 유효성 검사 @Valid, @Validated 정리 (2) | 2024.06.27 |
[Spring] createdAt, updatedAt 사용하기 (0) | 2024.06.19 |