출발지와 도착지 마커를 모두 지도 안에 표시하는 것은 사용자 경험을 개선하기 위한 중요한 요소입니다. 마커 사이의 거리가 매번 변할 수 있기 때문에 상황에 맞는 줌 레벨을 설정하고 지도의 범위를 자동으로 조절하는 방법이 필요합니다. 이 글에서는 제가 접근한 다양한 방식을 코드와 함께 소개하고, 각 방법의 장단점을 정리해보겠습니다.
두 마커 사이의 거리를 계산하여 지도 줌 레벨을 설정하는 방법입니다. 이 방법은 두 위치 간의 거리에 따라 적절한 줌 레벨을 설정하여 사용자가 두 마커를 쉽게 볼 수 있도록 합니다.
// 두 지점 사이의 거리를 측정
export const getDistance = (lat1, lng1, lat2, lng2) => {
function deg2rad(deg) {
return deg * (Math.PI / 180);
}
const R = 6371; // 지구 반지름 (km)
const dLat = deg2rad(lat2 - lat1);
const dLon = deg2rad(lng2 - lng1);
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(deg2rad(lat1)) *
Math.cos(deg2rad(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c; // 거리 (km)
};
import React, { useEffect } from "react";
import { getDistance } from "./getDistance";
const Map = () => {
const lat1 = 37.49813943152925;
const lng1 = 127.02827170358093;
const lat2 = 37.48390185679575;
const lng2 = 127.03450754512691;
const step = [100, 50, 30, 20, 10, 5, 3, 1, 0.5, 0.3, 0.1, 0.05, 0.03, 0.02, 0.01, 0.005];
const distance = getDistance(lat1, lng1, lat2, lng2);
let zoomLevel = 0;
for (let i = 0; i < step.length; i++) {
if (distance > step[i]) {
zoomLevel = i;
break;
}
}
useEffect(() => {
const script = document.createElement("script");
script.src = `https://openapi.map.naver.com/openapi/v3/maps.js?ncpClientId=YOUR_APP_KEY`;
script.async = true;
script.onload = () => {
if (window.naver && window.naver.maps) {
const mapOptions = {
center: new window.naver.maps.LatLng(lat1, lng1),
zoom: zoomLevel,
logoControl: false,
mapDataControl: false,
scaleControl: true,
tileDuration: 200,
};
new window.naver.maps.Map("map", mapOptions);
}
};
document.head.appendChild(script);
return () => {
document.head.removeChild(script);
};
}, [zoomLevel]);
return <div id="map" style={{ width: "500px", height: "500px" }}></div>;
};
export default Map;
장단점
출발지와 도착지의 위도와 경도의 최솟값과 최댓값을 계산하여 지도를 맞추는 방법입니다. 이 방법은 지도의 범위를 정확히 설정하여 두 마커가 모두 보이도록 보장합니다.
코드 예시
import React, { useEffect } from "react";
import { getDistance } from "./getDistance";
const calculateZoomLevel = (distance, step) => {
for (let i = 0; i < step.length; i++) {
if (distance > step[i]) {
return i;
}
}
return step.length - 1;
};
const getZoomLevelForBounds = (latDiff, lngDiff, step) => {
const latDistance = getDistance(0, 0, latDiff, 0);
const lngDistance = getDistance(0, 0, 0, lngDiff);
const maxDistance = Math.max(latDistance, lngDistance);
return calculateZoomLevel(maxDistance, step);
};
const Map = ({ lat1, lng1, lat2, lng2 }) => {
const step = [
100, 50, 30, 20, 10, 5, 3, 1, 0.5, 0.3, 0.1, 0.05, 0.03, 0.02, 0.01, 0.005,
];
const latDiff = Math.abs(lat1 - lat2);
const lngDiff = Math.abs(lng1 - lng2);
const zoomLevel = getZoomLevelForBounds(latDiff, lngDiff, step);
useEffect(() => {
const script = document.createElement("script");
script.src = `https://openapi.map.naver.com/openapi/v3/maps.js?ncpClientId=YOUR_APP_KEY`;
script.async = true;
script.onload = () => {
if (window.naver && window.naver.maps) {
const mapOptions = {
center: new window.naver.maps.LatLng(lat1, lng1),
zoom: zoomLevel + 7,
logoControl: false,
mapDataControl: false,
scaleControl: true,
tileDuration: 200,
};
new window.naver.maps.Map("map", mapOptions);
}
};
document.head.appendChild(script);
return () => {
document.head.removeChild(script);
};
}, [lat1, lng1, zoomLevel]);
return <div id="map" style={{ width: "500px", height: "500px" }}></div>;
};
export default Map;
장단점
단점: 위도와 경도의 최대/최소값만을 기준으로 하기 때문에 마커가 지도의 가장자리에 배치될 수 있으며, 여백이 충분하지 않을 수 있습니다.
Navermap API의 LatLngBounds 객체를 사용하여 두 마커가 모두 보이도록 지도의 경계를 자동으로 설정하는 방법입니다. 이 방법은 매우 간편하며, 사용자 경험을 고려한 최적의 방법으로 간주됩니다.
코드 예시
import { useEffect } from "react";
const MapWithFitBounds = ({ lat1, lng1, lat2, lng2 }) => {
useEffect(() => {
const script = document.createElement("script");
script.src = `https://openapi.map.naver.com/openapi/v3/maps.js?ncpClientId=m5sz5l3xvw`;
script.async = true;
script.onload = () => {
if (window.naver && window.naver.maps) {
const bounds = new window.naver.maps.LatLngBounds(
new window.naver.maps.LatLng(lat1, lng1),
new window.naver.maps.LatLng(lat2, lng2)
);
const mapOptions = {
center: bounds.getCenter(),
zoom: 10,
logoControl: false,
mapDataControl: false,
};
const map = new window.naver.maps.Map("map", mapOptions);
map.fitBounds(bounds);
}
};
document.head.appendChild(script);
return () => {
document.head.removeChild(script);
};
}, [lat1, lng1, lat2, lng2]);
return <div id="map" style={{ width: "300px", height: "300px" }}></div>;
};
export default MapWithFitBounds;
장단점