Naver Map API를 활용하여 4천 개의 식당 위치를 지도위에 마커로 표시하였다. 그런데 브라우저가 버벅거리는 현상이 발생하였다.. 도저히 지도를 이용하기 어려울 정도의 속도였다.
코드 구현
const response = await fetch('/api/restaurants');
const restaurants = await response.json();
for (const restaurant of restaurants) {
const lat = restaurant.lat;
const lng = restaurant.lng;
const marker = new naver.maps.Marker({
position: new naver.maps.LatLng(lat, lng),
map: map
});
}
Naver Map API v3 공식문서를 확인해보면 아래와 같이 나와있다.
지도 위에 오버레이가 많이 올라갈수록 브라우저의 그래픽 리소스를 많이 사용하게 되고 그만큼 성능이 느려질 수 밖에 없습니다. 따라서 가능한 한 적은 수의 오버레이만 지도 위에 올리는 것이 성능에 도움이 됩니다.
코드 구현
const response = await fetch('/api/restaurants');
const restaurants = await response.json();
const markers = [];
for (const restaurant of restaurants) {
const lat = restaurant.lat;
const lng = restaurant.lng;
const marker = new naver.maps.Marker({
position: new naver.maps.LatLng(lat, lng),
});
markers.push(marker);
}
naver.maps.Event.addListener(map, 'zoom_changed', () => {
updateMarkers(map, markers);
});
naver.maps.Event.addListener(map, 'dragend', () => {
updateMarkers(map, markers);
});
function updateMarkers(map, markers) {
const mapBounds = map.getBounds();
for (const marker of markers) {
const position = marker.getPosition();
if (mapBounds.hasLatLng(position)) {
showMarker(map, marker);
} else {
hideMarker(marker);
}
}
}
function showMarker(map, marker) {
marker.setMap(map);
}
function hideMarker(marker) {
marker.setMap(null);
}
어느 정도 최적화가 되었지만, 지도를 축소할수록 마커 수가 많아져서 또다시 성능문제가 발생했다.. 이를 해결하기 위해 클러스터링 기능을 사용해보자.
Naver Map API v3 공식문서를 확인해보면 아래와 같이 나와있다.
지도 위에 많은 마커가 표시될 때 마커를 그룹화하여 나타냅니다.
먼저 Naver Map API v3 공식문서를 보면 GitHub Repository에서 MarkerClustering.js와 이미지를 다운로드 받아야 된다. 그리고 jQuery 구문을 포함하고 있다곤 하는데... 필자는 jQuery 구문을 JavaScript로 바꿔서 작성했다.
<head>
...
<script type="text/javascript" src="./MarkerClustering.js"></script>
...
</head>
const response = await fetch('/api/restaurants');
const restaurants = await response.json();
const markers = [];
for (const restaurant of restaurants) {
const lat = restaurant.lat;
const lng = restaurant.lng;
const marker = new naver.maps.Marker({
position: new naver.maps.LatLng(lat, lng),
});
markers.push(marker);
}
const htmlMarker1 = {
content: `<div style="cursor:pointer;width:40px;height:40px;line-height:42px;font-size:10px;color:white;text-align:center;font-weight:bold;background:url('./img/cluster-marker-1.png');background-size:contain;"></div>`,
size: N.Size(40, 40),
anchor: N.Point(20, 20),
};
const htmlMarker2 = {
content: `<div style="cursor:pointer;width:40px;height:40px;line-height:42px;font-size:10px;color:white;text-align:center;font-weight:bold;background:url('./img/cluster-marker-2.png');background-size:contain;"></div>`,
size: N.Size(40, 40),
anchor: N.Point(20, 20),
};
const htmlMarker3 = {
content: `<div style="cursor:pointer;width:40px;height:40px;line-height:42px;font-size:10px;color:white;text-align:center;font-weight:bold;background:url('./img/cluster-marker-3.png');background-size:contain;"></div>`,
size: N.Size(40, 40),
anchor: N.Point(20, 20),
};
const htmlMarker4 = {
content: `<div style="cursor:pointer;width:40px;height:40px;line-height:42px;font-size:10px;color:white;text-align:center;font-weight:bold;background:url('./img/cluster-marker-4.png');background-size:contain;"></div>`,
size: N.Size(40, 40),
anchor: N.Point(20, 20),
};
const htmlMarker5 = {
content: `<div style="cursor:pointer;width:40px;height:40px;line-height:42px;font-size:10px;color:white;text-align:center;font-weight:bold;background:url('./img/cluster-marker-5.png');background-size:contain;"></div>`,
size: N.Size(40, 40),
anchor: N.Point(20, 20),
};
const markerClustering = new MarkerClustering({
minClusterSize: 2,
maxZoom: 10,
map: map,
markers: markers,
disableClickZoom: false,
gridSize: 120,
icons: [
htmlMarker1,
htmlMarker2,
htmlMarker3,
htmlMarker4,
htmlMarker5,
],
indexGenerator: [10, 100, 200, 500, 1000],
stylingFunction: (clusterMarker, count) => {
clusterMarker
.getElement()
.querySelector('div:first-child').innerText = count;
},
});