google map api에서는 사용자가 오버레이 영역을 지정할 수 있는 클래스(OverlayView)를 제공합니다. 이 기능에 관심을 가지고 찾아보게 된 계기는 지도에 뿌려지는 마커를 커스터마이징 하기 위해 이래저래 시도하던 중, google map api에서 제공하는 기본 마커에는 커스터마이징의 범위가 제한적인 것을 알게 되었기 때문입니다.

사용자 지정 오버레이

google map API에서 기본으로 제공하는 마커는 css class를 제어할 수 있는 방법이 없을 뿐 아니라, 해당 DOM 엘리먼트에 접근하는 기능도 제공하고 있지 않아서 사용자 입맛에 맞게 수정하기가 어려웠습니다.

우회해서 접근할 수 있는 방법이 있긴 하지만, 꼼수라는 생각을 지울 수 없었습니다.. 자세히 보기

OverlayView 클래스를 이용하면 직접 오버레이 영역에 접근해서 마커를 생성 할 수 있습니다. 이 기능은 구글 공식 문서를 참고하셔도 됩니다. 이 글에서는 해당 기능을 마커를 커스터마이징 하는 관점에서 좀 더 쉽게 작성해보려고 합니다.

오버레이 객체 만들기

1
MyOverlay.prototype = new google.maps.OverlayView();

자 이제 시작입니다. MyOverlay라는 생성자 함수의 프로토타입에 google.maps.OverlayView클래스의 인스턴스를 할당 했습니다. 그러면 MyOverlay를 통해 생성되는 객체는 google.maps.OverlayView의 프로퍼티와 메소드들을 상속받게 되겠죠. 이 부분이 잘 이해가 되지 않으신다면 JavaScript의 프로토타입에 대해서 살펴보시길 바랍니다.

헌데 우리는 아직 MyOverlay 생성자 함수를 정의하지 않았습니다. 순서상으로는 MyOverlay를 먼저 정의해야 합니다. 다시 소스를 작성해 보면…

1
2
3
4
5
6
7
8
9
function MyOverlay({position, label, map}) {
this.position_ = position;
this.label_ = label;
this.img_ = 'https://s.zigbang.com/v1/web/search/marker.png';
this.cssClass_ = 'subway_marker';
this.div_ = null;
this.setMap(map);
}
MyOverlay.prototype = new google.maps.OverlayView();

생성자 함수를 선언했습니다. 인자는 3개를 받습니다. position은 마커의 좌표값(lat, lng)입니다. label은 마커에 적힐 text이고, map은 마커가 찍힐 지도를 참조합니다. 나머지 변수들은 나중에 마커를 생성할때 사용하기 위해서 미리 생성해 둔 것입니다.
마지막에 MyOverlay함수를 통해서 객체가 생성될 때, this.setMap(map) 함수가 실행 되면서 지도(map)에 마커가 표시되는 것입니다.

위 설정만으로는 실제 마커가 찍히지 않습니다. MyOverlay의 프로토타입 프로퍼티에 onAdd, draw, onRemove 메소드를 정의해야 합니다.

  • onAdd()는 지도에 오버레이를 연결합니다. OverlayView.onAdd()는 지도가 오버레이를 연결할 준비가 되면 호출됩니다.
  • draw()는 객체의 시각적 디스플레이를 처리합니다. OverlayView.draw()는 객체가 먼저 표시되면 호출됩니다.
  • onRemove()는 오버레이 내에 추가한 요소를 삭제 합니다.

간단한 소스를 작성해 보면..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
MyOverlay.prototype.onAdd = function() {
var div = document.createElement('div');
var img = document.createElement('img');
var txt = document.createElement('div');
div.className = this.cssClass_;
img.src = this.img_;
txt.innerHTML = this.label_;
div.appendChild(img);
div.appendChild(txt);
this.div_ = div;
var panes = this.getPanes();
panes.overlayMouseTarget.appendChild(div);
};
MyOverlay.prototype.draw = function() {
var overlayProjection = this.getProjection();
var sw = overlayProjection.fromLatLngToDivPixel(this.center_);
var div = this.div_;
div.style.left = Math.round(sw.x) + 'px';
div.style.top = Math.round(sw.y) + 'px';
};
MyOverlay.prototype.onRemove = function() {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
};

onAdd 메소드는 객체(마커)를 생성하고 스타일을 정의합니다. 여기서 <div>엘리먼트를 생성하고 그안에 <img>엘리먼트와 <div>엘리먼트를 추가합니다. 여기 개발자는 원하는대로 마커를 구성할 수 있습니다. css class도 추가 할 수 있고, 원하는 대로 엘리먼트도 구성할 수 있습니다.

여기서 var panes = this.getPanes();는 지도에 다양한 계층이 쌓이는 순서를 지정합니다. 예제에서는 overlayMouseTarget에 마커를 추가했습니다. overlayMouseTarget는 마커와 같은 DOM 마우스 이벤트를 수신하는 요소를 포함시킬 수 있습니다. 다른 창들에 대한 설명은 공식문서를 참고하세요.

그리고 draw 메소드는 지도상에서 마커의 위치를 잡습니다.

오버레이 그리기

오버레이를 그려봅시다.

1
2
3
4
5
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 11,
center: {lat: 62.323907, lng: -150.109291}
});
var overlay = new MyOverlay(new google.maps.LatLng(62.281819, -150.287132), '텍스트', map);

끝입니다! MyOverlay 당연히 생성자 함수를 여러번 호출해서 마커를 여러개 그릴수도 있습니다.

그리고 이렇게 생성한 마커는 google API가 제공하는 마커보다 상속받는 변수나 메소드가 적습니다. 그 말은 사용하지 않는 메소드는 상속받지 않을 수 있다는 것입니다. 예를들어 클릭할 필요가 없는 마커인데 클릭 메소드가 있을필요는 없겠죠. 따라서 사용자가 생성한 오버레이는 더 가볍게 마커를 생성할 수 있습니다.