Spring RestTemplate - 공공api SERVICE KEY IS NOT REGISTERED ERROR
|2024. 10. 25. 02:55
문제
<200 OK OK,<OpenAPI_ServiceResponse>
<cmmMsgHeader>
<errMsg>SERVICE ERROR</errMsg>
<returnAuthMsg>SERVICE_KEY_IS_NOT_REGISTERED_ERROR</returnAuthMsg>
<returnReasonCode>30</returnReasonCode>
</cmmMsgHeader>
</OpenAPI_ServiceResponse>,[Access-Control-Allow-Origin:"*", Content-Type:"text/xml;charset=UTF-8", Content-Length:"229", Date:"Thu, 24 Oct 2024 15:24:30 GMT", Server:"NIA API Server"]>
기상청 단기예보 api를 사용하는데 위와 같은 응답이 왔다.

api에서 제공하는 에러 코드이다.

공공데이터 API는 위와 같이 인코딩 키와 디코딩 키를 둘 다 주고 잘 동작하는 키를 사용하라고 한다.
접근
1. 인코딩키와 디코딩키를 둘 다 사용해 본다. => 실패
2. 키가 전송되면서 바뀌나?
String apiUrl = "~~~~";
ResponseEntity<String> response = restTemplate.exchange(apiUrl, HttpMethod.GET, entity, String.class);
exchange를 따라 들어가 apiUrl이 바뀔 거 같은 부분을 디버깅해 보자.
@Nullable
public <T> T execute(String uriTemplate, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
URI url = this.getUriTemplateHandler().expand(uriTemplate, uriVariables);
return this.doExecute(url, uriTemplate, method, requestCallback, responseExtractor);
}
저 구간에서 디버깅을 찍어보면 String uriTemplate => URI url로 변화는 과정에 키 값이 변한다.
// this.uriTemplateHandler = initUriTemplateHandler();
private static DefaultUriBuilderFactory initUriTemplateHandler() {
DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory();
uriFactory.setEncodingMode(EncodingMode.URI_COMPONENT);
return uriFactory;
}
몇 가지 EncodingMode가 있는 듯하다.
일단 문제는, String으로 url을 넣으면 URI 객체로 변환되는 과정이 있고 그 과정에서 의도치 않은 변화가 일어나는 것이다.
해결
@Nullable
public <T> T execute(URI url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
return this.doExecute(url, (String)null, method, requestCallback, responseExtractor);
}
exchange에 URI 객체를 받는 다른 오버로딩 함수를 따라가면, url이 변환 없이 바로 doExecute로 들어간다.
URI 객체를 인자로 주면 쿼리 파라미터가 의도대로 들어갈 거 같다.
String apiUrl = "~~~~~";
URI apiUrl = new URI(apiUrl);
ResponseEntity<String> response = restTemplate.exchange(apiUrl, HttpMethod.GET, entity, String.class);
URI를 넣고 공공 api를 요청하면 정상적으로 데이터가 넘어오는 걸 확인할 수 있다.
+) UriComponentsBuilder
String baseUrl = "http://www.example.com/search";
URI uri = UriComponentsBuilder.fromHttpUrl(baseUrl)
.queryParam("key", "%2FY")
.queryParam("key2", "===2D")
.build()
.toUri();
System.out.println(uri); // http://www.example.com/search?key=%252FY&key2====2D
스프링 부트에서 UriComponentsBuilder를 사용해서 URI를 만들 수 있는데 value가 달라지는 걸 볼 수 있다.
restTemplate의 String => URI 변환되는 로직과 유사해 보인다.
URI를 만들 때도, 의도한 url이 생성됐는지 주의하자.
'back-end > spring' 카테고리의 다른 글
| JPA - Lazy 성능 차이, OneToOne 양방향 Lazy (0) | 2024.11.26 |
|---|---|
| Spring Data JPA - flush(sql error 1062 sqlstate 23000) (4) | 2024.10.20 |
| Error) Spring Security @PreAuthorize/@Secured NPE (2) | 2024.10.15 |
| Spring Data JPA - Cascade, orphanRemoval 차이 (0) | 2024.09.24 |
| Spring Security Session 인증 - REST API login (1) | 2024.09.07 |