1️⃣ kakao map api와 DB 연동
travelRetrieve.jsp
✅ 지도만 나오는 버전
//서버에서 가져온 위치 정보를 JavaScript 변수에 할당 //var locations = ${locations}; // 서버에서 위도와 경도 정보를 비동기적으로 가져오는 함수 function getLocationsFromServer() { // AJAX 또는 Fetch를 사용하여 서버에서 위치 정보를 가져온다. // 서버에서 가져온 데이터는 JSON 형식. fetch('/app/loginCheck/travelDel?travelID='+travelID) // API 엔드포인트 .then(response => response.json()) .then(data => { console.log(data); // 데이터 확인을 위한 로그 출력 // 가져온 데이터를 처리. displayLocationsOnMap(data); }) .catch(error => { console.error('Error fetching location data:', error); }); } //가져온 위치 정보를 지도에 표시하는 함수 function displayLocationsOnMap(locations) { var map = new kakao.maps.Map(document.getElementById('map'), { center: new kakao.maps.LatLng(37.566826, 126.9786567), // 지도의 중심 좌표 설정 level: 3 // 지도의 확대 레벨 설정 }); // 가져온 위치 정보를 반복하면서 마커를 생성하고 지도에 표시합니다. locations.forEach(location => { var markerPosition = new kakao.maps.LatLng(location.latitude, location.longitude); // 마커를 생성하고 지도에 추가 var marker = new kakao.maps.Marker({ position: markerPosition }); marker.setMap(map); }); } // 페이지 로드 시 위치 정보를 가져와서 지도에 표시 getLocationsFromServer();
서버로부터 받아온 계획(plan) 데이터를 사용하여 웹 페이지에 동적으로 일정 버튼을 생성하고, 각 버튼을 클릭할 때 해당 날짜의 일정 목록을 동적으로 생성하고 지도에 마커를 추가하는 기능을 구현했다.
<script>
var markers = []; // 마커 배열
$(function(){
var obj = {};
<c:forEach items="${planList}" var="plan" varStatus="status">
if(!obj["${plan.day_num}"]){
obj["${plan.day_num}"] = []; //day_num에 해당하는 키 없을 시, 새로운 배열 생성하여 obj 객체에 담음
}
obj["${plan.day_num}"].push({ //day_num을 키로 하고 해당 날짜에 대한 일정 목록을 값으로 가짐
item: "${plan.item}",
item_add: "${plan.item_add}",
time: "${plan.time}",
time: "${plan.time}",
mapx: "${plan.mapx}",
mapy: "${plan.mapy}",
idx: "${plan.idx}"
})
</c:forEach>
var days = ''; //days 초기화. 일정 버튼 저장할 HTML 문자열 가짐.
Object.keys(obj).forEach(function(e){ //obj객체의 키를 반복하면서 day_num에 대한 버튼 생성
//TODO: html 수정
days += '<button class="btn btn-primary plan-days" data-day='+e+'>DAY '+e+'</button>';
});
$('#test-days tbody').html(days); //test-days 테이블의 tbody에 일정 버튼 추가
//plan-days 버튼 클릭 이벤트
// 버튼 클릭시 해당 날짜의 일정 항목이 test-plan-item에 동적으로 생성됨
$(".plan-days").on("click", function(){
var day = $(this).data("day");
var planList = obj[day];
$("#test-plan-item").empty();
planList.forEach(function(plan){
// TODO html 수정
var html = ' <a href="#" class="list-group-item list-group-item-action py-3 lh-sm" aria-current="true">';
html+='<div class="d-flex w-100 align-items-center justify-content-between">';
html+=' <p> <strong class="mb-1">'+plan.item+'</strong> </p>';
html+='<small class="text-body-secondary">'+plan.time+'</small>';
html+='</div>';
html+='<div class="col-10 mb-1 small">'+plan.item_add+'</div>';
html+='</a>';
$("#test-plan-item").append(html); //html을 추가하여 세부 일정에 표시
// 테스트
console.log(plan.item);
console.log(plan.mapx);
console.log(plan.mapy);
// 해당 day에 속하는 모든 일정 항목의 위치 정보를 모아두고 전달
var dayPlans = [];
planList.forEach(function(plan) {
dayPlans.push({ mapx: plan.mapx, mapy: plan.mapy });
});
// 마커 추가 함수 호출
addMarkers(dayPlans);
});
})
function addMarkers(dayPlans) {
dayPlans.forEach(function(plan) {
var mapx = plan.mapx;
var mapy = plan.mapy;
// 카카오맵 API 초기화
kakao.maps.load(function() {
var container = document.getElementById('map');
var options = {
center: new kakao.maps.LatLng(mapy, mapx),
level: 3 // 지도 확대 수준 조정 (1-9 사이의 값)
};
var map = new kakao.maps.Map(container, options);
// 마커 생성
var markerPosition = new kakao.maps.LatLng(mapy, mapx);
var marker = new kakao.maps.Marker({
position: markerPosition
});
// 마커 지도에 표시
marker.setMap(map);
markers.push(marker);
console.log(markers)
});
});
}
});
- 일정 데이터 객체(obj) 생성:
- obj 객체는 planList에 대한 데이터를 날짜(day_num)를 기준으로 구조화한 것이다.
- 각 날짜별로 배열이 생성되고, 해당 배열에는 날짜에 속하는 일정들이 객체 형태로 담겨 있다.
- 일정 버튼 생성:
- Object.keys(obj).forEach를 사용하여 날짜별로 버튼을 생성하고, 이를 #test-days 테이블의 tbody에 추가한다.
- 일정 버튼 클릭 이벤트:
- .plan-days 클래스를 가진 버튼이 클릭되면 해당 날짜의 일정 목록을 동적으로 생성하고, 지도에 마커를 추가하는 이벤트 핸들러가 실행된다.
- 일정 목록 동적 생성:
- $(".plan-days").on("click", function(){...}에서 해당 날짜의 일정 목록을 동적으로 생성하고, 이를 #test-plan-item 영역에 추가한다.
- 마커 추가 함수(addMarkers):
- addMarkers 함수는 각 일정의 위치 정보를 받아와 지도에 마커를 추가하는 역할을 한다.
- 카카오맵 API를 사용하여 해당 위치로 지도를 생성하고, 마커를 해당 위치에 추가한다.
- 마커 배열(markers):
- markers 배열은 생성된 마커를 저장하는 역할이다.
🧨 일정 당 모든 세부 내역의 마커가 한번에 보이지않고, 최종적으로 하나의 마커만 보이는 이슈 발생
원인)
여러 개의 마커가 추가되어도 마지막 마커만 보이는 문제는 JavaScript의 비동기 작동과 클로저(Closure) 문제 때문에 발생할 수 있다. kakao.maps.load 메서드가 비동기적으로 실행되고, 마커 생성 루프에서 클로저 문제로 인해 마지막 마커만 나타날 수 있다.
해결)
클로저 문제를 해결하고, 각 마커에 대한 올바른 위치 정보를 보존해야 한다.
결론적으로 markers 배열을 전역 변수로 놓고, 그럼으로써 각 함수를 간단히 함으로써 해결했다.
<script>
var markers = []; // 마커 배열 (전역 변수)
$(function(){
var obj = {}; // day버튼
<c:forEach items="${planList}" var="plan" varStatus="status">
if(!obj["${plan.day_num}"]){
obj["${plan.day_num}"] = []; //day_num에 해당하는 키 없을 시, 새로운 배열 생성하여 obj 객체에 담음
}
obj["${plan.day_num}"].push({ //day_num을 키로 하고 해당 날짜에 대한 일정 목록을 값으로 가짐
item: "${plan.item}",
item_add: "${plan.item_add}",
time: "${plan.time}",
time: "${plan.time}",
mapx: "${plan.mapx}",
mapy: "${plan.mapy}",
idx: "${plan.idx}"
})
</c:forEach>
var days = ''; //days 초기화. 일정 버튼 저장할 HTML 문자열 가짐.
Object.keys(obj).forEach(function(e){ //obj객체의 키를 반복하면서 day_num에 대한 버튼 생성
//TODO: html 수정
days += '<button class="btn btn-primary plan-days" data-day='+e+'>DAY '+e+'</button>';
});
$('#test-days tbody').html(days); //test-days 테이블의 tbody에 일정 버튼 추가
// 마커 초기화
clearMarkers();
//-----------plan-days 버튼 클릭 이벤트 시작------------
// 버튼 클릭시 해당 날짜의 일정 항목이 test-plan-item에 동적으로 생성됨
$(".plan-days").on("click", function(){
var day = $(this).data("day");
var planList = obj[day];
$("#test-plan-item").empty();
// 선택한 day에 속하는 세부일정의 위치 정보 저장
var dayPlans = [];
planList.forEach(function(plan) {
dayPlans.push({ mapx: plan.mapx, mapy: plan.mapy });
// TODO html 수정
var html = ' <a href="#" class="list-group-item list-group-item-action py-3 lh-sm" aria-current="true">';
html+='<div class="d-flex w-100 align-items-center justify-content-between">';
html+=' <p> <strong class="mb-1">'+plan.item+'</strong> </p>';
html+='<small class="text-body-secondary">'+plan.time+'</small>';
html+='</div>';
html+='<div class="col-10 mb-1 small">'+plan.item_add+'</div>';
html+='</a>';
$("#test-plan-item").append(html); //html을 추가하여 세부 일정에 표시
// 테스트 코드
// console.log(plan.item);
// console.log(plan.mapx);
// console.log(plan.mapy);
});
// 마커 추가
addMarkers(dayPlans);
});
//-----------plan-days 버튼 클릭 이벤트 끝------------
// 초기화 함수
function clearMarkers() {
markers.forEach(function(marker) {
marker.setMap(null);
});
markers = [];
}
});
//---------------------------------- function() 끝 ----------------------------------
// 마커 추가 함수
function addMarkers(dayPlans) {
// Kakao Map
kakao.maps.load(function() {
var container = document.getElementById('map');
var options = {
center: new kakao.maps.LatLng(dayPlans[0].mapy, dayPlans[0].mapx), //첫 번째 마커 위치를 기준으로 중심 위치 설정
level: 3
};
var map = new kakao.maps.Map(container, options);
// 마커 생성하고 추가
dayPlans.forEach(function(plan) {
var mapx = plan.mapx;
var mapy = plan.mapy;
// 마커 생성
var markerPosition = new kakao.maps.LatLng(mapy, mapx);
var marker = new kakao.maps.Marker({
position: markerPosition
});
// 지도에 마커 출력
marker.setMap(map);
markers.push(marker);
// 테스트 코드
console.log(markers);
});
});
}
</script>
⭐ markers 배열을 전역 변수로 선언하는 이유?
마커 객체에 대한 참조를 유지하고, 다른 함수 또는 이벤트 처리에서 해당 마커를 쉽게 조작하고 접근하기 위함이다. 전역 변수로 선언하면 어디서든 해당 변수에 접근할 수 있으므로 여러 함수에서 마커 배열에 마커를 추가하거나 마커를 삭제하고, 필요한 경우 모든 마커를 초기화할 수 있다. 즉 유지보수가 더 쉬워진다.
⭐ markers 배열을 지역 변수로 선언한다면?
각 함수가 별개의 배열을 가지므로 일관성이 떨어지고 코드가 복잡해진다.
2️⃣ 버튼 활성화 추가
사용자가 어느 DAY 버튼을 눌렀는지 알 수 있게 버튼 활성화를 이용하여 표시하도록 했다.
그리고 페이지가 로드되면 사용자가 아무것도 클릭하지 않아도 자동으로 DAY1이 활성화되어 첫 화면에 보여지도록 하는 코드 추가
'Project 여담 > 5주차' 카테고리의 다른 글
11/04 : 3번째 멘토링 / 중간점검 (0) | 2023.11.05 |
---|---|
11/03 : 일정 삭제하기 DataIntegrityViolationException 오류 => ON CASCADE 추가 (0) | 2023.11.03 |
11/02 : 일정 상세보기 지도에 인포윈도우 추가 (0) | 2023.11.02 |
10/31 : 일정 상세보기 로직 구현 (0) | 2023.10.31 |
10/30 : 일정보관함 페이징 처리 / 여러가지 트러블슈팅 (0) | 2023.10.30 |