Files
jongryangje/app/Views/components/kakao_address_search.php
taekyoungc 647d5f919d 지정판매소 주소·지도 연동과 관련 설정을 반영
지정판매소 등록/수정/목록에 카카오 주소 검색 및 지도 연동 컴포넌트를 적용하고, 관련 모델·SQL 스크립트·테스트 설정을 함께 정리해 기능 동작 기반을 맞췄다.

Made-with: Cursor
2026-04-14 14:55:12 +09:00

127 lines
5.0 KiB
PHP

<?php
declare(strict_types=1);
/** @var string $buttonId 주소 검색 버튼 id */
$buttonId = $buttonId ?? 'btn-kakao-postcode';
/** @var string $zipName 우편번호 input name */
$zipName = $zipName ?? 'ds_zip';
/** @var string $roadName 도로명 input name */
$roadName = $roadName ?? 'ds_addr';
/** @var string $jibunName 지번 input name */
$jibunName = $jibunName ?? 'ds_addr_jibun';
/** @var string $sidoFieldName 카카오 시·도 → hidden name (비우면 미설정) */
$sidoFieldName = $sidoFieldName ?? '';
/** @var string $sigunguFieldName 카카오 시·군·구 → hidden name */
$sigunguFieldName = $sigunguFieldName ?? '';
/** @var string $detailFieldName 상세주소 input name (건물명 등, 비우면 미사용) */
$detailFieldName = $detailFieldName ?? '';
/**
* @var array{lg_sido?: string, lg_gugun?: string}|null $tenantScope 지자체 관할 검사(비우면 미검사)
*/
$tenantScope = $tenantScope ?? null;
/** @var bool $roadBaseOnly true면 도로명에 건물명 괄호 미부착(상세로 이전) */
$roadBaseOnly = ! empty($roadBaseOnly);
?>
<script src="https://t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script>
(function () {
var btnId = <?= json_encode($buttonId, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
var zipName = <?= json_encode($zipName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
var roadName = <?= json_encode($roadName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
var jibunName = <?= json_encode($jibunName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
var sidoFieldName = <?= json_encode($sidoFieldName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
var sigunguFieldName = <?= json_encode($sigunguFieldName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
var detailFieldName = <?= json_encode($detailFieldName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
var roadBaseOnly = <?= $roadBaseOnly ? 'true' : 'false' ?>;
var tenantScope = <?= json_encode($tenantScope ?? (object) [], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
function compactStr(s) {
return String(s || '').replace(/\s+/g, '');
}
function tokenMatches(needle, primary, blob) {
var n = compactStr(needle);
if (!n) return true;
var b = compactStr(blob);
if (b.indexOf(n) !== -1) return true;
var p = compactStr(primary);
if (p.indexOf(n) !== -1) return true;
if (n.indexOf(p) !== -1 && p) return true;
return false;
}
function addressAllowedByTenant(data) {
var lgSido = tenantScope && tenantScope.lg_sido ? String(tenantScope.lg_sido) : '';
var lgGugun = tenantScope && tenantScope.lg_gugun ? String(tenantScope.lg_gugun) : '';
if (!lgSido && !lgGugun) return true;
var sido = data.sido || '';
var sigungu = data.sigungu || '';
var road = data.roadAddress || '';
var jibun = data.jibunAddress || '';
var zip = data.zonecode || '';
var blob = sido + ' ' + sigungu + ' ' + road + ' ' + jibun + ' ' + zip;
if (lgSido && !tokenMatches(lgSido, sido, blob)) return false;
if (lgGugun && !tokenMatches(lgGugun, sigungu, blob)) return false;
return true;
}
function bind() {
var btn = document.getElementById(btnId);
if (!btn) return;
var form = btn.closest('form');
if (!form) return;
function field(n) {
return form.querySelector('[name="' + n + '"]');
}
btn.addEventListener('click', function () {
if (typeof daum === 'undefined' || !daum.Postcode) {
window.alert('주소 검색 스크립트를 불러오지 못했습니다. 네트워크를 확인해 주세요.');
return;
}
new daum.Postcode({
oncomplete: function (data) {
if (!addressAllowedByTenant(data)) {
window.alert('작업 중인 지자체 관할이 아닌 주소입니다. 해당 시·구 주소를 검색해 주세요.');
return;
}
var zipEl = field(zipName);
var roadEl = field(roadName);
var jibunEl = field(jibunName);
if (zipEl) zipEl.value = data.zonecode || '';
var roadAddr = data.roadAddress || '';
if (!roadBaseOnly && data.buildingName !== '') {
roadAddr += (roadAddr !== '' ? ' (' + data.buildingName + ')' : data.buildingName);
}
if (roadEl) roadEl.value = roadAddr;
if (jibunEl) jibunEl.value = data.jibunAddress || '';
if (sidoFieldName) {
var sidoEl = field(sidoFieldName);
if (sidoEl) sidoEl.value = data.sido || '';
}
if (sigunguFieldName) {
var sigEl = field(sigunguFieldName);
if (sigEl) sigEl.value = data.sigungu || '';
}
if (detailFieldName && roadBaseOnly) {
var detEl = field(detailFieldName);
if (detEl) detEl.value = data.buildingName || '';
}
}
}).open();
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', bind);
} else {
bind();
}
})();
</script>