공공 API를 호출하여 BD에 자동으로 최신화 시키는 코드를 작성해볼 것이다.

구조를 위와 같이 구성하였다.
파일의 구성은 다음과 같다.
Config
@Configuration
public class Public_config {
@Bean
public WebClient webClient() {
return WebClient.builder()
.baseUrl("http://openapi.price.go.kr")
.build();
}
}
Service
@Service
public class Public_service {
private final WebClient webClient;
public Public_service(WebClient webClient) {
this.webClient = webClient;
}
public Mono<String> getProductPriceInfo(String goodInspectDay, String entpId, String serviceKey) {
return webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/openApiImpl/ProductPriceInfoService/getProductPriceInfoSvc.do")
.queryParam("goodInspectDay", goodInspectDay)
.queryParam("entpId", entpId)
.queryParam("ServiceKey", serviceKey)
.build())
.retrieve()
.bodyToMono(String.class);
}
}
Controller
@RestController
public class Public_controller {
private final Public_service publicService;
public Public_controller(Public_service publicService) {
this.publicService = publicService;
}
@GetMapping("/product-price")
public Mono<String> getProductPriceInfo(@RequestParam String goodInspectDay,
@RequestParam String entpId,
@RequestParam String ServiceKey) {
return publicService.getProductPriceInfo(goodInspectDay, entpId, ServiceKey);
}
}
역시나 우리의 Spring은 바로 버그가 터져버렸다.
Unable to load io.netty.resolver.dns.macos.MacOSDnsServerAddressStreamProvider,
fallback to system defaults. This may result in incorrect DNS resolutions on
MacOS. Check whether you have a dependency on 'io.netty:netty-resolver-dns-native-
macos'. Use DEBUG level to see the full stack: java.lang.UnsatisfiedLinkError:
failed to load the required native library
API를 Postman으로 호출하려고 테스트를 하던 중, 위와 같은 오류가 발생하였다.
인터넷에 검색하면 바로 나오는 해결방법이다.
build.gradle에 종속성 추가
runtimeOnly 'io.netty:netty-resolver-dns-native-macos:4.1.104.Final:osx-aarch_64'
dependencies에 추가했지만 실패
레퍼런스 : https://stackoverflow.com/questions/71966221/spring-api-gateway-m1-java-lang-unsatisfiedlinkerror-no-netty-resolver-dns-n
jdk를 변경해보라는 말이 있다.
corretto 21 -> oracle graalvm 21 로 변경했지만 실패
gradlew 종속성 확인, 캐시 삭제 후 다시 build
./gradlew clean
./gradlew build --refresh-dependencies // dependencies 다시 build
./gradlew :dependencies | grep netty-resolver-dns-native-macos // 라이브러리 확인
황당한 점은 이미 netty 종속성이 잘 들어가있다는 점이었다.
| | +--- io.netty:netty-resolver-dns-native-macos:4.1.110.Final -> 4.1.111.Final
| | | +--- io.netty:netty-resolver-dns-native-macos:4.1.110.Final -> 4.1.111.Final (*)
| | +--- io.netty:netty-resolver-dns-native-macos:4.1.110.Final -> 4.1.111.Final
| | | +--- io.netty:netty-resolver-dns-native-macos:4.1.110.Final -> 4.1.111.Final (*)
| | +--- io.netty:netty-resolver-dns-native-macos:4.1.110.Final -> 4.1.111.Final
| | | +--- io.netty:netty-resolver-dns-native-macos:4.1.110.Final -> 4.1.111.Final (*)
| | +--- io.netty:netty-resolver-dns-native-macos:4.1.110.Final -> 4.1.111.Final
| | | +--- io.netty:netty-resolver-dns-native-macos:4.1.110.Final -> 4.1.111.Final (*)
| | +--- io.netty:netty-resolver-dns-native-macos:4.1.110.Final -> 4.1.111.Final
| | | +--- io.netty:netty-resolver-dns-native-macos:4.1.110.Final -> 4.1.111.Final (*)
이후 모든 netty를 dependencies에 추가했지만 실패
implementation 'io.netty:netty-all'
방화벽도 꺼봤다.

실패
gradle 을 터미널에서 새로 깔았다.

springboot에서 wrapper로 gradle을 지원하기 때문에 없어도 잘 작동한다.
하지만 혹시나 해서 깔아봤지만 실패
진짜 이게 뭔가싶어서 외부라이브러리를 하나하나 다 뜯어봤다.

resolver가 x86-64밖에 없다.

(내 이틀...!!!)
이상한게 dependency는 그대로 놔뒀고, build도 계속 반복했었는데,
Intellij를 껐다가 다시 실행시키니까

외부 라이브러리가 갱신이 되었고,
package com.projectharpseal.APIcall.service;
public class NettyLibraryTest {
public static void main(String[] args) {
try {
io.netty.resolver.dns.macos.MacOSDnsServerAddressStreamProvider.ensureAvailability();
System.out.println("Netty native library loaded successfully.");
} catch (Throwable t) {
t.printStackTrace();
System.out.println("Failed to load Netty native library.");
}
}
}
gpt에게 netty 테스트 코드를 짜달라고 한 뒤
오후 7:54:26: 실행 중 ':NettyLibraryTest.main()'...
> Task :compileJava
> Task :processResources UP-TO-DATE
> Task :classes
> Task :NettyLibraryTest.main()
Netty native library loaded successfully.
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
For more on this, please refer to https://docs.gradle.org/8.8/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
BUILD SUCCESSFUL in 1s
3 actionable tasks: 2 executed, 1 up-to-date
오후 7:54:27: 실행이 완료되었습니다 ':NettyLibraryTest.main()'.
실행 확인 도장까지 받았다.
의욕이 사라질것 같다...
디버깅을 하고 API를 호출하니 또 문제가 터져버렸다.
<?xml version="1.0" encoding="UTF-8"?><response>
<resultCode>90</resultCode>
<resultMsg>Invalid Authentication key.</resultMsg>
</response>
위는 Postman으로 호출해본 백엔드 API 코드이다.(위에서 나오는 Controller Get)
깔끔하게 90오류가 뜨는걸 확인할 수 있다.
이걸 공식문서에 확인을 해보면

