먼저 카카오 API를 이용한 지도, 지도 검색 개발일지를 기록한 https://isjiji.tistory.com/43 게시글을 확인해주세요 ^~^
초보개발자 Toy-project 개발일지 ▲NO.6 ▲ 카카오 지도 API, 현재위치 근처 맛집 지도 검색 Javascript
목차 1 인사^~^ 2 오늘의 개발일지 주제 3 카카오API 사용하는방법 4 Front-end에 지도 띄우기 1. 왜 오랜만에 개발일지를 쓰나요? 개발일지를 한번 작성하면 세시간가량의 시간이 소요된다. 퇴근 후,
isjiji.tistory.com
카카오에서 제공해주는 소스를 활용하면, 지도를 띄우고 그 지도 위에 특정장소를 마킹할 수 있다.
하지만! 근처 장소 위주로 찾을 수는 없다.
카카오 오픈소스에서 제공하는 기본 장소가 '이태원'으로 설정되어있고,
검색시에는 한국 전역을 대상으로 장소검색을 하기 때문이다.

하지만!
아래의 사진을 보자. 중국집을 검색하면, 근처에 있는 중국집이 지도와 검색리스트에 표시되는 것을 볼 수 있다.
어떻게 하면 현재 위치를 중심으로 장소를 검색할 수 있을까?

