-
[JavaScript] SpringBoot 프로젝트 : 장소 검색(Kakao 지도 API)프로젝트/기능 정리 2023. 7. 14. 00:52
목적
- 카카오 지도 API를 이용해 장소를 검색하고 주소를 찾아본다.
- 지도나 마커 표시 없이 장소 검색 목록만 가져와서 표시한다.
갤러리 형태의 게시판을 만드는데 글을 등록할 때 장소를 검색해서 함께 등록할 수 있는 기능을 만들고자 했다.
카카오지도 샘플에는 검색과 더불어 마커를 지도에 표시하는 기능도 있지만,
여기서는 검색된 목록과 주소만 필요하여 필요 없는 코드는 제거하고 기록을 남겨두도록 하겠다.
우선 미완 상태지만 현재 페이지 모습은 아래와 같다.
글 등록 페이지 제일 하단에 위치한 장소를 입력하는 과정을 알아보겠다.
[Kakao 지도 API]
일단 카카오지도 API 를 사용하기 위해서는 API KEY가 필요하다.
KEY 발급을 위해 카카오 개발자사이트에 가입하자.
Kakao Developers
카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.
developers.kakao.com
https://apis.map.kakao.com/web/guide/
위 사이트의 가이드를 따라하면 된다.
API KEY를 받았으면 지도를 사용할 문서에 아래 코드를 적어준다.
<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=API-KEY&libraries=services"></script>
API-KEY라고 써있는 부분에 본인이 받은 JavaScript 키를 적어주면 된다.
뒤에 &libraries=services는 지도 검색 기능을 사용하기 위해 넣어줬다.
[JSP]
jsp에 작성한 태그는 장소 부분만 보도록 하자.
//코드 생략 <li> <img class = "option-icon" src = "../image_bundle/placeholder.png"> <span class = "option-title">장소</span> </li> <li class = "hide"> <input type = "text" id ="keyword" class = "input-textbox" placeholder = "장소를 검색해주세요."> </li> </ul> <ul id = "placesList"></ul>
중요한건 <input> 태그의 id인 "keyword"와 아래 검색 목록이 위치하게 될 <ul> 태그의 id인 "placeList"가 되겠다.
사용한 코드는 아래 사이트에 있다.
https://apis.map.kakao.com/web/sample/keywordList/
키워드로 장소를 검색하고 목록으로 보여주는 샘플인데, 샘플 이미지를 보자면 이렇다.
장소 검색 샘플 이미지 이 샘플 코드는 지도와 마커, 검색 목록을 모두 표시하고 있지만,
지금 원하는 기능은 장소 검색바 아래에 검색된 장소 이름과 주소만 띄우는 것이니 필요 없는 코드를 제거해줬다.
jsp 가장 하단에 script를 넣었고 전체 코드는 아래와 같다.
<script> $('#keyword').change(function(){ searchPlaces(); }); var ps = new kakao.maps.services.Places(); var listEl = document.getElementById('placesList'); function searchPlaces() { var keyword = document.getElementById('keyword').value; if (!keyword.replace(/^\s+|\s+$/g, '')) { removeAllChildNods(listEl); return false; } ps.keywordSearch( keyword, placesSearchCB); } function placesSearchCB(data, status, pagination) { if (status === kakao.maps.services.Status.OK) { displayPlaces(data); } else if (status === kakao.maps.services.Status.ZERO_RESULT) { removeAllChildNods(listEl); alert('검색 결과가 존재하지 않습니다.'); return; } else if (status === kakao.maps.services.Status.ERROR) { alert('검색 결과 중 오류가 발생했습니다.'); return; } } function displayPlaces(places) { var fragment = document.createDocumentFragment(); removeAllChildNods(listEl); for ( var i=0; i<places.length; i++ ) { var itemEl = getListItem(i, places[i]); (function(name){ itemEl.onclick = function(){ var key = document.getElementById('keyword'); key.value = name; removeAllChildNods(listEl); } })(places[i].place_name); fragment.appendChild(itemEl); } listEl.appendChild(fragment); } function getListItem(index, places) { var el = document.createElement('li'), itemStr = '<span class="markerbg marker_' + (index+1) + '"></span>' + '<div class="info">' + ' <span class = "info-title"><b>' + places.place_name + '</b></span>'; if (places.road_address_name) { itemStr += ' <br><span class = "info-address"><small>' + places.road_address_name + '</small></span>' } else { itemStr += ' <br><span class = "info-address"><small>' + places.address_name + '</small></span>'; } el.innerHTML = itemStr; el.className = 'item'; return el; } function removeAllChildNods(el) { while (el.hasChildNodes()) { el.removeChild (el.lastChild); } } </script>
function별로 보도록 하자.
$('#keyword').change(function(){ searchPlaces(); });
우선 가장 위에 있는 이 코드는 장소 검색바에 change 이벤트가 발생하면, searchPlaces()를 실행해서 해당 키워드로 장소를 검색하도록한 부분이다.
다음으로 미리 선언해둔 변수다.
var ps = new kakao.maps.services.Places(); var listEl = document.getElementById('placesList');
ps는 카카오지도에서 키워드를 통해 장소를 검색할 수 있게 해주는 객체이다.
listEl은 앞서 설명한 <ul>를 넣어줬다.
[searchPlaces]
function searchPlaces() { var keyword = document.getElementById('keyword').value; if (!keyword.replace(/^\s+|\s+$/g, '')) { removeAllChildNods(listEl); return false; } ps.keywordSearch( keyword, placesSearchCB); }
searchPlaces 함수는 keyword의 값을 가져와서 값이 있는지 확인하고, 있으면 ps 객체의 keywordSearch함수의 인자로 keyword를 전달한다.
[placesSerchCB]
placesSerchCB 함수는 장소 검색이 완료되었을 때 호출되는 콜백함수이다.
function placesSearchCB(data, status, pagination) { if (status === kakao.maps.services.Status.OK) { displayPlaces(data); } else if (status === kakao.maps.services.Status.ZERO_RESULT) { removeAllChildNods(listEl); alert('검색 결과가 존재하지 않습니다.'); return; } else if (status === kakao.maps.services.Status.ERROR) { alert('검색 결과 중 오류가 발생했습니다.'); return; } }
정상적으로 검색이 완료되었을 경우 displayPlaces함수에 리턴받은 data를 전달해준다.
[displayPlaces]
displayPlaces 함수는 검색된 데이터를 꺼내서 <ul>태그에 하나씩 넣어주는 역할을 한다.
function displayPlaces(places) { var fragment = document.createDocumentFragment(); removeAllChildNods(listEl); for ( var i=0; i<places.length; i++ ) { var itemEl = getListItem(i, places[i]); (function(name){ itemEl.onclick = function(){ var key = document.getElementById('keyword'); key.value = name; removeAllChildNods(listEl); } })(places[i].place_name); fragment.appendChild(itemEl); } listEl.appendChild(fragment); }
for문을 돌면서 places에 담겨 있는 데이터를 하나씩 꺼내 getListItem 함수에 넘겨준다.
마커나 리스트나 모두 이벤트를 연결하고 싶을 땐, 생성할 때 같이 해줘야한다.
그래서 이벤트 등록도 for문을 돌며 하나씩 만들었다.
중간에 (function(name){})(places[i].place_name); 이 구조가 낯설 수 있는데(처음 보고 뭔지 몰랐음..), 샘플에 나온 코드는 아래와 같다.
(function(marker, title) { kakao.maps.event.addListener(marker, 'mouseover', function() { displayInfowindow(marker, title); }); kakao.maps.event.addListener(marker, 'mouseout', function() { infowindow.close(); }); itemEl.onmouseover = function () { displayInfowindow(marker, title); }; itemEl.onmouseout = function () { infowindow.close(); }; })(marker, places[i].place_name); fragment.appendChild(itemEl); }
마지막 소괄호에 들어간(marker, places[i].place_name)을 function(marker, title)에 대응해서 받겠다는 의미로 보면된다.
반복적인 코드를 좀 줄여주는 역할 말고 다른게 있는지는 잘 모르겠으나, 사용법만 보자면 위 말대로 이해하면 될 것 같다.
[getListItem]
getListItem 함수는 실제로 HTML 코드를 생성하는 부분이다.
function getListItem(index, places) { var el = document.createElement('li'), itemStr = '<span class="markerbg marker_' + (index+1) + '"></span>' + '<div class="info">' + ' <span class = "info-title"><b>' + places.place_name + '</b></span>'; if (places.road_address_name) { itemStr += ' <br><span class = "info-address"><small>' + places.road_address_name + '</small></span>' } else { itemStr += ' <br><span class = "info-address"><small>' + places.address_name + '</small></span>'; } el.innerHTML = itemStr; el.className = 'item'; return el; }
<li>태그를 만들고 그 안에 필요한 내용인 장소명과 주소를 적어줬다.
중간 조건문은 도로명 주소 유무에 따라 있으면 도로명 주소, 없으면 지번 주소를 적도록 분기해둔 것이다.
[removeAllChildNods]
마지막으로 removeAllChildNods 함수는 <ul>태그 아래 생성된 자식노드들을 모두 제거하기 위한 함수이다.
새로운 키워드로 검색했거나, 이미 장소를 선택했거나, 검색 키워드를 입력하지 않았거나 할 때 호출해서 아래 자식 노드를 제거할 때 사용했다.
[결과]
검색 결과 두 번째 검색 결과를 클릭하면 아래와 같이 된다.
선택 결과 지금은 장소명만 가지고왔지만 다른 <input> 태그를 hidden으로 해두고 주소도 저장하여 폼이 전송될 때 주소도 같이 넘겨줄 수 있겠다.
이미 카카오API 샘플에 코드 주석과 설명이 잘 나와있어 자세한 설명은 없지만, 필요한 부분만 잘라서 커스텀한 부분을 정리해두기 위해 작성했다.
나중에 이 장소명 또는 주소로 좌표를 알아내서 지도에 표시해주는 기능도 시간이 되면 할 것 같으니, 나중에 이어서 정리하도록 하겠다.
'프로젝트 > 기능 정리' 카테고리의 다른 글