한 줄 요약: 공공데이터포탈 service key와
UriComponentsBuilder
의 URL 인코딩
공공데이터포탈 API를 사용하여 미니 프로젝트를 만들고 있다. 이 과정에서 발생한 URL 인코딩 관련 문제를 적어보려고 한다. 사용한 API는 다음과 같이 웹사이트에서 발급받은 인증키를 넣고, 필요한 파라미터들과 함께 요청해야했다.
API가 돌아가는지 대충 확인하기 위해 초기에는 StringBuilder로 필요한 파라미터를 모아 URL을 묶은 후 API를 호출하는 코드를 작성했다.
StringBuilder urlBuilder = new StringBuilder(ARRIVE_BUS_API_URL);
urlBuilder.append("?" + URLEncoder.encode("serviceKey", "UTF-8") + "=" + serviceKey);
urlBuilder.append(
"&" + URLEncoder.encode("_type", "UTF-8") + "=" + URLEncoder.encode("json", "UTF-8"));
urlBuilder.append(
"&" + URLEncoder.encode("cityCode", "UTF-8") + "=" + URLEncoder.encode("12345", "UTF-8"));
urlBuilder.append(
"&" + URLEncoder.encode("nodeid", "UTF-8") + "=" + URLEncoder.encode(nodeId, "UTF-8"));
return new URL(urlBuilder.toString());
API 한두개 정도면 괜찮겠지만, 사용하는 API가 4개 이상이 되니 조금 꼴보기 싫어졌다. 그래서 검색하던 중, UriComponentsBuilder
라는 친구를 발견했다. 이 친구를 사용하면 URL을 다음과 같이 깔끔하게 표현할 수 있었다.
URI uri = UriComponentsBuilder.fromHttpUrl(ARRIVE_BUS_API_URL)
.queryParam("serviceKey", serviceKey)
.queryParam("_type", "json")
.queryParam("cityCode", "12345")
.queryParam("nodeid", nodeId)
.encode(StandardCharsets.UTF_8)
.build()
.toUri();
URL url = uri.toURL();
쌩 문자열로 이어붙이는 것보다 이렇게 깔끔하게 주어지는 인터페이스를 사용하는 게 유지보수 측면에도 좋아보였다. 그래서 사용하고자 하였는데.. 문제가 발생했다.
StringBuilder로 작성한 URL은 200 OK가 오는데, UriComponentsBuilder로 작성한 URL에는 오지 않음
다른 차이가 하나도 없는데 UriComponentsBuilder
만 되지않는 게 이상하여 두 방식으로 만든 URL을 출력해봤다.
두 방식으로 만들어낸 URL은 길이부터 달랐고, 그 중 서비스키부분의 길이가 달랐다. UriComponentsBuilder
로 만든 URL은 %2F
가 나와야하는 곳에 %252
, %3D%3D
는 %253D%253D
가 표시되어 있었다. 분명 인코딩관련한 문제가 생겼다고 판단했다.
공공데이터 포탈에 가서 다시 한 번 개인 API인증키를 확인해봤다.
(이전에는 아무 생각없이 인코딩된 값을 복사해서 사용했는데, 이제야 디코딩된 값도 있는 게 보였다)
공공데이터 포탈 인증키는 인코딩값과 디코딩값 두 개를 지원해준다. 두 값을 복사해서 비교하면 다음과 같이 나온다.
위가 인코딩된 값, 아래가 디코딩된 값이다.
원본값(디코딩값)은 마지막에 ==가 붙어있는 것을 보니 BASE64로 인코딩되어있다는 걸 알 수 있다. 그리고 인코딩된 값은 URL인코딩이 되어있다.
문제가 발생했던 UriComponentsBuilder
의 코드를 보면 아래와 같이 .encode()를 붙인 것을 볼 수 있다.
URI uri = UriComponentsBuilder.fromHttpUrl(ARRIVE_BUS_API_URL)
.queryParam("serviceKey", serviceKey)
.queryParam("_type", "json")
.queryParam("cityCode", "12345")
.queryParam("nodeid", nodeId)
.encode(StandardCharsets.UTF_8)
.build()
.toUri();
encode() 때문에 이중 인코딩이 발생하면서 원하는 URL이 나오지 않은 것으로 보였다. 실제로 URL 인코딩 사이트에서 돌려보니 이중인코딩된 값이 맞았다.
(%2F
=> %252
, %3D%3D
=> %253D%253D
)
해결할 방법은 2가지가 있다.
UriComponentsBuilder
에 encode()를 빼기나는 2번 방식으로 문제를 해결하였다.
특수문자를 URL에 사용할 때는 URL인코딩이 필수라는 걸 알게되었다.
그렇다면 서비스가 사용자에게 검색기능을 제공하고, 해당 검색어를 URL의 쿼리파라미터로 받는 경우 URL인코딩 처리가 필수겠다는 생각이 들었다. 실제로 찾아보니 Springboot에서 자동으로 URL 디코딩처리를 해준다고 하였다. 많은 기능을 자동으로 지원해주다보니 몰랐던 개념이 많은 것 같다. 아직 많이 부족하다는 생각이 든다. 많이 공부해야겠다