반응형
구현 기능
- kakaomap 에서 지원해 주는 Maps API의 Web API를 이용
- 나의 위치 정보를 가져와 지도 중심좌표를 생성
- db에 저장되어 있는 업체들의 주소를 불러와 맵에 마커를 표시한다.
kakaoMap Maps API 이용 방법
- kakao developers에 접속하여 애플리케이션을 추가, 플랫폼 web을 선택하여 사용한다.
- web에 사용할 것이기 때문에 발급된 JavaScript Key를 복사하여 카카오 지도를 나타내고자 하는 HTML에 추가한다.
<script
type="text/javascript"
src="//dapi.kakao.com/v2/maps/sdk.js?appkey=yourkey&libraries=services,clusterer,drawing">
</script>
위치 정보 가져오기
- 카카오 맵에서 제공하는 geolocation 기능을 이용하면 위치 정보를 받아 올 수 있다.
function currentLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (position) {
// Geolocation 접속 위치 불러오기
let lat = position.coords.latitude; //위도
let lon = position.coords.longitude; //경도
let mapContainer = document.getElementById('map'), // 지도를 표시할 div
mapOption = {
center: new kakao.maps.LatLng(lat, lon), // 지도의 중심좌표
level: 5, // 지도의 확대 레벨
};
const map = new kakao.maps.Map(mapContainer, mapOption); // 지도를 생성합니다
...
- 함수 currentLocation()을 생성 후 geolocation을 이용하여 현재 위치를 받아온다.
- 위치정보 권한을 수락하면 내 위치를 중심으로 한 지도가 생성된다.
db에 저장된 comapny 정보 가져온 후 지도에 마커 찍어주기
axios.get('/api/company').then(function (response) {
const result = response.data;
function currentLocation() { ...
- axios를 이용하여 db에 저장되어 있는 정보를 불러와 result 변수에 저장하였다.
let positions = [{ content: `<div">내 위치</div>`, lating: new kakao.maps.LatLng(lat, lon) }];
for (const company of result) {
geocoder.addressSearch(company.map, function (address, status) {
if (status === kakao.maps.services.Status.OK) {
positions.push({
content: `<div style="text-align:center;">${company.companyName}</div>`,
lating: new kakao.maps.LatLng(address[0].y, address[0].x),
});
}
for (let i = 1; i <= positions.length; i++) {
let marker = new kakao.maps.Marker({
map: map, // 마커를 표시할 지도
position: positions[i - 1].lating, // 마커의 위치
});
let infowindow = new kakao.maps.InfoWindow({
content: positions[i - 1].content, // 인포윈도우에 표시할 내용
});
kakao.maps.event.addListener(marker, 'mouseover', makeOverListener(map, marker, infowindow));
kakao.maps.event.addListener(marker, 'mouseout', makeOutListener(infowindow));
}
});
}
...
- geocoder.addressSearch() 함수는 인수 address에 company.map의 정보를 불러온다.
- address에 company.map의 정보가 정상적으로 들어 갔으면 status가 true가 되며 positions 객체에 정보를 넣는다.
- positions 에는 마커에 들어갈 정보들을 객체형태로 배열에 저장하였다.
- positions의 [0] 번째 저장된 정보는 내 위치에 해당하는 마커 정보로 geolocation을 통해 구한
위도와 경도의 정보가 들어있다.
문제 발생 및 문제 해결
- geocoder.addressSearch() 함수는 비동기 함수이기 때문에 모든 주소 검색 요청이
병렬도 시작되어 결과가 순서대로 반환되지 않는다. - 따라서 positions 배열에 올바른 순서로 데이터를 채우지 못하는 경우가 발생하여,
정상적으로 for문을 통해 positions 배열에 객체를 push 해도 누락되는 데이터가 발생한다. - 오류 내용 : Uncaught (in promise) TypeError: Cannot read properties of undefined
문제 해결 1 - 실패
- 비동기 함수를 순서대로 실행하게 하기 위해
- asnyc, await을 사용하였다.
function currentLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(async function (position) {
// Geolocation 접속 위치 불러오기
....
await geocoder.addressSearch(company.map, function (address, status) { ...
- 하지만 geocoder.addressSearch 함수는 비동기 함수이기 때문에
사용해도 요청이 병렬도 시작되기 때문에 효과가 없었다.
문제 해결 2 - 성공
- for..of 반복문을 사용하여 비동기 요청을 순차적으로 실행시켰다.
for (const company of result) {
await geocoder.addressSearch(company.map, function (address, status) {
if (status === kakao.maps.services.Status.OK) {
positions.push({
content: `<div style="text-align:center;">${company.companyName}</div>`,
lating: new kakao.maps.LatLng(address[0].y, address[0].x),
});
console.log(positions.length);
}
for (let i = 1; i <= positions.length; i++) {
let marker = new kakao.maps.Marker({
map: map, // 마커를 표시할 지도
position: positions[i - 1].lating, // 마커의 위치
});
let infowindow = new kakao.maps.InfoWindow({
content: positions[i - 1].content, // 인포윈도우에 표시할 내용
});
kakao.maps.event.addListener(marker, 'mouseover', makeOverListener(map, marker, infowindow));
kakao.maps.event.addListener(marker, 'mouseout', makeOutListener(infowindow));
}
});
}
- 각 주소 검색 요청을 완료할 때가지 기다려지기 때문에 비동기 요청이여도 순차적으로 실행하여
올바른 순서로 데이터를 처리할 수 있었고, positions 배열에 데이터가 정상적으로 추가되었다. - console.log 를 통해 positions 배열의 길이를 확인하였다.
- for 반복문을 이용했을 때는 순서가 뒤죽박죽이거나, 위의 오류 메세지가 발생했는데
for .. of 반복문을 통해 해결된 모습이다.
전체 코드 ( JS )
document.addEventListener('DOMContentLoaded', function () {
axios.get('/api/company').then(function (response) {
const result = response.data;
function currentLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(async function (position) {
// Geolocation 접속 위치 불러오기
let lat = position.coords.latitude; //위도
let lon = position.coords.longitude; //경도
let mapContainer = document.getElementById('map'), // 지도를 표시할 div
mapOption = {
center: new kakao.maps.LatLng(lat, lon), // 지도의 중심좌표
level: 5, // 지도의 확대 레벨
};
const map = new kakao.maps.Map(mapContainer, mapOption); // 지도를 생성합니다
const geocoder = new kakao.maps.services.Geocoder(); // 주소 좌표 변환 객체 생성
let positions = [{ content: `<div">내 위치</div>`, lating: new kakao.maps.LatLng(lat, lon) }];
for (const company of result) {
await geocoder.addressSearch(company.map, function (address, status) {
if (status === kakao.maps.services.Status.OK) {
positions.push({
content: `<div style="text-align:center;">${company.companyName}</div>`,
lating: new kakao.maps.LatLng(address[0].y, address[0].x),
});
//console.log(positions.length);
}
for (let i = 1; i <= positions.length; i++) {
let marker = new kakao.maps.Marker({
map: map, // 마커를 표시할 지도
position: positions[i - 1].lating, // 마커의 위치
});
let infowindow = new kakao.maps.InfoWindow({
content: positions[i - 1].content, // 인포윈도우에 표시할 내용
});
kakao.maps.event.addListener(marker, 'mouseover', makeOverListener(map, marker, infowindow));
kakao.maps.event.addListener(marker, 'mouseout', makeOutListener(infowindow));
}
});
}
function makeOverListener(map, marker, infowindow) {
return function () {
infowindow.open(map, marker);
};
}
// 인포윈도우를 닫는 클로저를 만드는 함수입니다
function makeOutListener(infowindow) {
return function () {
infowindow.close();
};
}
});
}
return true;
}
currentLocation(); // 지도 불러오기
});
});
반응형