현재 진행하는 프로젝트에서 지도를 렌더링 하여 해당 지도 영역 안에 아파트 마커를 찍어야 하는 기능을 개발해야 한다.
다만 최근 면접에서 해당 기능에 대해 "네이버지도 API를 활용하지 않고 구현하는 방법"에 대해 질문을 받았으나 제대로 답변하지 못하여 해당 방법에 대해서 한번 고민해보고 기존의 네이버 지도 API를 이용한 방법에 대해서도 간단하게 알아보자
이번 포스팅에 관련된 기능은 아래와 같다.
위와같이 현재 지도에서 보이는 영역에 아파트로 해당하는 위치를 마커를 찍어주는 기능이다.
해당 마커들은 지도가 어느 위치(좌표)냐에 따라서 마커 표시가 다르게 나타나야 한다.
좌표 데이터들은 백엔드에서 아파트에 대한 좌표 데이터를 DB에 가지고 있고 프론트가 요청시 보내주면 해당 좌표에 마커를 렌더링 시켜야 한다.
현재 아파트 좌표 데이터는 대략 40000 ~ 50000개의 데이터이다.
해당 데이터들을 한번에 받아올수도 있겠지만 해당 방법으로 한다면 한번의 API요청이 응답의 크기도 커지고 이에 따라서 응답시간도 굉장히 늦어질 것이다.
이를 해결하기 위해 기능 기획단계에서 현재 보이는 영역에만 존재하는 아파트 좌표데이터들을 백엔드에서 가져오기로 결정하였다.
그럼, 어떻게 현재 사용자에게 보이는 영역에만 해당하는 아파트 좌표 데이터만 요청 할 수 있을까?
해당 기능을 구현하기 위해서 우리는 아래와 같이 동그라미 친 "우측상단, 좌측하단" 좌표를 백엔드로 보내주면 백엔드는 해당 좌표를 통해서 지도의 "우측상단, 좌측하단"의 x,y좌표 차이를 계산하여 영역을 구하고 해당 영역에 포함되어 있는 좌표 데이터를 보내주기로 하였다.
getBounds()
메서드는 네이버지도 API에서 제공하는 메서드로 현재 렌더링 된 지도 영역의 경계 좌표 값을 반환해준다.
위와같이 네이버 지도 API로 생성한 "map"객체로 접근이 가능하다.
getBounds()
메서드는 아래와 같이 4개의 값과 해당 값에 접근이 가능한 메서드를 반환한다.
위의 4개의 값 중 우리가 사용할 값은
ne(North-East) 지도의 우측 상단
, sw(South-West) 지도의 좌측 하단
값을 사용할 것이다.
getBounds()
메서드의 반환값들 중 ne
, sw
값을 가져오기 위해서 해당 반환값들 중 해당 값에 접근이 가능한 getNE()
, getSW()
를 사용하면 해당 값들을 반환 받을 수 있다.
반환 받은 해당 값들을 백엔드에게 보내주면 백엔드는 해당 좌표영역 안에 있는 아파트 좌표 데이터들을 보내주게 된다!!
보다시피 네이버 지도 API를 이용한다면 현재 보이는 지도의 영역 좌표 데이터에 쉽게 접근이 가능해진다.
하지만, 여기서 네이버 지도 API를 이용하지 않고 좌표 데이터에 접근하는 방법에는 무엇이 있을까?
지도 API를 사용하지 않고 렌더링 한 지도의 "우측 상단", "좌측 하단"의 좌표를 구하는 방법에는 무엇이 있을까?
이를 위해 고려해야 할 점은 아래와 같다.
지도를 렌더링 하는 영역의 "너비"와 "높이"는 고정적이지 않다.(반응형)
정확도
사실 이 둘 중에 제일 중요한건 1번이라고 생각한다.
![]() | ![]() |
---|
위와같이 사용자의 뷰포트에 따라서 지도영역은 다양한 크기로 렌더링 되게 된다.
또한 줌레벨에 따라서도 "우측상단", "좌측하단"의 좌표가 다르게 정의 될 수 있다.
이를 위해 라이브러리 없이 구현한다면 결국 각 뷰포트에 대한 지도 영역의 크기에 대해서 지도 중심을 정의한 다음 단위를 정의하고 해당 단위마다 좌표값이 얼마나 이동하는지 구해야 한다.
예를 들면 x축방향으로 1px -> + 0.000001
와 같이 사전 정의가 필요한 것이다.
또한 해당 방법은 같은 너비라고 하더라도 줌 레벨에 따라서 다르게 적용될 수 있으므로 줌 레벨에 따른 사전 단위 정의를 여러개 정의해야 한다.
다만 해당 방법에서 줌 레벨을 고려하지 않고 반응형 너비를 고려하지 않는다면 즉, 특정 범위(ex. 가로세로 길이가 500m인 정사각형)와 같은 범위라면 손쉽게 구현이 가능하다고 생각한다.
위도의 경우 1도에 대략 111,000m라고 알려져 있다.
그렇다면 위도 0.0000001도의 거리는 대략 11.1cm라고 생각하면 된다.
이렇게 된다면 500m는 현재 위도에서 대략 0.0044도 정도 더해주면 된다.
즉, 현재 위치의 위도가 "37.5060304"이고 내가 있는 현재 위치를 정중앙으로 한 정사각형의 "우측상단", "좌측하단"의 좌표를 구한다면 "37.5060304 + 0.0022"인 "37.5064704"가 되는 것이다.
경도의 경우에는 위치에 따라 달라진다.
경도 1도의 거리는 적도에서는 대략 111320m x cos(위도)를 적용해야 정확한 1도의 거리가 나오게 된다.
예를 들어 위도 30도에서는 111320 x cos(30)의 결과값인 96500m가 1도의 거리가 된다.
즉, 최소단위인 0.0000001도에서는 대략 9.65cm가 된다.
현재 위치가 "위도 37.5060304, 경도 126.8496436"이라고 가정하고 현재 위치에서 가로 세로 500m인 정사각형의 중앙에 위치한다고 가정해보자.
그렇다면 정사각형의 "우측 상단", "좌측 하단"의 좌표를 구하여서 백엔드로 좌표를 보내준다고 가정하면 어떻게 해야 할까?
// 위도, 경도 상태
const [coordinates, setCoordinates] = useState({
topRight: { lat: 0, lng: 0 },
bottomLeft: { lat: 0, lng: 0 }
});
const calculateSquareCoordinates = (centerLat, centerLng, sideLength = 500) => {
//위도 계산식
const latChange = (sideLength / 2) / 111000;
// 경도 계산식
const lngChange = (sideLength / 2) / (Math.cos(centerLat * Math.PI / 180) * 111320);
const topRight = {
lat: centerLat + latChange,
lng: centerLng + lngChange
};
const bottomLeft = {
lat: centerLat - latChange,
lng: centerLng - lngChange
};
return { topRight, bottomLeft };
};
useEffect(() => {
// 예시 로케이션 or navigator.geolocation.getCurrentPosition메서드를 이용하여서 현재 위치 대입
const currentLat = 37.5060304;
const currentLng = 126.8496436;
// 좌표 계산
const result = calculateSquareCoordinates(currentLat, currentLng);
setCoordinates(result);
}, []);
const sendToBackend = async () => {
try {
// 백엔드로 전송할 데이터 준비
const data = {
topRight: {
latitude: coordinates.topRight.lat,
longitude: coordinates.topRight.lng
},
bottomLeft: {
latitude: coordinates.bottomLeft.lat,
longitude: coordinates.bottomLeft.lng
}
};
....
백엔드 통신 로직
....
}catch(error){
...
}
위와같이 다소 최적화가 덜 된 로직이지만 이러한 로직을 통해서 네이버지도 API의 도움 없이도 "우측상단", "좌측하단"의 좌표 값을 구할 수 있는 로직을 구현할 수 있다.
이번 포스팅은 지도의 특정 범위안에 있는 좌표데이터를 얻기위해 "우측상단", "좌측하단" 좌표를 구하는 방법에 대해서 알아보았다.
다만 네이버지도 API 도움 없이 구하는 방법은 줌레벨과, 지도영역 너비를 고려하지 않고 정적인 범위에 대해서 구하는 방법만 생각해봤다는게 아쉽다고 생각한다.
또한, 위에 나와있는 방법을 내 스스로 생각하지 못했다 라는 점도 굉장히 아쉽게 느껴진다.
면접에서 면접관님이 라이브러리에 우리가 필요한 기능이 정의되어 있지 않을 경우 해당 기능을 구현하는 방법에 대해 생각하는 과정을 중요하게 말씀해주셨다.
이런 과정은 단순히 반복되는 코드만 치는 개발자가 아닌 진짜 뭔가를 개발하는 개발자가 해야 하는 과정이 아닌가를 다시 한번 되새기게 해주는 말씀이였다고 생각한다.
지금까지는 서비스를 만들어 나간다는 과정 자체에 재미를 느끼고 계속 개발을 이어나가는게 원동력이었다면 앞으로는 서비스를 만들어 나간다는 과정 자체를 심도있게 생각해야한다는걸 절실히 느꼈다.
https://navermaps.github.io/maps.js.ncp/docs/tutorial-4-map-bounds.example.html
https://navermaps.github.io/maps.js.ncp/docs/naver.maps.Map.html#getBounds__anchor