해당 글은 코드 스테이츠 개인 기술 회고를 하면서 작성함
위치 서비스를 구현해야 하는 만큼 사용자의 위치 정보를 얻어야 하는 점이 이번 프로젝트의
critical task 중 하나였습니다.
처음 위치정보를 어떤 식으로 받아와야 하는 것에 대해 막연하게
“그거 그냥 카카오나 네이버 지도 api” 쓰면 될 것 같은데라고 생각하였지만
“어떻게?”라는 관점에서 보니정말 막막하였습니다.
1. 사용자 IP로 위치정보 받아오기
처음에는 IP로 확인을 하면 되지 않을까 해서 찾아본 결과, MaxMind회사의 GeoLite2라는
api를 사용하면 도시정보를 받아볼 수 있다는 걸 알게 되어 바로 도입을 해보았습니다.
GeoLite2는 큰 그림에서 봤을 때 접속자의 IP를 토대로 GeoLite2 DB상에 저장된 도시정보를
반환하는 형식이었습니다.
따라서 실 사용자의 IP를 추적하는 것이 필요했습니다.
이는 HttpServletRequest의 getRemoteAddr()를 사용하면 손쉽게 가능할 줄 알았으나
실 사용자가 아닌 프록시나 로드 밸런서의 IP 주소를 받는 경우가 많습니다.
따라서 구글링 후, 하단의 코드를 추가하여 여러 중개 서버를 고려한 실제 클라이언트 ip주소를
받을 수 있도록 하였습니다.
@Transactional
public String getRemoteIP(HttpServletRequest request) {
String ip = null;
ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-RealIP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("REMOTE_ADDR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
Ip자체를 받는 것에는 이상이 없었으나, 문제는 도시 정보가 “영문”으로 반환된다는 점과
반환된 주소의 정확도가 꽤나 떨어진다는 문제가 있었습니다. (예: 부천인데 화성으로 표시)
2. 위/경도 값으로 위치정보 받아오기
이대로는 실제 서비스 시 무리가 올 것이 분명하기 때문에 고민 끝에 kakao api를 사용해보기로
결정했습니다. 저희는 위/경도 나 ip 주소를 실제 도시 정보로 반환받길 원했기 때문에 kakao api의
“Reverse GeoCoding”이라는 기술이 적합했습니다.
다만 상기 api는 위도 / 경도를 요청으로 보내야 하기 때문에 우리 측에서 위/경도 값을 먼저
갖고 있어야 합니다. 이를 위해 프론트 분들께 GeoLocation을 사용하여 위/경도 값을 받고
서버 쪽으로 보내달라고 요청했습니다.
카카오 API - Reverse GeoCoding을 사용하면 보낸 위/경도 값으로 해당 지역 정보를 한글로 상세하게
반환받을 수 있습니다. (JSON 형태로 String값 반환)
"documents": [
{
"address_name": "전북 익산시 부송동 100",
"y": "35.97664845766847",
"x": "126.99597295767953",
"address_type": "REGION_ADDR",
"address": {
"address_name": "전북 익산시 부송동 100",
"region_1depth_name": "전북",
"region_2depth_name": "익산시",
"region_3depth_name": "부송동",
"region_3depth_h_name": "삼성동",
"h_code": "4514069000",
"b_code": "4514013400",
"mountain_yn": "N",
"main_address_no": "100",
"sub_address_no": "",
"x": "126.99597295767953",
"y": "35.97664845766847"
},
"road_address": {
"address_name": "전북 익산시 망산길 11-17",
"region_1depth_name": "전북",
"region_2depth_name": "익산시",
"region_3depth_name": "부송동",
"road_name": "망산길",
"underground_yn": "N",
"main_building_no": "11",
"sub_building_no": "17",
"building_name": "",
"zone_no": "54547",
"y": "35.976749396987046",
"x": "126.99599512792346"
}
}
반환 형식은 도로명주소 + 일반 주소로 이루어져 있는데 도로명 주소가 없으면
일반 주소에서 찾도록 하였고, 저희는 상세 지역정보가 아닌 “시” 단위의 정보가 필요했기에
반환 형식에서의 “region_2 depth_name “ 부분만 필요했습니다.
따라서 구글링을 토대로 JSONObject와 JSONArray클래스를 사용해 해당 부분을 구했습니다.
예외적으로, “서울”은 "region_1 depth_name"으로 반환되기 때문에 위의 방식대로 한다면
“xx구“라고 반환이 됩니다. 따라서 먼저 서울이라는 문자열이 있는지 판단하고 만약 서울이라면
바로 “서울”을 string에 담아 반환하도록 하였습니다.
***
//서울로 시작하면 바로 반환
if("서울".equals((String)address.get("region_1depth_name"))){
return "서울"; }
else {
value = (String) address.get("region_2depth_name");
}
***
이후로 위치정보를 사용자 entity에 저장하여 활용할 수 있게 되었습니다.
추가적으로 내 주변의 스터디를 파악해야 하기 때문에 위 / 경도 값을 계산하여 반경 3KM 내
존재하는 스터디(스터디장의 위치정보)를 리스트로 반환하도록 하였습니다.
해당 위경도 간 계산 부분은 같은 백엔드 팀원께서 구현을 도와주셨습니다.
결론
우여곡절이 많았고 쉽지 않았지만 처음으로 외부 api를 사용해보았다는 점에서 스스로 의미가 있었다고
생각합니다. 또한, 하면서 원하는 결괏값을 얻을 수 있었기 때문에 개인적으로 재밌게 구현할 수 있었고
추후에 다른 프로젝트를 하게 되더라도 기꺼이 도입해볼 수 있겠다는 자신감도 얻었습니다.
전반적인 코드는 아래의 블로그를 참고하였습니다.
[Java] Kakao map 카카오맵 로컬 API 좌표(경도, 위도)로 주소 변환하기(REST API)
안녕하세요 애리몽입니다. 오늘은 좌표를 이용하여 주소를 가져오는 카카오맵 로컬 API를 사용하면서 삽질했던 부분들 정리 해보려고 합니다. 좌표-주소 변환 특정 좌표의 지번 주소 및 도로명
developerjal.tistory.com
'codestates_BE_bootcamp39 > 회고' 카테고리의 다른 글
Pre-Project 돌아보기 (2) | 2022.09.10 |
---|---|
Section 4 돌아보기 (2) | 2022.08.18 |
Section 3 돌아보기 (2) | 2022.07.20 |
Section 2 돌아보기 (0) | 2022.06.22 |
Section 1 돌아보기 (6) | 2022.05.23 |