전국 50개 매장 좌표를 네이버 지도 API에 단순 반복문으로 한 번에 뿌리면, 줌·드래그 시 심각한 화면 끊김(Lag)이 발생합니다.
원인은 크게 두 가지입니다. DOM 비대화와 메인 스레드 블로킹입니다.
해결책은 IntersectionObserver 기반 지연 로딩, 뷰포트 필터링, 공식 MarkerClustering 모듈 세 가지를 조합하는 것입니다.
이 글은 홈페이지 제작을 기획 중인 마케팅 담당자·경영자가 개발팀에 올바른 요구사항을 전달할 수 있도록 실무 관점에서 풀어씁니다.
프랜차이즈 본사, 전국 대리점 네트워크, 멀티숍 브랜드 홈페이지를 제작할 때 빠지지 않는 기능이 바로 매장 찾기 지도입니다.
처음에는 간단해 보입니다. 네이버 지도 API 키를 발급받고, 매장 50개의 위도·경도 좌표를 for 루프로 돌려 마커를 찍으면 끝나는 것처럼 느껴지죠.
그런데 실제로 서비스를 오픈하고 나면 이런 민원이 들어옵니다.
"지도 드래그할 때 화면이 뚝뚝 끊려요. 모바일에서는 거의 멈추는 수준이에요."
50개는 적은 숫자처럼 보이지만, 구현 방식이 잘못되면 500개와 다를 바 없이 브라우저를 괴롭힙니다.
DOM(Document Object Model)은 브라우저가 HTML을 읽어서 만드는 '화면 설계도'라고 생각하면 됩니다. 마커 하나를 지도에 추가할 때마다 이 설계도에 요소가 하나씩 추가됩니다. 설계도가 복잡해질수록 브라우저가 화면을 다시 그리는 데 걸리는 시간이 늘어납니다.
브라우저는 기본적으로 한 번에 한 가지 일만 처리합니다(메인 스레드). 지도 스크립트를 다운로드하는 동안에는 다른 화면 요소를 그리지 못하고 멈춥니다. 사용자 눈에는 "페이지가 하얗게 굳는" 현상으로 보입니다.
전국 지도를 멀리서 보면 서울에만 마커가 20개씩 겹쳐 보입니다. 클러스터링은 가까운 마커들을 "서울 20개" 같은 숫자 하나로 묶어주는 기술입니다. 줌인하면 다시 개별 마커로 펼쳐집니다. 렌더링 부하도 줄고 가독성도 높아집니다.
페이지를 열자마자 지도를 불러오는 것이 아니라, 사용자가 스크롤해서 지도 영역에 도달하는 순간에만 로딩을 시작하는 방식입니다. 지도를 보지도 않는데 300KB짜리 스크립트를 미리 내려받을 이유가 없습니다.
네이버 지도 API v3에서 커스텀 마커를 만들 때 편의상 HTML 코드를 직접 넣는 방식을 씁니다. 이 방식은 마커 하나당 실제 DOM 요소를 하나씩 생성합니다.
50개 마커 각각에 인포윈도우(말풍선 팝업)와 이벤트 리스너까지 붙이면, 브라우저는 줌을 바꿀 때마다 수백 개 요소의 위치와 크기를 전부 다시 계산합니다. 이것을 Reflow/Repaint라고 하는데, 이 과정이 반복되면 드래그 한 번에 프레임이 10~15fps까지 떨어집니다. 매끄러운 화면이 60fps라는 점을 감안하면 사실상 슬라이드쇼 수준입니다.
네이버 지도 API 스크립트는 300KB를 넘습니다.
이것을 <head> 태그 안에 동기식으로 넣으면, 스크립트가 완전히 다운로드·파싱될 때까지 페이지 나머지 요소가 전혀 그려지지 않습니다.
여기에 50개 매장 데이터를 서버에서 가져오는 API 호출 시간, 지도 초기화, 마커 렌더링까지 한꺼번에 맞물리면 초기 로딩이 수 초씩 지연됩니다. Google의 핵심 성능 지표인 INP(Interaction to Next Paint) 점수도 함께 추락합니다.
사용자가 지도 영역 근처에 스크롤하기 전까지는 스크립트를 아예 로드하지 않습니다.
IntersectionObserver는 특정 요소가 화면에 보이기 시작하는 순간을 감지하는 브라우저 내장 API입니다.
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
loadNaverMapScript();
observer.disconnect();
}
},
{ rootMargin: '100px' }
);
observer.observe(document.getElementById('map-container'));
rootMargin: '100px'은 지도가 화면에 완전히 들어오기 100px 전부터 미리 로딩을 시작하라는 뜻입니다.
사용자가 스크롤하는 속도보다 빠르게 준비되므로 체감 대기 시간이 거의 없습니다.
실무 포인트: 지도 영역이 보이기 전까지는 가벼운 정적 이미지(Static Map 스크린샷)나 회색 플레이스홀더를 보여주세요. 사용자는 "지도가 곧 뜨겠구나"를 자연스럽게 인지합니다.
서울을 보고 있을 때 부산·광주 매장 마커를 그릴 필요가 전혀 없습니다.
map.getBounds()로 현재 지도의 경계 영역을 가져와서, 그 안에 있는 좌표만 마커로 활성화하고 나머지는 setMap(null)로 메모리에서 내립니다.
naver.maps.Event.addListener(map, 'idle', () => {
const bounds = map.getBounds();
markers.forEach((marker) => {
const inView = bounds.hasLatLng(marker.getPosition());
marker.setMap(inView ? map : null);
});
});
반드시 'idle' 이벤트를 사용하세요.
'bounds_changed' 이벤트를 쓰면 드래그 1픽셀마다 이 함수가 수십 번 실행되어 CPU가 폭주합니다.
'idle'은 드래그와 줌이 완전히 멈춘 뒤 딱 한 번만 실행됩니다.
전국 지도로 축소(Zoom-out)했을 때 50개 마커가 한 화면에 다닥다닥 붙으면 가독성도 나쁘고 렌더링 부하도 심해집니다.
네이버 지도 공식 깃허브(navermaps/marker-tools.js)에서 MarkerClustering.js를 내려받아 적용합니다.
const markerClustering = new MarkerClustering({
minClusterSize: 2,
maxZoom: 13,
map: map,
markers: markers,
disableClickZoom: false,
gridSize: 120,
stylingFunction: function(clusterMarker, count) {
clusterMarker.getElement().querySelector('div').textContent = count;
}
});
minClusterSize: 2 → 2개 이상 겹칠 때 묶음 표시maxZoom: 13 → 줌 레벨 13 이하에서만 클러스터 작동gridSize: 120 → 120픽셀 반경 안의 마커를 하나로 묶음홈페이지 제작 전 개발팀 또는 외주 업체에 아래 항목을 확인하세요.
<head> 동기 로딩인가, IntersectionObserver 지연 로딩인가MarkerClustering 모듈 사용 여부bounds_changed(비효율) 대신 idle 이벤트 사용 여부Q1. 매장이 50개밖에 없는데 클러스터링까지 해야 하나요?
50개라도 전국 지도로 축소하면 수도권에 20개 이상이 한 화면에 겹칩니다. 클러스터링 없이는 마커가 서로 가려져 사용자가 클릭조차 못 합니다. 또한 향후 매장이 늘어날 때 구조를 다시 짜는 비용이 훨씬 크므로 처음부터 적용하는 것이 경제적입니다.
Q2. 지연 로딩을 적용하면 지도가 느리게 뜨지 않나요?
rootMargin 옵션으로 사용자가 지도 영역에 도달하기 100~200px 전에 미리 로딩을 시작하므로, 체감 대기 시간은 거의 없습니다.
오히려 초기 페이지 로딩이 빨라져 전체적인 사용자 경험이 개선됩니다.
Q3. 네이버 지도 API 비용이 걱정됩니다.
네이버 클라우드 플랫폼의 Dynamic Map API는 호출 횟수 기준으로 과금됩니다. 지연 로딩을 적용하면 지도를 보지 않는 사용자의 API 호출 자체가 발생하지 않으므로 비용 절감에 직접적으로 도움이 됩니다. 상세 페이지에 '지도 보기' 버튼을 두어 클릭 시에만 지도를 로드하는 UX도 비용 관리에 효과적입니다.
Q4. React나 Vue 같은 프레임워크를 쓰면 구현이 달라지나요?
기본 원리는 동일합니다. React에서는 useEffect와 useRef를 조합해 IntersectionObserver를 구현하고, Vue에서는 mounted 훅과 $el을 활용합니다.
다만 컴포넌트 언마운트 시 지도 인스턴스와 이벤트 리스너를 반드시 정리(cleanup)해야 메모리 누수를 막을 수 있습니다.
Q5. 홈페이지 제작 업체에 이 내용을 어떻게 요구사항으로 전달하나요?
"네이버 지도 API 지연 로딩, 뷰포트 마커 필터링, MarkerClustering 적용"이라는 세 가지 키워드를 제안요청서(RFP)에 명시하세요. 이 세 가지를 아무런 설명 없이 바로 이해하는 업체라면 지도 기능 구현 경험이 있다고 판단해도 좋습니다.
매장 찾기 지도는 단순한 기능처럼 보이지만, 구현 방식 하나로 홈페이지 전체의 성능과 사용자 경험이 갈립니다.
세 가지만 기억하세요.
setMap(null)로 내리고, idle 이벤트로 제어한다.MarkerClustering 모듈로 겹치는 마커를 묶어 렌더링 부하를 줄인다.이 세 가지를 처음 설계 단계에서 반영하면, 나중에 매장이 100개·200개로 늘어나도 구조를 다시 짜지 않아도 됩니다. 반대로 처음에 "일단 동작하게만" 만들어두면, 나중에 뜯어고치는 비용이 처음 제대로 만드는 비용의 3~5배를 넘는 경우가 흔합니다.
홈페이지 제작을 기획 중이라면, 지도 기능 하나에도 이런 기술적 판단이 필요합니다. 에이달은 전국 다점포 브랜드의 매장 찾기 기능부터 예약 연동, 성능 최적화까지 실무 경험을 바탕으로 설계합니다.
구체적인 요구사항이 있거나 현재 운영 중인 지도 기능의 성능이 걱정된다면, 부담 없이 무료 컨설팅을 요청해 주세요.
📞 02-2664-8631 | ✉️ master@adall.co.kr 에이달(ADALL) | 서울 강서구 방화대로31길 2, 5~6층
무료 컨설팅 받아보고 싶다면?
무료 컨설팅 신청하기