1.
구글로케이션을 사용하여 현재 위치를 가져온다.
navigator.geolocation.getCurrentPosition(getLocWeather, showErrorMsg); 요게 실행되면
현재위치를 가져올 수 있는 경우 getLocWeather() 함수 실행
현재위치를 가져올 수 없는 경우 showErrorMsg() 함수 실행
2.
getLocWeather()가 실행되면, 함수의 인자를 이용해서 위도와 경도를 가져올 수 있다.
var userLat = position.coords.latitude; //위도
var userLat = position.coords.longitude; //경도
위도와 경도를 각각 변수에 담아준다!!
3.
카카오에서 제공한 JSON형식의 mapOption에 위도와 경도를 담아준다.
mapOption = {
center: new kakao.maps.LatLng(userLat, userLng), // 구글로케이션으로 받은 위도, 경도
level: 3
};
map = new kakao.maps.Map(mapContainer, mapOption); //지도생성
//참고 !! 카카오가 제공하는 기존 위치 설정임
mapOption = {
center: new kakao.maps.LatLng(37.566826, 126.9786567), // 지도의 중심좌표
level: 3 // 지도의 확대 레벨
};
4.
장소를 검색할 때, 현재위치 근처로 검색되게 하려면, (ex '치킨' 검색시 - 사용자 근처에 있는 치킨집 검색 )
ps.keywordSearch(keyword, placesSearchCB, {x:userLng , y:userLat});
첫번째 인자에 검색명두번째 인자에 콜백함수세번째 인자에 경도와 위도 입력 (경도가 먼저 입력되어야함!!!!!!)
5.
현재위치를 가져올 수 없는 경우 showErrorMsg() 함수가 실행된다.
받아온 error 인자의 code 값을 조사해서 error 이유를 alert로 띄워준다.
function showErrorMsg(error){
switch(error.code){
case error.PERMISSION_DENIED: alert("사용자가 사용 요청을 거부했습니다."); break;
case error.POSITION_UNAVAILABLE: alert("가져온 위치 정보를 사용할 수 없습니다."); break;
case error.TIMEOUT: alert("요청 허용 시간을 초과했습니다."); break;
case error.UNKNOWN_ERROR: alert("알 수 없는 오류가 발생했습니다."); break;
}
}
이렇게 하면 현재위치를 가져와서 카카오지도에 세팅해줄 수 있다.
var userLng,userLat, mapContainer, map, infowindow,
ps = new kakao.maps.services.Places(); // 장소 검색 객체를 생성;
var markers = [];
navigator.geolocation.getCurrentPosition(getLocWeather, showErrorMsg);
function getLocWeather(position){ //현재 위치
userLat = position.coords.latitude; //위도
userLng = position.coords.longitude;//경도
mapContainer = document.getElementById('map'), // 지도를 표시할 div
mapOption = {
center: new kakao.maps.LatLng(userLat, userLng), // 지도의 중심좌표
level: 3 // 지도의 확대 레벨
};
map = new kakao.maps.Map(mapContainer, mapOption); //지도생성
infowindow = new kakao.maps.InfoWindow({zIndex:1}); // 검색 결과 목록이나 마커를 클릭했을 때 장소명을 표출할 인포윈도우를 생성
searchPlaces("맛집");
}
function searchPlaces(keyword) {// 키워드 검색 요청
if (!keyword.replace(/^\s+|\s+$/g, '')) {
alert('키워드를 입력해주세요');
return false;
}
ps.keywordSearch(keyword, placesSearchCB,{x:userLng , y:userLat}); // 장소검색 객체를 통해 키워드로 장소검색을 요청
}
function showErrorMsg(error){ //위치찾기 오류발생
switch(error.code){
case error.PERMISSION_DENIED: alert("사용자가 사용 요청을 거부했습니다."); break;
case error.POSITION_UNAVAILABLE: alert("가져온 위치 정보를 사용할 수 없습니다."); break;
case error.TIMEOUT: alert("요청 허용 시간을 초과했습니다."); break;
case error.UNKNOWN_ERROR: alert("알 수 없는 오류가 발생했습니다."); break;
}
}
추가로 변경한 사항들
현재위치가 결정될 때, 1)지도를 띄우고, 2)초기 키워드검색이 이뤄지도록하기 위해 아래 사항들을 추가적으로 변경했다.
1. 위도, 경도, 장소검색객체, 지도담을 div, 지도생성객체, 지도 객체, 마커/검색키워드 저장소를
구글로케이터 실행 이전에 선언해준다.
var userLng,userLat, mapContainer, map, infowindow,
var ps = new kakao.maps.services.Places();
2. getLocWeather() 함수에서 현재위치가 정해질 때,
지도좌표, 지도 생성, 마커생성 로직을 입력하고, searchPlace() 함수를 실행한다.
function getLocWeather(position){ //현재 위치
userLat = position.coords.latitude; //위도
userLng = position.coords.longitude;//경도
mapContainer = document.getElementById('map'), // 지도를 표시할 div
mapOption = {
center: new kakao.maps.LatLng(userLat, userLng), // 지도의 중심좌표
level: 3 // 지도의 확대 레벨
};
map = new kakao.maps.Map(mapContainer, mapOption); //지도생성
infowindow = new kakao.maps.InfoWindow({zIndex:1}); // 검색 결과 목록, 마커를 클릭했을 때 장소명을 표출 searchPlaces("맛집");
}
3. searchPlace() 인자에 초기 키워드를 넣는다. (사용자가 키워드 입력하기 전에 초기키워드설정해줌)
searchPlaces("맛집");
html
<section>
<div>
<div class="row gx-4 gx-lg-5 justify-content-center">
<div class="col-lg-8 col-xl-6 text-center">
<h2 class="mt-0">맛집 추천</h2>
<hr class="divider" />
</div>
</div></br>
<div class="map_wrap">
<div id="map" style="width:90%;height:100%;left:5%;bottom:3%;position:relative;overflow:hidden;"></div>
<div id="menu_wrap" class="bg_white">
<div class="option">
<div>
<form onsubmit="searchPlaces(this.keyword.value); return false;">
키워드 : <input type="text" id="keyword" size="15">
<button type="submit">검색하기</button>
</form>
</div>
</div>
<hr>
<ul id="placesList"></ul>
<div id="pagination"></div>
</div>
</div>
</div>
</section>
javascript
<!--지도-->
<script>
var userLng,userLat, mapContainer, map, infowindow,
var ps = new kakao.maps.services.Places(); // 장소 검색 객체를 생성;
var markers = [];
navigator.geolocation.getCurrentPosition(getLocWeather, showErrorMsg);
function getLocWeather(position){ //현재 위치
userLat = position.coords.latitude; //위도
userLng = position.coords.longitude;//경도
mapContainer = document.getElementById('map'), // 지도를 표시할 div
mapOption = {
center: new kakao.maps.LatLng(userLat, userLng), // 지도의 중심좌표
level: 3 // 지도의 확대 레벨
};
map = new kakao.maps.Map(mapContainer, mapOption); //지도생성
infowindow = new kakao.maps.InfoWindow({zIndex:1}); // 검색 결과 목록이나 마커를 클릭했을 때 장소명을 표출할 인포윈도우를 생성
searchPlaces("맛집");
}
function showErrorMsg(error){ //위치찾기 오류발생
switch(error.code){
case error.PERMISSION_DENIED: alert("사용자가 사용 요청을 거부했습니다."); break;
case error.POSITION_UNAVAILABLE: alert("가져온 위치 정보를 사용할 수 없습니다."); break;
case error.TIMEOUT: alert("요청 허용 시간을 초과했습니다."); break;
case error.UNKNOWN_ERROR: alert("알 수 없는 오류가 발생했습니다."); break;
}
}
function searchPlaces(keyword) {// 키워드 검색 요청
if (!keyword.replace(/^\s+|\s+$/g, '')) {
alert('키워드를 입력해주세요');
return false;
}
ps.keywordSearch(keyword, placesSearchCB,{x:userLng , y:userLat}); // 장소검색 객체를 통해 키워드로 장소검색을 요청
}
function placesSearchCB(data, status, pagination) { // 장소검색이 완료됐을 때 호출되는 콜백함수
if (status === kakao.maps.services.Status.OK) {
displayPlaces(data); // 정상적으로 검색이 완료됐으면 검색 목록과 마커를 표출
displayPagination(pagination); // 페이지 번호를 표출
} else if (status === kakao.maps.services.Status.ZERO_RESULT) {
alert('검색 결과가 존재하지 않습니다.');
return;
} else if (status === kakao.maps.services.Status.ERROR) {
alert('검색 결과 중 오류가 발생했습니다.');
return;
}
}
// 검색 결과 목록과 마커를 표출하는 함수입니다
function displayPlaces(places) {
var listEl = document.getElementById('placesList'),
menuEl = document.getElementById('menu_wrap'),
fragment = document.createDocumentFragment(),
bounds = new kakao.maps.LatLngBounds(),
listStr = '';
removeAllChildNods(listEl); // 검색 결과 목록에 추가된 항목들을 제거
removeMarker(); // 지도에 표시되고 있는 마커를 제거
for ( var i=0; i<places.length; i++ ) {
var placePosition = new kakao.maps.LatLng(places[i].y, places[i].x), // 마커를 생성하고 지도에 표시
marker = addMarker(placePosition, i),
itemEl = getListItem(i, places[i]); // 검색 결과 항목 Element를 생성
bounds.extend(placePosition); // 검색된 장소 위치를 기준으로 지도 범위를 재설정하기위해 LatLngBounds 객체에 좌표를 추가
// 마커와 검색결과 항목에 mouseover 했을때 해당 장소에 인포윈도우에 장소명을 표시합니다 mouseout 했을 때는 인포윈도우를 닫습니다
(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);
}
listEl.appendChild(fragment); // 검색결과 항목들을 검색결과 목록 Element에 추가
menuEl.scrollTop = 0;
map.setBounds(bounds); // 검색된 장소 위치를 기준으로 지도 범위를 재설정
}
function getListItem(index, places) {// 검색결과 항목을 Element로 반환하는 함수
var el = document.createElement('li'),
itemStr = '<span class="markerbg marker_' + (index+1) + '"></span>' +
'<div class="info">' +
' <h5>' + places.place_name + '</h5>';
if (places.road_address_name) {
itemStr += ' <span>' + places.road_address_name + '</span>' +
' <span class="jibun gray">' + places.address_name + '</span>';
} else {
itemStr += ' <span>' + places.address_name + '</span>';
}
itemStr += ' <span class="tel">' + places.phone + '</span>' +
'</div>';
el.innerHTML = itemStr;
el.className = 'item';
return el;
}
function addMarker(position, idx, title) { // 마커를 생성하고 지도 위에 마커를 표시하는 함수
var imageSrc = 'https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/marker_number_blue.png', // 마커 이미지 url, 스프라이트 이미지 사용
imageSize = new kakao.maps.Size(36, 37), // 마커 이미지의 크기
imgOptions = {
spriteSize : new kakao.maps.Size(36, 691), // 스프라이트 이미지의 크기
spriteOrigin : new kakao.maps.Point(0, (idx*46)+10), // 스프라이트 이미지 중 사용할 영역의 좌상단 좌표
offset: new kakao.maps.Point(13, 37) // 마커 좌표에 일치시킬 이미지 내에서의 좌표
},
markerImage = new kakao.maps.MarkerImage(imageSrc, imageSize, imgOptions),
marker = new kakao.maps.Marker({
position: position, // 마커의 위치
image: markerImage
});
marker.setMap(map); // 지도 위에 마커를 표출
markers.push(marker); // 배열에 생성된 마커를 추가
return marker;
}
function removeMarker() {// 지도 위에 표시되고 있는 마커를 모두 제거
for ( var i = 0; i < markers.length; i++ ) {
markers[i].setMap(null);
}
markers = [];
}
function displayPagination(pagination) {// 검색결과 목록 하단에 페이지번호를 표시는 함수
var paginationEl = document.getElementById('pagination'),
fragment = document.createDocumentFragment(),
i;
while (paginationEl.hasChildNodes()) { // 기존에 추가된 페이지번호를 삭제
paginationEl.removeChild (paginationEl.lastChild);
}
for (i=1; i<=pagination.last; i++) {
var el = document.createElement('a');
el.href = "#";
el.innerHTML = i;
if (i===pagination.current) {
el.className = 'on';
} else {
el.onclick = (function(i) {
return function() {
pagination.gotoPage(i);
}
})(i);
}
fragment.appendChild(el);
}
paginationEl.appendChild(fragment);
}
function displayInfowindow(marker, title) {// 검색결과 목록 또는 마커를 클릭했을 때 호출되는 함수
var content = '<div style="padding:5px;z-index:1;">' + title + '</div>';
infowindow.setContent(content);// 인포윈도우에 장소명을 표시
infowindow.open(map, marker);
}
// 검색결과 목록의 자식 Element를 제거하는 함수
function removeAllChildNods(el) {
while (el.hasChildNodes()) {
el.removeChild (el.lastChild);
}
}
</script>