2차 팀 프로젝트에서 스크럼을 통해 지도를 추가하자고 했다.
네이버는 유료여서 카카오 지도 API를 선택했고 그 과정을 적어보려 한다.
국내 사용자 기준 접근성과 구현 효율이 가장 좋다고 판단했다.
또한 공연장 위치를 직관적으로 제공하기에 적합했고 프론트엔드와 연동이 매우 간단했다.

학습용이라 프로젝트이름을 입력하면 된다.

플렛폼 키 들어가면 자바스크립트 API 키를 제공해주는데 프론트에 카카오 지도를 제공해주는데 사용하면 된다.
SDK url은 도메인까지만 입력하면 된다.

마지막으로 카카오맵 사용 설정 여부만 ON으로 바꿔주면 된다.
이게 있는지 모르고 좀 지도가 왜 안 나오지 하면서 좀 헤맸다..
여기까지 하면 코드짜기 전 사전 작업 끝이다.
@Value("${kakao.map.app-key}")
private String kakaoMapAppKey;
@GetMapping("/config/map")
public Map<String, String> getMapConfig() {
return Map.of("kakaoMapAppKey", kakaoMapAppKey);
}
아까 받은 API를 환경변수화 해서 키에 넣은 후 프론트단에 내려준다.
{
"id": 1,
"imageUrl": "https://operation-grape.s3.ap-northeast-2.amazonaws.com/p1.jpeg",
"performanceName": "뮤지컬 데스노트",
"venue": "디큐브 링크아트센터",
"startDate": "2026-04-20",
"endDate": "2026-06-30",
"performanceTime": 165,
"startedAt": "2026-03-26T19:00:49.359692",
"price": 95000,
"remainingSeatLimit": null,
"address": "서울특별시 구로구 경인로 662 (신도림동, 디큐브시티)",
"latitude": 37.5089149,
"longitude": 126.888417
}
카카오 지도 위치를 찍기 위해 위경도를 같이 내려줬다.
let kakaoMap = null;
let kakaoMarker = null;
async function loadKakaoSdk() {
if (window.kakao && window.kakao.maps) {
return;
}
const res = await fetch('/api/performances/config/map');
if (!res.ok) {
throw new Error('카카오 지도 설정을 불러오지 못했습니다.');
}
const config = await res.json();
const appKey = config.kakaoMapAppKey;
if (!appKey) {
throw new Error('카카오 지도 키가 없습니다.');
}
await new Promise((resolve, reject) => {
const existing = document.querySelector('script[data-kakao-map="true"]');
if (existing) {
existing.addEventListener('load', resolve, { once: true });
return;
}
const script = document.createElement('script');
script.src = `https://dapi.kakao.com/v2/maps/sdk.js?appkey=${appKey}&libraries=services&autoload=false`;
script.dataset.kakaoMap = 'true';
script.onload = () => kakao.maps.load(resolve);
script.onerror = () => reject(new Error('카카오 SDK 로드 실패'));
document.head.appendChild(script);
});
}
async function openMapModal() {
try {
if (!data) return;
const venueName = data.venue || '공연장';
const address = data.address;
const latitude = data.latitude;
const longitude = data.longitude;
if ((latitude == null || longitude == null) && !address) {
alert('공연장 위치 정보가 없습니다.');
return;
}
await loadKakaoSdk();
document.getElementById('mapTitle').textContent = `${venueName} 위치`;
document.getElementById('mapModal').classList.add('active');
const mapContainer = document.getElementById('kakaoMap');
const defaultCenter = new kakao.maps.LatLng(37.5665, 126.9780);
if (!kakaoMap) {
kakaoMap = new kakao.maps.Map(mapContainer, {
center: defaultCenter,
level: 3
});
}
kakaoMap.relayout();
if (kakaoMarker) {
kakaoMarker.setMap(null);
}
// 위도/경도 있으면 바로 지도 표시
if (latitude != null && longitude != null) {
const coords = new kakao.maps.LatLng(latitude, longitude);
kakaoMap.setCenter(coords);
kakaoMap.setLevel(3);
kakaoMarker = new kakao.maps.Marker({
map: kakaoMap,
position: coords
});
const infowindow = new kakao.maps.InfoWindow({
content: `
<div style="
padding:8px 12px;
font-size:13px;
font-weight:600;
color:#191919;
background:#ffffff;
border:1px solid #ddd;
border-radius:8px;
white-space:nowrap;
">
${venueName}
</div>
`
});
infowindow.open(kakaoMap, kakaoMarker);
return;
}
// 주소 fallback
const geocoder = new kakao.maps.services.Geocoder();
geocoder.addressSearch(address, function(result, status) {
if (status === kakao.maps.services.Status.OK) {
const coords = new kakao.maps.LatLng(result[0].y, result[0].x);
kakaoMap.setCenter(coords);
kakaoMap.setLevel(3);
kakaoMarker = new kakao.maps.Marker({
map: kakaoMap,
position: coords
});
const infowindow = new kakao.maps.InfoWindow({
content: `<div style="padding:6px 10px;font-size:13px;">${venueName}</div>`
});
infowindow.open(kakaoMap, kakaoMarker);
} else {
alert('공연장 위치를 찾을 수 없습니다.');
}
});
} catch (error) {
console.error('지도 열기 실패:', error);
alert(error.message || '지도를 불러오지 못했습니다.');
}
}
JSON 응답 → 위도/경도 꺼냄 → 카카오 좌표 생성 → 지도 중심 이동 → 마커 표시

실제 JSON 값의 위치가 나오는 모습이다.