참고
https://www.youtube.com/watch?v=EydLtNIqYkw&t=110s
https://apis.map.kakao.com/web/sample/keywordBasic/
Kakao Map API 에서 제공하는 키워드로 장소 검색 기능을 사용할 수 있다.
api library 를 사용하는 것이기 때문에
index.html
파일의 script 부분에 &libraies=services
를 추가한다.
Kakao Map API 문서에서 키워드로 장소검색하기
에 나와있는 예제 코드를 보면 장소 검색 객체를 생성하고 그 키워드로 keywordSearch
메서드를 이용해 장소를 검색할 수 있다. 첫번째 매개변수는 검색하고자 하는 장소이고, 두번째 매개변수는 호출되는 콜백함수로 각각이 가리키는 data
, status
, pagination
을 출력해보니 다음과 같았다.
여객 터미널을 검색했더니 data는 장소 검색된 배열의 값으로, status 는 결과값에 따른 상태값, pagination 은 그 배열과 관련된 페이지네이션 정보와 같았다.
이런 정보를 가지고 App.vue
폴더에 필요한 코드를 삽입해보겠다.
<div class="searchbox">
<div>
<input type="text" value="여객 터미널" @keyup.enter="serachPlace" />
</div>
</div>
먼저, html 부분에 검색하고자 하는 영역과 input 을 생성하여 검색하고자 하는 값을 enter 키보드 이벤트 핸들러를 추가한다.
data() {
return {
mapOption: {
center: {
lat: 33.450701,
lng: 126.570667,
},
level: 8,
},
search: {
keyword: null,
pgn: null,
results: [],
},
}
},
methods: {
serachPlace(e) {
const keyword = e.target.value.trim();
if (keyword.length === 0) return;
const ps = new window.kakao.maps.services.Places();
ps.keywordSearch(keyword, (data, status, pgn) => {
this.search.keyword = keyword;
this.search.pgn = pgn;
this.search.results = data;
});
},
}
enter 키보드 이벤트 함수는 input의 e.target.value 값을 받아와 keyword 로 저장한다.
앞서 문서에서 살펴본 예제와 같이 장소 검색 객체를 생성하고 키워드와 콜백함수를 이용해 변수 search 에 해당하는 값들을 저장시킨다.
<div class="searchbox">
<div>
<input type="text" value="여객 터미널" @keyup.enter="serachPlace" />
</div>
<div class="results">
<div class="place" v-for="rs in search.results" :key="rs.id">
<h4>{{ rs.place_name }}</h4>
<div class="addr">{{ rs.address_name }}</div>
</div>
</div>
</div>
html 부분에 키워드 검색 영역과 검색 결과 영역(place_name 과 address_name)을 추가한다.
result 배열의 각각의 값은 다음과 같다.
.map-area {
display: flex;
position: relative;
.searchbox {
position: absolute;
top: 0;
left: 0;
height: 600px;
z-index: 10000;
background-color: #ffffffaa;
width: 300px;
display: flex;
flex-direction: column;
.results {
flex: 1 1 auto;
overflow-y: auto;
.place {
padding: 8px;
cursor: pointer;
&:hover {
background-color: rbga(240, 248, 255, 0.657);
}
h4 {
margin: 0;
}
}
}
}
클래스명에 맞게 css 도 설정해준다.
'여객 터미널' 이라는 검색어를 enter 키를 통해 입력하면 결과값들을 목록으로 한 눈으로 보여진다.
장소 클릭으로 화면 이동은 첫번째 Kakao Map API 에서 다루었던 내용이다.
mapOption의 center 중심 좌표를 새로 설정하면 되는 것이었다.
<div class="results">
<div
class="place"
v-for="rs in search.results"
:key="rs.id"
@click="showPlace(rs)"
//클릭 이벤트 추가
>
<h4>{{ rs.place_name }}</h4>
<div class="addr">{{ rs.address_name }}</div>
</div>
</div>
</div>
키워드 검색 결과 영역에 click 클릭 이벤트 핸들러를 추가시켰다.
showPlace(place) {
this.mapOption.center = {
lat: place.y,
lng: place.x,
};
showPlace 함수는 place 매개변수를 받아 place.y 와 place.x 값을 통해 위도와 경도를 새로 할당시킴으로써 지도의 중심을 바꾸어준다.
목록으로 불러온 값들 중 하나하나의 값을 클릭시 오른쪽의 지도의 중심이 변화되는 것을 볼 수 있다.
<template>
<div>
<h3>kakao map demo(center, level)</h3>
<div class="controll">
<button @click="zoom(1)">
<span class="material-symbols"> zoom_in </span>
</button>
<button @click="zoom(-1)">
<span class="material-symbols"> zoom_out </span>
</button>
</div>
<div class="map-area">
<!-- <div class="harbors">
<div
class="harbor"
v-for="hbr in harbors"
:key="hbr.seq"
@click="showOnMap(hbr)"
:class="{ active: hbr === activeHarbor }"
>
<h3>{{ hbr.place }}</h3>
</div>
</div> -->
<div class="searchbox">
<div>
<input type="text" value="여객 터미널" @keyup.enter="serachPlace" />
</div>
<div class="results">
<div
class="place"
v-for="rs in search.results"
:key="rs.id"
@click="showPlace(rs)"
>
<h4>{{ rs.place_name }}</h4>
<div class="addr">{{ rs.address_name }}</div>
</div>
</div>
</div>
<KakaoMap ref="kmap" class="kmap" :options="mapOption">
<div ref="harborOverlay" class="overlay-popup">
<h3>오버레이 화면</h3>
<div>이 곳에 항구 정보를 표시함</div>
</div>
</KakaoMap>
</div>
</div>
</template>
<script>
import KakaoMap from "./components/map/KakaoMap.vue";
import api from "./service/api";
import MarkerHandler from "./components/map/marker-handler";
import KakaoOverlay from "./components/map/overlay";
export default {
components: {
KakaoMap,
},
data() {
return {
mapOption: {
center: {
lat: 33.450701,
lng: 126.570667,
},
level: 8,
},
search: {
keyword: null,
pgn: null,
results: [],
},
// harbors: [],
// markers: null, //marker handler
// activeHarbor: null, //selected harbor
// overlay: null, //overlay instance
};
},
mounted() {
const vueKakoMap = this.$refs.kmap;
const harborOverlay = this.$refs.harborOverlay;
this.markers = new MarkerHandler(vueKakoMap, {
markerClicked: (harbor) => {
// console.log("[clicked ]", harbor);
this.showOnMap(harbor);
this.overlay.showAt(harbor.lat, harbor.lng);
},
});
console.log(harborOverlay);
// this.overlay = new KakaoOverlay(vueKakoMap, this.$refs.harborOverlay);
this.overlay = new KakaoOverlay(vueKakoMap, harborOverlay);
api.harbor.all((res) => {
// console.log("[항구목록]", res.harbors);
this.harbors = res.harbors;
//create markers
this.markers.add(this.harbors, (harbor) => {
return { lat: harbor.lat, lng: harbor.lng };
});
});
},
methods: {
serachPlace(e) {
const keyword = e.target.value.trim();
if (keyword.length === 0) return;
const ps = new window.kakao.maps.services.Places();
ps.keywordSearch(keyword, (data, status, pgn) => {
this.search.keyword = keyword;
this.search.pgn = pgn;
this.search.results = data;
});
},
zoom(delta) {
const level = Math.max(3, this.mapOption.level + delta);
this.mapOption.level = level;
// console.log("zoom", this.mapOption.level);
},
showOnMap(harbor) {
this.activeHarbor = harbor;
// console.log(harbor);
this.mapOption.center = {
lat: harbor.lat,
lng: harbor.lng,
};
},
showPlace(place) {
this.mapOption.center = {
lat: place.y,
lng: place.x,
};
},
},
};
</script>
<style lang="scss">
button {
border: 1px solid transparent;
padding: 6px;
background-color: #efefefdd;
border-radius: 6px;
&:hover {
background-color: #ddd;
border-color: #ddd;
cursor: pointer;
}
&:active {
background-color: #aaa;
border-color: #aaa;
}
}
.map-area {
display: flex;
position: relative;
.searchbox {
position: absolute;
top: 0;
left: 0;
height: 600px;
z-index: 10000;
background-color: #ffffffaa;
width: 300px;
display: flex;
flex-direction: column;
.results {
flex: 1 1 auto;
overflow-y: auto;
.place {
padding: 8px;
cursor: pointer;
&:hover {
background-color: rbga(240, 248, 255, 0.657);
}
h4 {
margin: 0;
}
}
}
}
.harbors {
.harbor {
padding: 10px;
border: 1px solid transparent;
&:hover {
background-color: aliceblue;
border-color: #6a9dff;
cursor: pointer;
}
&:active {
background-color: rgb(166, 197, 224);
border-color: #4471c5;
}
&.active {
background-color: rgb(253, 229, 150);
border-color: rgb(211, 173, 3);
}
h4 {
margin: 0;
}
}
}
.kmap {
flex: 1 1 auto;
.overlay-popup {
background-color: #ffffffcc;
box-shadow: 0 0 8px #0000004d, 0 0 1px 2px #00000022;
max-width: 200px;
min-width: 160px;
h3 {
margin: 0;
padding: 8px;
background-color: #ed4215;
color: white;
font-weight: 400;
font-size: 16px;
}
.addr {
padding: 8px;
white-space: break-spaces;
}
}
}
}
</style>
라이브러리를 사용할 수 있다는 것에서 카카오는 많은 것을 하게끔 도와주는 것을 알았다. 오늘 step 2 에서 지도 중심이 변화되는 것을 배웠는데, 이를 토대로 해당 장소를 클릭하면 지도의 중심이 변화되면서 마커 또한 생성이 되게 추가로 다루어 보고 싶다.
showPlace(place) {
this.mapOption.center = {
lat: place.y,
lng: place.x,
};
this.markers.add(this.search.results, (result) => {
console.log("[result]", result);
return { lat: result.y, lng: result.x };
});
},
showPlace 함수 내에서 검색한 키워드의 장소들을 markers 에 add 메서드를 통해 장소들을 저장한 배열들을 이용해 지도에 표시되게 하였다.