🗺️
SSGSO 프로젝트를 진행하며 숙소의 전반적인 개발을 맡았는데 그 중 가장 많은 시간을 할애하고 고민을 한 부분이 숙소 생성이었다.
단순히 Jsp Form에서 사용자의 input값을 넘겨주었던 과거의 프로젝트와는 달리 이번에는 도로명 주소 API를 활용해 주소를 받아오고, 또 받아온 주소를 원하는 형태(위도/경도)로 적절하게 바꾸고 mapper로 넘겨줘야했기 때문이다.
따라서 이번 글에서는 다음의 과정들을 기록하고자한다.
- 도로명 주소 팝업 API
- 카카오 API 지오코딩 (Geocoding)
- Response에서 x,y 값을 추출
- Oracle DB에 INSERT & SELECT
사실 3번 부분이 많이 비효율적인거 같다(수많은 TypeReference의 사용..)
객체를 만드는게 더 효율적인 방법이었을까? 아니면 다른 좋은 방법이 또 있을까? 아직도 많이 고민된다.🤔
DB에 들어갈 data
- 전체 주소
- 도로명주소 API 활용
- 위도, 경도
- 얻은 전체 주소 활용
- Kakao Geocoder API
✔️Github
https://www.juso.go.kr/addrlink/devAddrLinkRequestGuide.do?menu=roadApi
JSP
를 선택API 신청하기
클릭승인키
에서 API 호출을 위한 키를 확인할 수 있다. 복사해두기!!jusoPopup.jsp
파일을 popup
폴더안에 넣고 폴더 자체를 프로젝트 안에 복사<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Insert title here</title>
String roadFullAddr = request.getParameter("roadFullAddr");
</head>
<script language="javascript">
function init() {
var url = location.href;
var confmKey = "발급받은 승인키";
...
}
roadFullAddr
만 남기고 다 주석처리하기confmKey
에 승인키 입력<div class="secRight">
<div class="rightheader">숙소 등록</div>
<form name="form" action="<%=contextPath%>/mypage/mySsgsoCreate" method="post">
<table class="list">
<tr>
<th>주소</th>
<td>
<input type="text" id="roadFullAddr" style="width:80%;" name="roadFullAddr" placeholder="주소검색을 눌러 주소를 입력해주세요" required readonly />
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="등록" id="addbutton">
<input type="reset" value="초기화" id="resetbutton">
</td>
</tr>
</table>
</form>
<button class="searchButton" onclick="goPopup()">주소 검색</button>
</div>
form
태그 바깥에 빼주어야한다. jusoPopup
도 form 형태로 가기 때문에 값이 제대로 입력되지 않은 채로 Controller로 바로 전달되기 때문이다.실행했을 때 자꾸 jsp 파일을 찾을 수 없다고 떠서 많은 애를 먹었다.🤦🏻♀️
경로를 상대경로로도 해보고, 절대경로로도 해보고, 현재 폴더 위치로 바꿔보기도하고 온갖 경로로 바꿔봐도 나오지가 않아서 아예 폴더 자체를 이리저리 옮기다보니 팝업이 정상적으로 나타났다.
아래와 같이 webapp
바로 아래에 popup
을 넣어주면
view
아래 mypage
폴더에 위치한 jsp와 "../popup/jusoPopup.jsp"
URL로 적용할 수 있다.
지오코딩이란 고유 명칭(주소 등)을 가지고 위도와 경도의 좌표값을 얻는 것을 의미한다.
- Kakao Geocode API 활용
https://apis.map.kakao.com/web/guide/
https://dapi.kakao.com/v2/local/search/address.json?query=판교역로 235
GET
& 테스트용 Request url 지정Headers
KEY
: AuthorizationVALUE
: KakaoAK 승인키붙여넣기@Override
public String getKakaoApiFromAddress(String roadFullAddr) {
String apiKey = "승인키";
String apiUrl = "https://dapi.kakao.com/v2/local/search/address.json";
String jsonString = null;
try {
roadFullAddr = URLEncoder.encode(roadFullAddr, "UTF-8");
String addr = apiUrl + "?query=" + roadFullAddr;
URL url = new URL(addr);
URLConnection conn = url.openConnection();
conn.setRequestProperty("Authorization", "KakaoAK " + apiKey);
BufferedReader rd = null;
rd = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
StringBuffer docJson = new StringBuffer();
String line;
while ((line=rd.readLine()) != null) {
docJson.append(line);
}
jsonString = docJson.toString();
rd.close();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return jsonString;
}
원래는 RestTemplate
을 사용하려고 했다. 하지만 Spring Boot에서는 잘만 사용할 수 있었던 ResponseEntity
와 같은 것들을 Spring Framework에서는 사용할 수 없었다.🤦
또한 Header에 정보를 담아 보내야 하는 형태여서 getObject
등을 사용하지 못했다.
그 대신 URLConnection
을 사용했다.
우선 URLEncoder
을 이용해 roadFullAddr을 UTF-8로 인코딩했다.
URL
에 API 호출 주소를 넣고 setRequestProperty
으로 헤더 Authorization을 추가
StringBuffer에 값을 넣고 String 형태로 변환하고 jsonString을 return
jdk 1.2 부터 내장되어 있으며, java.net 패키지에 있다. URL의 내용을 읽어오거나, URL 주소에 GET, POST로 데이터를 전달 할 때 사용한다.
이번에 String 으로 된 JSON 을 변경하는게 좋은 방법이 아니라는 점을 알게되었다. (이유를 찾아봐도 안나와서 직접 Stackoverflow에 질문해놨다.. 답변 plz..ㅜ_ㅜ)
내 코드가 많이 복잡해지고 애를 먹었던 이유이지 않을까 싶다. 좀더 좋은 방법이 무엇일까.. 이럴때 정말 좋은 선생님, 또는 좋은 멘토의 중요성을 절실히 느낀다🤧
Json String과 Map 사이의 변환은 Jackson 라이브러리를 사용했다.
🙇🏻♀️ 답변이 왔다!
답변에 따르면
JSON → Map
으로 바꾸는것은 Javascript나 Python에서만 하는 방법이므로Java에서는 관용적으로 JSON과 매핑되는 객체를 설계하고 연결해준다고 한다.
역시 객체를 생성해서 매핑시켜줘야 했나보다!! 다음주부터 프로젝트를 이어서 할 예정인데 그때 이 부분을 리팩토링해야겠다🤸🏼♀️🤸🏼♀️🤸🏼♀️ 아이씬나
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
ObjectMapper mapper = new ObjectMapper();
TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>(){};
Map<String, Object> jsonMap = mapper.readValue(jsonString, typeRef);
@Override
public HashMap<String, String> getXYMapfromJson(String jsonString) {
ObjectMapper mapper = new ObjectMapper();
HashMap<String, String> XYMap = new HashMap<String, String>();
try {
TypeReference<Map<String, Object>> typeRef
= new TypeReference<Map<String, Object>>(){};
Map<String, Object> jsonMap = mapper.readValue(jsonString, typeRef);
@SuppressWarnings("unchecked")
List<Map<String, String>> docList
= (List<Map<String, String>>) jsonMap.get("documents");
Map<String, String> adList = (Map<String, String>) docList.get(0);
XYMap.put("x",adList.get("x"));
XYMap.put("y", adList.get("y"));
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return XYMap;
}
JSON String에서 총 세번을 변환시켜서 x, y 값을 얻었다. (국회의사당역 주소 검색)
jsonString
{"documents":[{"address":{"address_name":"서울 영등포구 여의도동 1-6","b_code":"1156011000","h_code":"1156054000","main_address_no":"1","mountain_yn":"N","region_1depth_name":"서울","region_2depth_name":"영등포구","region_3depth_h_name":"여의동","region_3depth_name":"여의도동","sub_address_no":"6","x":"126.917885535023","y":"37.5280674292228"},"address_name":"서울 영등포구 국회대로 지하 758","address_type":"ROAD_ADDR","road_address":{"address_name":"서울 영등포구 국회대로 지하 758","building_name":"국회의사당역","main_building_no":"758","region_1depth_name":"서울","region_2depth_name":"영등포구","region_3depth_name":"여의도동","road_name":"국회대로","sub_building_no":"","underground_yn":"Y","x":"126.917885535023","y":"37.5280674292228","zone_no":"07236"},"x":"126.917885535023","y":"37.5280674292228"}],"meta":{"is_end":true,"pageable_count":1,"total_count":1}}
jsonMap
{documents=[{address={address_name=서울 영등포구 여의도동 1-6, b_code=1156011000, h_code=1156054000, main_address_no=1, mountain_yn=N, region_1depth_name=서울, region_2depth_name=영등포구, region_3depth_h_name=여의동, region_3depth_name=여의도동, sub_address_no=6, x=126.917885535023, y=37.5280674292228}, address_name=서울 영등포구 국회대로 지하 758, address_type=ROAD_ADDR, road_address={address_name=서울 영등포구 국회대로 지하 758, building_name=국회의사당역, main_building_no=758, region_1depth_name=서울, region_2depth_name=영등포구, region_3depth_name=여의도동, road_name=국회대로, sub_building_no=, underground_yn=Y, x=126.917885535023, y=37.5280674292228, zone_no=07236}, x=126.917885535023, y=37.5280674292228}], meta={is_end=true, pageable_count=1, total_count=1}}
docList
[{address={address_name=서울 영등포구 여의도동 1-6, b_code=1156011000, h_code=1156054000, main_address_no=1, mountain_yn=N, region_1depth_name=서울, region_2depth_name=영등포구, region_3depth_h_name=여의동, region_3depth_name=여의도동, sub_address_no=6, x=126.917885535023, y=37.5280674292228}, address_name=서울 영등포구 국회대로 지하 758, address_type=ROAD_ADDR, road_address={address_name=서울 영등포구 국회대로 지하 758, building_name=국회의사당역, main_building_no=758, region_1depth_name=서울, region_2depth_name=영등포구, region_3depth_name=여의도동, road_name=국회대로, sub_building_no=, underground_yn=Y, x=126.917885535023, y=37.5280674292228, zone_no=07236}, x=126.917885535023, y=37.5280674292228}]
adList
{address={address_name=서울 영등포구 여의도동 1-6, b_code=1156011000, h_code=1156054000, main_address_no=1, mountain_yn=N, region_1depth_name=서울, region_2depth_name=영등포구, region_3depth_h_name=여의동, region_3depth_name=여의도동, sub_address_no=6, x=126.917885535023, y=37.5280674292228}, address_name=서울 영등포구 국회대로 지하 758, address_type=ROAD_ADDR, road_address={address_name=서울 영등포구 국회대로 지하 758, building_name=국회의사당역, main_building_no=758, region_1depth_name=서울, region_2depth_name=영등포구, region_3depth_name=여의도동, road_name=국회대로, sub_building_no=, underground_yn=Y, x=126.917885535023, y=37.5280674292228, zone_no=07236}, x=126.917885535023, y=37.5280674292228}
정말 다시봐도 엄청난 노가다다...^^...🤦 차라리 객체를 생성해서 넣어두었어야 했을까? 아니면 서버단에서 이걸 처리한게 실수일까?
JSONObject
을 사용하려고 했지만 자꾸 arguments 에러가 떠서 사용하지 못했다.
x(경도, longitude)와 y(위도, latitude)값은 실수 형태이다.
- x=126.917885535023, y=37.5280674292228
따라서
- 소수점 형태로 DB에 값을 INSERT
- 소수점 형태 SELECT 해와야 한다.
p
: 소수점을 포함한 모든 숫자의 개수c
는 소수점이하 숫자의 개수모든 숫자
는 10자리, 정수
는 3자리,소숫점 아래
6자리까지 표현될 수 있다.9
: 해당자리 숫자 의미, 없을 경우 공백0
: 해당 자리 숫자, 없을 경우 0표시<select id="selectAccomodation" parameterType="int" resultType="com.project.ssgso.dto.AccomodationDto">
SELECT AC_NO, NAME, INFO, AC_IMG,
TO_CHAR(LATITUDE, 'FM990.000000') AS latitude,
TO_CHAR(LONGITUDE, 'FM990.000000') AS longitude,
ADDRESS, AC_PHONE, AC_ACCOUNT, AC_PRICE, CATEGORY, MEM_NO
FROM ACCOMODATION
WHERE AC_NO = #{ac_no}
ORDER BY AC_NO ASC
</select>
개발자 국비과정을 듣게 됐는데 스프링 프로젝트 할 때, 참고해봐야겠네요~감사합니다~