아니 갑자기 서버 동기화니 뭐니 하는 오류 메시지가 뜬다.
다른 오류도 한번 살펴보면

키가 잘못된거면 SERVICE KEY IS NOT REGISTERED ERROR라고 뜬다.
곰곰히 한번 생각해봤다.
라는 생각을 하고 나서 내가 제출하고 있는 데이터를 뽑아봤다.
// 제출데이터 확인용 코드 추가
//Config
@Configuration
public class Public_config {
@Bean
public WebClient webClient() {
return WebClient.builder()
.baseUrl("http://openapi.price.go.kr")
.filter(logRequest())
.build();
}
//내가 무슨 데이터를 보내는지 filter를 통해서 확인한다.
private ExchangeFilterFunction logRequest() {
return ExchangeFilterFunction.ofRequestProcessor(clientRequest ->{
System.out.println("Request : " + clientRequest.method() + " " + clientRequest.url());
return Mono.just(clientRequest);
});
}
}
//보내야 하는 url
http://openapi.price.go.kr/openApiImpl/ProductPriceInfoService/getProductPriceInfoSvc.do?goodInspectDay=20240705&entpId=100&ServiceKey=XM38b9NRI3rme90bFEAXkx5tMhliHgoaAChXa7hA3BtVsmRzGKmrNkp7A9tILINENoYFQq9qEQqRi2Ln%2BtmPSQ%3D%3D
//내가 보내는 url
http://openapi.price.go.kr/openApiImpl/ProductPriceInfoService/getProductPriceInfoSvc.do?goodInspectDay=20240804&entpId=100&ServiceKey=XM38b9NRI3rme90bFEAXkx5tMhliHgoaAChXa7hA3BtVsmRzGKmrNkp7A9tILINENoYFQq9qEQqRi2Ln+tmPSQ%3D%3D
//차이점
원본 url = ~~ %2BtmPSQ%3D%3D
내가 보내는 url = ~~ +tmPSQ%3D%3D
내 예상처럼 데이터가 통신 과정에서 변화가 일어나는 모습이 보인다.
정확하게 써보자면
레퍼런스 : https://subji.github.io/posts/2020/04/28/urldecodepercenterror
이게 지금 발견해서 다행이라고 봐야할지 참 복잡한 심정이다.
위의 문서를 보면 %, /, \ 등에 대한 설정이 추가적으로 있는것을 확인할 수 있다.
%값을 파라미터로 받는것은 일반적이지 않는 행위라고 판단했다.
최종 결과물의 목표는 다음과 같다.
따라서 지금 내가 파라미터를 통신하는것이 의미가 없다.
그래서 하드코딩을 해봤다.
@Service
public class Public_service {
private final WebClient webClient;
public Public_service(WebClient webClient) {
this.webClient = webClient;
}
public Mono<String> getProductPriceInfo() {
String url = "url, parameter, key";
return webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String.class);
}
}
하지만 문제가 또 발생하였다.
보내야 하는 값 : %2B
실제로 보내는 값 : %252B
파라미터를 하드코딩 해도 url을 보낼때 한번더 인코딩을 하며 문제가 발생하고야 만다.
@Service
public class Public_service {
private final WebClient webClient;
public Public_service(WebClient webClient) {
this.webClient = webClient;
}
public Mono<String> getProductPriceInfo() {
String baseUrl = "baseurl";
String path = "path";
String queryParams = "parameter , key";
String fullUrl = baseUrl + path + "?" + queryParams;
return webClient.get()
.uri(URI.create(fullUrl))
.retrieve()
.bodyToMono(String.class);
}
}
그래서 계속 실험을 하며 알아낸 결과는...
결국 문서를 찾고 찾은 끝에 발견했다...

URI 생성자를 정리해 놓은 문서인데, create(string) 값을 넣으면 URI를 주어진 stirng값으로 만들어 준다고 한다.
따라서 URI.create(fullUrl)을 사용하게 되면 String 값이 그대로 파라미터로 넘어가게 되고...

드디어 해결..!
하드코딩하는건 좀 아니지않나? 라고 할 수 있다.
하지만 어차피 자동화를 하기 위해서 변수들을 전부 서버에 기록해 놓고 API를 부를때 client, host는 관련 변수를 전달하지 않는다.
따라서 .env 같은 text 파일을 하나 만들어 놓고, 이를 끌어다 쓸 생각이다.

이미 아실분들은 다 아실거라 생각하는데, 특이하게 URL이 아닌 URI를 사용한다.
레퍼런스 : https://www.inflearn.com/community/questions/1161423/url-%EC%99%80-uri-%EC%B0%A8%EC%9D%B4
간단 요약을 하자면
이라고 한다.
따라서, 통신을 하며 꼭 자원의 위치를 가리키는 것이 아닐수도 있으니 URI라고 지칭하는것이 정확한 표현이겠다.
분명히 쉬운 부분인데 왜이렇게 힘이 빠지는지 모르겠다..