Ajax로 다중 마커와 함께 커스텀 오버레이를 생성 시 문제가 생겨서 문의드립니다

안녕하세요. 현재 웹 프로젝트를 만들고 있는 초급 개발자입니다.
이번에 카카오맵 API를 활용하면서 중간에 막히는 것이 있어서 질문 드립니다.

우선 구현하고자 하는 기능은

  1. 메인 페이지 접속 시, DB에 저장된 모든 마을들의 주소를 가져와서 지도 상에 마커로 표시한다.
  2. 검색어로 특정 마을을 검색 시, 전체 마커를 숨긴 뒤 AJAX를 이용하여 특정 마을의 위치만 다시 마커로 표시한다.
  3. 검색어가 없다면, 1번과 같이 모든 마을의 위치를 지도 상에 마커로 표시한다.
  4. 검색 결과 표시된 각각의 마커 클릭 시 각 마을에 해당되는 커스텀 오버레이가 표시된다.

구현하려는 오버레이는 http://apis.map.kakao.com/web/sample/removableCustomOverlay/ 를 참조했으며,
제가 생각해서 구현한 코드는 다음과 같습니다.

<script>
// 카카오맵 지도 생성
var mapContainer = document.getElementById('map'), // 지도를 표시할 div 
mapOption = { 
    center: new kakao.maps.LatLng(36.20316293878739, 127.81803310329138), // 지도의 중심좌표
    level: 12 // 지도의 확대 레벨
};

// 지도를 표시할 div와  지도 옵션으로  지도를 생성합니다
var map = new kakao.maps.Map(mapContainer, mapOption); 

// 주소를 좌표로 바꾸기 위한 geocoder 생성
var geocoder = new kakao.maps.services.Geocoder();

// 전체 마을 위치 마커를 담을 배열
var markers = [];
					
$.ajax({
	url : '${pageContext.request.contextPath}/town/api/showAll',
	type : 'GET',
	dataType : 'json',
	data : '',
	success : function(response) {
		if(response.result == 'fail') {
			console.log(response.message);
			return;
		}
						
		if(response.result == 'success') {
			// 마을 전체 리스트를 돌면서 마을 위치마다 마커 찍기
			$.each(response.data, function(index, value){
				// DB에서 주소를 불러와 geocoder를 이용해 좌표로 변환
				geocoder.addressSearch(response.data[index].addr, function(result, status) {
					// 정상적으로 검색이 완료됐으면 
					if (status === kakao.maps.services.Status.OK) {
						var coords = new kakao.maps.LatLng(result[0].y, result[0].x);

						// 마커가 표시될 위치입니다
						var markerPosition  = new kakao.maps.LatLng(coords.Ha, coords.Ga); 

						// 마커를 생성합니다
						var marker = new kakao.maps.Marker({
						    position: markerPosition
						});
						
						// 커스텀 오버레이에 표시할 컨텐츠 입니다
						// 커스텀 오버레이는 아래와 같이 사용자가 자유롭게 컨텐츠를 구성하고 이벤트를 제어할 수 있기 때문에
						// 별도의 이벤트 메소드를 제공하지 않습니다 
						var content = '<div class="wrap">' + 
						            '    <div class="info">' + 
						            '        <div class="title">' + 
						            				response.data[index].name + 
						            '            <div class="close" onclick="closeOverlay()" title="닫기"></div>' + 
						            '        </div>' + 
						            '        <div class="body">' + 
						            '            <div class="img">' +
						            '                <img src="http://cfile181.uf.daum.net/image/250649365602043421936D" width="73" height="70">' +
						            '           </div>' + 
						            '            <div class="desc">' + 
						            '                <div class="ellipsis">' + response.data[index].addr + '</div>' + 
						            '                <div><a href="http://www.kakaocorp.com/main" target="_blank" class="link">홈페이지</a></div>' + 
						            '            </div>' + 
						            '        </div>' + 
						            '    </div>' +    
						            '</div>';
					
						// 커스텀 오버레이를 닫는 함수
			            function closeOverlay() {
			        	    overlay.setMap(null);
			        	}            
						            
						// 커스텀 오버레이 생성
						var overlay = new kakao.maps.CustomOverlay({
						    content: content,
						    map: map,
						    position: marker.getPosition()
						});
						
						// 마커를 클릭했을 때 커스텀 오버레이를 표시합니다
						kakao.maps.event.addListener(marker, 'click', function() {
						    overlay.setMap(map);
						});
						
						// 검색된 마커만 지도 위에 표시하기 위해 전체 마커 숨기기
						closeOverlay();
						
						// 마커가 지도 위에 표시되도록 설정합니다
						marker.setMap(map);
													
						// 생성된 마커를 배열에 추가
						markers.push(marker);
					} 
				});
			})
		}
	}
});
		
// 지도에 마커 표시
function setMarkers(map) {
    for(var i=0; i<markers.length; i++) {
        markers[i].setMap(map);
    }            
}

// 지도에서 마커 숨기기
function hideAllMarkers() {
	setMarkers(null);
	markers = [];
}
			
