본문 바로가기
Spring

[Spring] 지도API와 데이터베이스 연동으로 다중마커 표시하는 방법

by GoodDayDeveloper 2021. 3. 30.
반응형

안녕하세요. 오늘은 다음지도 API와 데이터베이스 연동으로 다중마커를 표시하는 방법에 대해 이야기해보겠습니다.

저는 기업의 상호명과 도로명 주소를 이용하여 검색을 하면 검색이 된 리스트들이 여러개의 마커로 표시되는 화면을 구현하려합니다.

화면은 대략 이렇습니다. 태명이라는 검색어를 치게 되면

리스트결과에 대한 첫번째 인덱스가 포커싱이 되고,

결과의 리스트들은 지도에 다중으로 마커표시가 되게 됩니다.

 


XML

 

우선 XML에서 쿼리를 만들어줍니다.

상호명과 도로명주소를 조회하고 조건절에 검색어를 조회할 수 있도록 합니다.

<select id="getList" parameterType="tbl_factoryVO" resultType="tbl_factoryVO">
SELECT
    fctry_cmpnm
    fctry_rdnmadr
FROM
    tbl_factory 
WHERE
<if test="searchWrd != null and searchWrd !=''">
    (fctry_rdnmadr LIKE CONCAT('%', #{searchWrd},'%') or  
    fctry_cmpnm LIKE CONCAT('%', #{searchWrd},'%'))
</if>
</select>

 

 

SERVICE, SERVICEIMPL, DAO

 

그리고 리스트에 대한 서비스, 임플, DAO를 생성해줍니다.

List<tbl_factoryVO> getList(tbl_factoryVO searchVO);
@Override
public List<tbl_factoryVO> getList(tbl_factoryVO searchVO) {
    return dao.getList(searchVO);
}
public List<tbl_factoryVO> getList(tbl_factoryVO searchVO) {
    return selectList("getList", searchVO);
}

 

 

 

CONTROLLER

 

그리고 컨트롤러에서 리스트를 만들고 

Model로 내보낼때, 배열이기 때문에 JSONArray형식으로 내보내줍니다.

 

@RequestMapping(value = "/index", method = RequestMethod.GET)
public String list(@ModelAttribute("searchVO") tbl_factoryVO searchVO,  ModelMap model) throws Exception {

    try{
        
        List<tbl_factoryVO> test = homeservice.getList(searchVO);
        model.addAttribute("rdnmadrListJson", JSONArray.fromObject(test));
        
    }catch(Exception e){
        System.out.println(e.toString());
    }
    return "return 주소";
}

 

 

 

 

 

 

JSP

 

뷰화면입니다.

여기서는 검색화면과 지도 화면을 생성하고,

검색버튼을 눌렀을때 form의 id인 listform을 submit합니다.

주소를 불러와서 다중 마커를 표시하는 부분은 가장 중요하기 때문에 밑에서 따로 정리하겠습니다.

 

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
<script>
function fn_search() {
    $("#listForm").submit();
    return false;
}
</script>
 
<style>
/* map / + & - */
html, body {width:100%;height:100%;margin:0;padding:0;} 
.map_wrap {position:relative;overflow:hidden;width:100%;height:350px;}
.radius_border{border:1px solid #919191;border-radius:5px;}     
.custom_typecontrol {position:absolute;top:10px;right:10px;overflow:hidden;height:30px;margin:0;padding:0;z-index:1;font-size:12px;font-family:'Malgun Gothic', '맑은 고딕', sans-serif;}
.custom_typecontrol span {display:block;width:65px;height:30px;float:left;text-align:center;line-height:30px;cursor:pointer;}
.custom_typecontrol .btn {background:#fff;background:linear-gradient(#fff,  #e6e6e6);}       
.custom_typecontrol .btn:hover {background:#f5f5f5;background:linear-gradient(#f5f5f5,#e3e3e3);}
.custom_typecontrol .btn:active {background:#e6e6e6;background:linear-gradient(#e6e6e6, #fff);}    
.custom_typecontrol .selected_btn {color:#fff;background:#425470;background:linear-gradient(#425470, #5b6d8a);}
.custom_typecontrol .selected_btn:hover {color:#fff;}   
.custom_zoomcontrol {position:absolute;top:50px;right:10px;width:36px;height:80px;overflow:hidden;z-index:1;background-color:#f5f5f5;} 
.custom_zoomcontrol span {display:block;width:36px;height:40px;text-align:center;cursor:pointer;}     
.custom_zoomcontrol span img {width:15px;height:15px;padding:12px 0;border:none;}             
.custom_zoomcontrol span:first-child{border-bottom:1px solid #bfbfbf;}            
 
/* overlay */
.overlay_info a {display: block; background: #d95050; background: #d95050 url(https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/arrow_white.png) no-repeat right 14px center; text-decoration: none; color: #fff; padding:12px 36px 12px 14px; font-size: 14px; border-radius: 0px 0px 0 0}
.overlay_info a strong {background: no-repeat; padding-left: 2px;}
.overlay_info .desc {padding:14px;position: relative; min-width: 190px; height: 56px}
.overlay_info .address {font-size: 12px; color: #333; position: absolute; left: 80px; right: 14px; top: 24px; white-space: normal}
</style>
 
 
<form:form commandName="searchVO" method="get" name="listForm" id="listForm" action="/">
 
<div>
    <input type="text" class="text" id="searchWrd" name="searchWrd" placeholder="검색어를 입력해주세요" style="width: 300px;" value="${searchVO.searchWrd }" /> 
    <a href="#" onclick="fn_search();"    class="btn-login" style="width: 100px; height: 40px; margin-left: 5px;"><spring:message code="btn.search" text="검색" /></a>
</div>
 
 
<div>
      <div class="map_wrap">
        <div id="map" style="width:100%;height:100%;position:relative;overflow:hidden;"></div> 
     <!-- 지도타입 컨트롤 div 입니다 -->
    <div class="custom_typecontrol radius_border">
        <span id="btnRoadmap" class="selected_btn" onclick="setMapType('roadmap')">지도</span>
        <span id="btnSkyview" class="btn" onclick="setMapType('skyview')">스카이뷰</span>
    </div>
    <!-- 지도 확대, 축소 컨트롤 div 입니다 -->
    <div class="custom_zoomcontrol radius_border"> 
        <span onclick="zoomIn()"><img src="https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/ico_plus.png" alt="확대"></span>  
        <span onclick="zoomOut()"><img src="https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/ico_minus.png" alt="축소"></span>
    </div>
    </div>
</div>
 
</form:form>
 
 
 
<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey="인증키를 넣어주세요."&libraries=services"></script>
<script>
var mapContainer = document.getElementById('map'), // 지도를 표시할 div  
    mapOption = { 
        center: new kakao.maps.LatLng(33.450701126.570667), // 지도의 중심좌표
        level: 3// 지도의 확대 레벨
    };
 
var map = new kakao.maps.Map(mapContainer, mapOption); // 지도를 생성합니다
 
//지도타입 컨트롤의 지도 또는 스카이뷰 버튼을 클릭하면 호출되어 지도타입을 바꾸는 함수입니다
function setMapType(maptype) { 
    var roadmapControl = document.getElementById('btnRoadmap');
    var skyviewControl = document.getElementById('btnSkyview'); 
    if (maptype === 'roadmap') {
        map.setMapTypeId(kakao.maps.MapTypeId.ROADMAP);    
        roadmapControl.className = 'selected_btn';
        skyviewControl.className = 'btn';
    } else {
        map.setMapTypeId(kakao.maps.MapTypeId.HYBRID);    
        skyviewControl.className = 'selected_btn';
        roadmapControl.className = 'btn';
    }
}
 
// 지도 확대, 축소 컨트롤에서 확대 버튼을 누르면 호출되어 지도를 확대하는 함수입니다
function zoomIn() {
    map.setLevel(map.getLevel() - 1);
}
 
// 지도 확대, 축소 컨트롤에서 축소 버튼을 누르면 호출되어 지도를 확대하는 함수입니다
function zoomOut() {
    map.setLevel(map.getLevel() + 1);
}
 
//주소-좌표 변환 객체를 생성합니다
var geocoder = new kakao.maps.services.Geocoder();
 
var rdnmadrList = new Array();
var cmpnmList = new Array();
 
var rdnList =JSON.parse('${rdnmadrListJson}');
 
for(var k in rdnList){    
    var $obj = rdnList[k];
    var aa =  $obj.fctry_rdnmadr;
    var bb  =  $obj.fctry_cmpnm;
    rdnmadrList.push(aa);
    cmpnmList.push(bb);
    
}
 
//주소 리스트 
rdnmadrList.forEach(function(addr, index){
    // 주소로 좌표를 검색합니다
    geocoder.addressSearch(addr, function(result, status) {
        // 정상적으로 검색이 완료됐으면 
         if (status === kakao.maps.services.Status.OK) {
    
            var coords = new kakao.maps.LatLng(result[0].y, result[0].x);
            
            // 결과값으로 받은 위치를 마커로 표시합니다
            var marker = new kakao.maps.Marker({
                map: map,
                position: coords
            });
            
            var content = '<div class="overlay_info">';
            content += '    <a><strong>' + cmpnmList[index] +'</strong></a>';
            content += '    <div class="desc">';
            content += '        <img src="https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/place_thumb.png" alt="">';
            content += '        <span class="address">'+ rdnmadrList[index]  +'</span>';
            content += '    </div>';
            content += '</div>';
    
            // 인포윈도우로 장소에 대한 설명을 표시합니다
            var infowindow = new kakao.maps.InfoWindow({
                //  content: cmpnmList[index], 
                contentcontent,
                   disableAutoPan: true
                   
            });
            infowindow.open(map, marker);
    
            // 지도의 중심을 결과값으로 받은 위치로 이동시킵니다
            if(index == 0){
                map.setCenter(coords);    
            }
        } 
    });    
}); 
 
</script>
cs

 

 

이곳이 가장 핵심 부분입니다.

 

우선 변수에 model로 빼낸 rdnmadrListJson을 json.parse를 이용하여

json데이터를 넣어 데이터를 빼낼 수 있도록 오브젝트 객체로 만들어줍니다.

그리고 별도의 배열 변수를 만든다음

for문으로 rdnList를 돌려서 오브젝트로 속성을 빼낸 다음,

별도의 배열 변수에 넣어줍니다.

 

 

반응형

 


 

 

 

그리고 foreach문으로 주소 배열 변수(rdnmadrList)를 넣어줍니다. 

addr는 element, index는 인덱스입니다.

geocoder를 이용하여 addr를 넣어주고 result를 통해서 좌표를 확인합니다.

내용 부분인 content에 상호명배열변수인 cmpnmList와 주소배열변수인 cmpnmList에 index값을 넣어서 값이 

제대로 들어갈 수 있도록 설정합니다.

그리고 지도에 검색 결과의 위치로 가도록 하는 부분은 index값이 0일때만 보이도록 하여

맨 처음의 결과값만 나타날 수 있도록 설정합니다.

 

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
var rdnList =JSON.parse('${rdnmadrListJson}');
 
var rdnmadrList = new Array();
var cmpnmList = new Array();
 
for(var k in rdnList){    
    var $obj = rdnList[k];
    var aa =  $obj.fctry_rdnmadr;
    var bb  =  $obj.fctry_cmpnm;
    rdnmadrList.push(aa);
    cmpnmList.push(bb);
    
}
 
//주소 리스트 
rdnmadrList.forEach(function(addr, index){
    // 주소로 좌표를 검색합니다
    geocoder.addressSearch(addr, function(result, status) {
        // 정상적으로 검색이 완료됐으면 
         if (status === kakao.maps.services.Status.OK) {
    
            var coords = new kakao.maps.LatLng(result[0].y, result[0].x);
            
            // 결과값으로 받은 위치를 마커로 표시합니다
            var marker = new kakao.maps.Marker({
                map: map,
                position: coords
            });
            
            var content = '<div class="overlay_info">';
            content += '    <a><strong>' + cmpnmList[index] +'</strong></a>';
            content += '    <div class="desc">';
            content += '        <img src="https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/place_thumb.png" alt="">';
            content += '        <span class="address">'+ rdnmadrList[index]  +'</span>';
            content += '    </div>';
            content += '</div>';
    
            // 인포윈도우로 장소에 대한 설명을 표시합니다
            var infowindow = new kakao.maps.InfoWindow({
                //  content: cmpnmList[index], 
                content: content,
                   disableAutoPan: true
                   
            });
            infowindow.open(map, marker);
    
            // 지도의 중심을 결과값으로 받은 위치로 이동시킵니다
            if(index == 0){
                map.setCenter(coords);    
            }
        } 
    });    
}); 
cs

 

 

반응형

댓글