- 새롭게 진행중인 "점심메뉴 추천서비스" 토이 프로젝트에 카카오맵 API를 활용하여 현재 사용자의 위치 근방에 존재하는 음식점들의 정보를 보여줄 수 있게 카카오맵 API 를 활용하였다.
- Js를 사용하면 더욱 간편하게 사용이 가능하지만 서버에서 데이터를 받아와서 화면에 뿌려주는 방식으로 구현하여 추후 기능 업데이트시에 서버에서 데이터를 별도로 가공할 목적으로 카카오맵 API를 Rest하게 사용하도록 구현하였다.
- 모든 코드는 GitHub에서 확인 가능하다
Link : https://github.com/DoHyeonJ/LunchRecommend
1. Controller
package com.lunchrecommend;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequiredArgsConstructor
public class LunchController {
private final LunchService lunchService;
/**
* 음식점 추천 List Post 요청
* @param request 요청 데이터
* @return ResponseEntity
*/
@PostMapping("/lunch")
private ResponseEntity<String> searchLunch(HttpServletRequest request) {
String latitude = request.getParameter("latitude");
String longitude = request.getParameter("longitude");
String page = request.getParameter("page");
return lunchService.getSearchLunchList(latitude, longitude, page, "15");
}
@PutMapping("/lunch")
private ResponseEntity<String> updateLunch() {
return ResponseEntity.status(HttpStatus.OK).body("PutMapping!!");
}
@DeleteMapping("lunch")
private ResponseEntity<String> deleteLunch() {
return ResponseEntity.status(HttpStatus.NO_CONTENT).body("DeleteMapping!!");
}
}
- Controller에서는 "/lunch"라는 url 호출시 동작할 mapping들을 정의해 놓았다.
- 현재는 PostMapping만 사용하지만 추후 사용성을 대비하여 Put과 Delete도 구현해놓았다.
- Url 요청시에 "http://localhost:8080/lunch" 를 호출 할 경우 Http Method를 Post, Put, Delete를 설정하여 호출할 경우를 각각 구현하였다.
- 필수 파라미터로는 latitude : 위도, longitude : 경도, page : 노출 페이지 수 / 3개의 파라미터를 받게된다.
2. Service
package com.lunchrecommend;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@Service
public class LunchService {
/**
* 좌표 기준 음식점 데이터 조회
* 파라미터 순서는 x, y 순서로 넘겨줘야 데이터 리턴 받을 수 있음
* @param longitude 경도 -> x
* @param latitude 위도 -> y
* @param page 페이지 수 1,2,3
* @param size 페이지당 조회 수 15
* @return ResponseEntity
*/
public ResponseEntity<String> getSearchLunchList(String longitude, String latitude, String page, String size) {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "KakaoAK {APIKEY}");
HttpEntity<String> entity = new HttpEntity<>("", headers);
String baseUrl = "https://dapi.kakao.com/v2/local/search/category.json?" +
"category_group_code=FD6" +
"&page=" + page +
"&size="+ size +
"&sort=accuracy" +
"&x=" + latitude +
"&y=" + longitude;
RestTemplate restTemplate = new RestTemplate();
return restTemplate.exchange(baseUrl, HttpMethod.GET, entity, String.class);
}
}
- 위에서 본 Controller에서 Post요청을 통해 getSearchLunchList 메서드를 호출한다.
- 경도, 위도, 노출 페이지 파라미터는 받았으며, 여기서 따로 설정해줘야 하는 부분은 headers.set을 해주고있는 부분의 {APIKEY}를 넣어줘야 한다.
- Key값 얻는법
- category_group_code는 카테고리 코드가 들어가야 하며 FD6은 음식점의 코드이다. 다른 카테고리의 코드가 필요하다면 카카오 공식문서를 참고하면 된다.
- https://developers.kakao.com/docs/latest/ko/local/dev-guide#search-by-category
3. View
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Kakao 지도 시작하기</title>
<script src="https://code.jquery.com/jquery-3.6.0.js" crossorigin="anonymous"></script>
</head>
<body>
<div>
<input type="hidden" id="select_count" value="3">
<input id="lunch_recommend" type="button" value="맛집 추천"/>
</div>
<div id="lunch_list">
<!-- lunch list area -->
</div>
<!-- TODO : appkey 변수화 작업 필요-->
<script type="text/javascript"
src="//dapi.kakao.com/v2/maps/sdk.js?appkey={APPKEY}&libraries=services"></script>
<script>
$('#lunch_recommend').click(function () {
if ("geolocation" in navigator) { /* geolocation 사용 가능 */
navigator.geolocation.getCurrentPosition(function (data) {
const latitude = data.coords.latitude;
const longitude = data.coords.longitude;
const page = $('#select_count').val();
searchLunchReccomend(latitude, longitude, page);
}, function (error) {
alert(error);
}, {
enableHighAccuracy: true,
timeout: Infinity,
maximumAge: 0
});
} else { /* geolocation 사용 불가능 */
alert('geolocation 사용 불가능');
}
});
function searchLunchReccomend(latitude, longitude, page) {
// 이미 한번이라도 호출했던 리스트 제거
$('.lunch_list_content').remove();
for (var i=1; i<parseInt(page)+1; i++) {
const jsonData = {
latitude: latitude,
longitude: longitude,
page: i
}
$.ajax({
type: "post",
url: "/lunch",
data: jsonData,
dataType: "json",
success: function (result) {
const lunchDiv = $('#lunch_list');
const resultList = Object.values(result)[0];
resultList.forEach(function (item) {
// 특정 카테고리 예외처리
if (checkCategoryName(item.category_name)) {
return;
}
const address = item.address_name;
const url = item.place_url;
const phone = item.phone;
const x = item.x;
const y = item.y;
const name = item.place_name;
const div = '<div class="lunch_list_content" ' + 'data-address=' + address + 'data-url=' + url + 'data-x=' + x + 'data-y=' + y + 'data-phone=' + phone + '>' + name + '</div>'
lunchDiv.append(div);
});
$('#lunch_recommend').hide();
$('#random_recommend').show();
},
error: function (e) {
alert("Fail");
console.log(e);
}
});
}
}
</script>
</body>
</html>
- 맛집 추천이라는 버튼을 누르게 될경우 lunch_recommend 클릭 이벤트가 발생하여 현재위치의 위도,경도값을 가져온다.
- 그 후 searchLunchRecommend 함수가 실행되며 lunch_list 라는 div 태그안에 현위치 근방의 음식점들이 노출되게 된다.
- 설정이 필요한 부분은 src="//dapi.kakao.com/v2/maps/sdk.js?appkey={APPKEY}&libraries=services" 스크립트영역에 APPKEY를 넣어주면된다.
- 크롬에서는 SSL 이 붙어있지않다면 현위치 조회가 불가하기 때문에 테스트는 다른 브라우저에서 해야한다.