지정판매소 등록/수정/목록에 카카오 주소 검색 및 지도 연동 컴포넌트를 적용하고, 관련 모델·SQL 스크립트·테스트 설정을 함께 정리해 기능 동작 기반을 맞췄다. Made-with: Cursor
154 lines
5.5 KiB
PHP
154 lines
5.5 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
$key = trim((string) ($kakaoJavascriptKey ?? ''));
|
|
?>
|
|
<div id="kakao-map-modal" class="hidden fixed inset-0 z-[300] flex items-center justify-center p-4" aria-hidden="true" role="dialog" aria-modal="true" aria-labelledby="kakao-map-modal-title">
|
|
<div class="absolute inset-0 bg-black/50" id="kakao-map-modal-backdrop"></div>
|
|
<div class="relative z-[301] w-full max-w-2xl max-h-[90vh] flex flex-col rounded border border-gray-300 bg-white shadow-lg overflow-hidden">
|
|
<div class="flex items-center justify-between px-3 py-2 border-b border-gray-200 bg-gray-50 shrink-0">
|
|
<span id="kakao-map-modal-title" class="text-sm font-bold text-gray-800">위치</span>
|
|
<button type="button" id="kakao-map-modal-close" class="text-gray-600 hover:text-gray-900 text-xl leading-none px-1" aria-label="닫기">×</button>
|
|
</div>
|
|
<div id="kakao-map-modal-container" class="w-full bg-gray-100" style="min-height: 380px; height: 50vh;"></div>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
(function () {
|
|
var APP_KEY = <?= json_encode($key, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
|
var modal = document.getElementById('kakao-map-modal');
|
|
var backdrop = document.getElementById('kakao-map-modal-backdrop');
|
|
var btnClose = document.getElementById('kakao-map-modal-close');
|
|
var mapContainer = document.getElementById('kakao-map-modal-container');
|
|
var mapInstance = null;
|
|
var markerInstance = null;
|
|
var scriptLoading = false;
|
|
var pendingAfterLoad = [];
|
|
|
|
function hideModal() {
|
|
if (!modal) return;
|
|
modal.classList.add('hidden');
|
|
modal.setAttribute('aria-hidden', 'true');
|
|
}
|
|
|
|
function showModal() {
|
|
if (!modal) return;
|
|
modal.classList.remove('hidden');
|
|
modal.setAttribute('aria-hidden', 'false');
|
|
}
|
|
|
|
function runPending() {
|
|
var q = pendingAfterLoad;
|
|
pendingAfterLoad = [];
|
|
q.forEach(function (fn) {
|
|
try {
|
|
fn();
|
|
} catch (e) {}
|
|
});
|
|
}
|
|
|
|
function ensureScript(cb) {
|
|
if (!APP_KEY) {
|
|
window.alert('카카오맵 JavaScript 키가 설정되지 않았습니다. .env에 kakao.javascriptKey를 설정해 주세요. (Kakao Developers → 앱 키 → JavaScript 키)');
|
|
return;
|
|
}
|
|
if (typeof kakao !== 'undefined' && kakao.maps) {
|
|
cb();
|
|
return;
|
|
}
|
|
pendingAfterLoad.push(cb);
|
|
if (scriptLoading) {
|
|
return;
|
|
}
|
|
scriptLoading = true;
|
|
var s = document.createElement('script');
|
|
s.charset = 'UTF-8';
|
|
s.async = true;
|
|
// 동적 삽입 시 autoload=false 후 kakao.maps.load() 필수 (카카오 웹 가이드)
|
|
s.src = 'https://dapi.kakao.com/v2/maps/sdk.js?appkey=' + encodeURIComponent(APP_KEY) + '&libraries=services&autoload=false';
|
|
s.onload = function () {
|
|
scriptLoading = false;
|
|
if (typeof kakao === 'undefined' || !kakao.maps || typeof kakao.maps.load !== 'function') {
|
|
pendingAfterLoad = [];
|
|
window.alert(
|
|
'카카오맵 API를 불러올 수 없습니다.\n\n' +
|
|
'Kakao Developers → 내 애플리케이션 → 해당 앱 → 「제품 설정」에서 「Kakao Map」(지도) / 로컬 API를 사용 설정으로 켜 주세요.\n' +
|
|
'(비활성 시 서버에서 OPEN_MAP_AND_LOCAL 오류가 납니다.)\n\n' +
|
|
'또한 플랫폼(Web)에 이 사이트 주소(예: http://localhost:8080)가 등록되어 있어야 합니다.'
|
|
);
|
|
return;
|
|
}
|
|
kakao.maps.load(function () {
|
|
runPending();
|
|
});
|
|
};
|
|
s.onerror = function () {
|
|
scriptLoading = false;
|
|
pendingAfterLoad = [];
|
|
window.alert(
|
|
'카카오맵 스크립트를 불러오지 못했습니다.\n\n' +
|
|
'• 네트워크·차단(광고 차단) 확인\n' +
|
|
'• Kakao Developers → 제품 설정에서 「Kakao Map」활성화\n' +
|
|
'• 플랫폼(Web)에 접속 중인 URL 등록'
|
|
);
|
|
};
|
|
document.head.appendChild(s);
|
|
}
|
|
|
|
if (btnClose) {
|
|
btnClose.addEventListener('click', hideModal);
|
|
}
|
|
if (backdrop) {
|
|
backdrop.addEventListener('click', hideModal);
|
|
}
|
|
document.addEventListener('keydown', function (e) {
|
|
if (e.key !== 'Escape' || !modal || modal.classList.contains('hidden')) {
|
|
return;
|
|
}
|
|
hideModal();
|
|
});
|
|
|
|
window.openDesignatedShopKakaoMap = function (addressQuery) {
|
|
var q = String(addressQuery || '').trim();
|
|
if (!q) {
|
|
window.alert('주소가 없습니다.');
|
|
return;
|
|
}
|
|
ensureScript(function () {
|
|
if (typeof kakao === 'undefined' || !kakao.maps || !kakao.maps.services) {
|
|
window.alert('카카오맵을 초기화할 수 없습니다.');
|
|
return;
|
|
}
|
|
var geocoder = new kakao.maps.services.Geocoder();
|
|
geocoder.addressSearch(q, function (result, status) {
|
|
if (status !== kakao.maps.services.Status.OK || !result || !result[0]) {
|
|
window.alert('주소를 지도에서 찾을 수 없습니다.');
|
|
return;
|
|
}
|
|
var coords = new kakao.maps.LatLng(result[0].y, result[0].x);
|
|
showModal();
|
|
if (!mapInstance) {
|
|
mapInstance = new kakao.maps.Map(mapContainer, {
|
|
center: coords,
|
|
level: 3
|
|
});
|
|
} else {
|
|
mapInstance.setCenter(coords);
|
|
mapInstance.setLevel(3);
|
|
}
|
|
if (markerInstance) {
|
|
markerInstance.setMap(null);
|
|
}
|
|
markerInstance = new kakao.maps.Marker({ position: coords, map: mapInstance });
|
|
setTimeout(function () {
|
|
if (mapInstance) {
|
|
mapInstance.relayout();
|
|
mapInstance.setCenter(coords);
|
|
}
|
|
}, 100);
|
|
});
|
|
});
|
|
};
|
|
})();
|
|
</script>
|