// 전체 체험마을 중 이름으로 검색하여 특정 위치에만 마커 찍기
$('#searchTown').click(function(event){
	// form submit 이벤트 차단
	event.preventDefault();
	
	// 요소에서 마을 이름 가져오기
	var name = $("#townName").val();
				
	$.ajax({
		url : "${pageContext.request.contextPath}/town/api/search?townName="+name,
		type : "GET",
		dataType : "JSON",
		data : "",
		success : function(response) {	// 요청 성공 시 받아온 응답 객체를 전달받는 익명함수 수행
			if(response.result == "fail") {	// 응답 결과가 fail이라면
				console.log(response.message);	// 콘솔 로그 출력 후
				return;	// 종료
			}
			
			// 지도에 표시된 전체 마커 숨기기
			hideAllMarkers();
			
			// 마을 이름으로 조회 성공 시 (success)
			if(response.result == "success") {
				$.each(response.data, function(index, value){
					geocoder.addressSearch(response.data[index].addr, function(result, status) {
						// 정상적으로 검색이 완료됐으면 
						if (status === kakao.maps.services.Status.OK) {
							var coords = new kakao.maps.LatLng(result[0].y, result[0].x);
						   																			
							// 마커가 표시될 위치입니다
							var markerPosition  = new kakao.maps.LatLng(coords.Ha, coords.Ga); 

							// 마커를 생성합니다
							var marker = new kakao.maps.Marker({
							    position: markerPosition
							});
							
							// 커스텀 오버레이에 표시할 컨텐츠 입니다
							// 커스텀 오버레이는 아래와 같이 사용자가 자유롭게 컨텐츠를 구성하고 이벤트를 제어할 수 있기 때문에
							// 별도의 이벤트 메소드를 제공하지 않습니다 
							var content =	'<div class="wrap">' + 
								            '    <div class="info">' + 
								            '        <div class="title">' + 
								            				response.data[index].name + 
								            '            <div class="close" onclick="closeOverlay()" title="닫기"></div>' + 
								            '        </div>' + 
								            '        <div class="body">' + 
								            '            <div class="img">' +
								            '                <img src="http://cfile181.uf.daum.net/image/250649365602043421936D" width="73" height="70">' +
								            '           </div>' + 
								            '            <div class="desc">' + 
								            '                <div class="ellipsis">' + response.data[index].addr + '</div>' + 
								            '                <div><a href="http://www.kakaocorp.com/main" target="_blank" class="link">홈페이지</a></div>' + 
								            '            </div>' + 
								            '        </div>' + 
								            '    </div>' +    
								            '</div>';
								            
					        // 커스텀 오버레이를 닫는 함수
				            function closeOverlay() {
				        	    overlay.setMap(null);
				        	}
					        
							// 커스텀 오버레이 생성
							var overlay = new kakao.maps.CustomOverlay({
							    content: content,
							    map: map,
							    position: marker.getPosition()       
							});
							
							// 마커를 클릭했을 때 커스텀 오버레이를 표시합니다
							kakao.maps.event.addListener(marker, 'click', function() {
							    overlay.setMap(map);
							});
							
							// 검색된 마커만 지도 위에 표시하기 위해 전체 마커 숨기기
							closeOverlay();
							
							// 마커가 지도 위에 표시되도록 설정합니다
							marker.setMap(map);
							
							// 생성된 마커를 배열에 추가
							markers.push(marker);
						} 
					});
				});
			}
		},
		error : function(jqXHR, status, error) {
			console.log(jqXHR.status, status);
		}
	});
});

정확한 문제 파악을 위해 다소 길고 가독성이 떨어지는 코드를 첨부하게 되었습니다. 양해 부탁드립니다.

이렇게 작성을 하고 실행을 하면 전체, 그리고 검색을 통해 특정 마을이 지도 상에 마커로 표시까진 됩니다.
문제는 커스텀 오버레이인데요. 마커 클릭 시 오버레이가 잘 표시되지만, 오버레이의 x를 눌러도 창이 닫히지 않습니다.
아마도 제 생각엔 오버레이를 닫는 함수인 closeOverlay()가 ajax 안에 있다보니
바깥에서 찾질 못해서 오버레이가 닫히지 않는 것 같습니다.

그래서 closeOverlay()를 바깥으로 빼서 테스트를 해봤는데
그 경우에는 setMap 함수를 찾지 못해서 여러 마커를 클릭해도 특정 마커의 오버레이만 실행되는 복잡한 상황에 직면했습니다.

지금도 계속 해결법을 찾아보고 있지만 부족한 식견인지라
전문가분들의 답변을 듣고 싶어서 이렇게 장문의 글을 작성하게 되었습니다.
긴 글 읽어주셔서 감사드립니다.

p.s) 오후 7:16 코드 다시 수정했습니다.

content를 생성하는 부분을 아래와 같이 바꿔주세요.

var closeOverlay = function() {
    overlay.setMap(null);
};

var $wrap = $('<div class="wrap" />');
var $info = $('<div class="info" />');
var $title = $('<div class="title" />').text(response.data[index].name);
var $close = $('<div class="close" title="닫기" />').click(closeOverlay);
var $body = $('<div class="body" />'); // body 안쪽은 생략

$wrap.append($info);
$info.append($title).append($body);
$title.append($close);

var content = $wrap[0];
var overlay = new kakao.maps.CustomOverlay({
    content: content,
    map: map,
    position: marker.getPosition()
});
1개의 좋아요

덕분에 잘 해결되었습니다. 감사합니다.

1개의 좋아요