From f089ba2dfed06ed777fceedbf4d3e0682f02590e Mon Sep 17 00:00:00 2001 From: sb Date: Sat, 11 Jul 2026 08:20:19 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=84=A4=EC=9D=B4=EB=B2=84=20=EC=A7=80?= =?UTF-8?q?=EB=8F=84=20=EC=97=B0=EB=8F=99=20(=EC=8A=A4=ED=8C=9F=20?= =?UTF-8?q?=EB=A7=88=EC=BB=A4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 네이버 지도 JS SDK 동적 로더(싱글턴) + @types/navermaps 타입 - NaverMap 컴포넌트: 스팟 실좌표 마커, 클릭 시 스팟 선택/정보창, 선택 시 중심 이동 - HomePage 지도 자리를 네이버 지도로 교체, 마커 선택 → 해당 스팟 한줄평/메이트 로드 - 키(VITE_NAVER_MAP_CLIENT_ID) 미설정 시 폴백 처리 Co-Authored-By: Claude Opus 4.8 (1M context) --- .env.example | 5 ++ package-lock.json | 18 +++++ package.json | 1 + src/components/NaverMap.vue | 129 ++++++++++++++++++++++++++++++++++++ src/env.d.ts | 7 ++ src/lib/naverMap.ts | 30 +++++++++ src/pages/HomePage.vue | 32 +++++---- 7 files changed, 205 insertions(+), 17 deletions(-) create mode 100644 src/components/NaverMap.vue create mode 100644 src/lib/naverMap.ts diff --git a/.env.example b/.env.example index ba8b7b3..7b342e9 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,7 @@ # 백엔드 API 주소 (dognation_BT) VITE_API_BASE_URL=http://localhost:8080 + +# 네이버 지도 (네이버 클라우드 플랫폼 > Maps 에서 발급) +VITE_NAVER_MAP_CLIENT_ID=your_ncp_key_id +# 신규 키는 ncpKeyId(기본), 구형 키면 ncpClientId 로 변경 +# VITE_NAVER_MAP_AUTH_PARAM=ncpKeyId diff --git a/package-lock.json b/package-lock.json index d3ba7c6..686b382 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "@capacitor/cli": "^8.3.4", "@capacitor/ios": "^8.3.4", "@quasar/vite-plugin": "^1.9.0", + "@types/navermaps": "^3.7.5", "@vitejs/plugin-vue": "^5.2.1", "sass-embedded": "^1.83.0", "typescript": "^5.7.2", @@ -1525,6 +1526,23 @@ "@types/node": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/navermaps": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@types/navermaps/-/navermaps-3.9.2.tgz", + "integrity": "sha512-pPO38MFRRY+2wka0VdG3U9RlwLEVBwJVYy50KtQzwxNdJVVfas8Qlv4USx3wxvly7GNX9Cew2qfjdKEmLoQPKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/node": { "version": "26.1.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz", diff --git a/package.json b/package.json index 31b7584..205e4b5 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@capacitor/android": "^8.3.4", "@capacitor/cli": "^8.3.4", "@capacitor/ios": "^8.3.4", + "@types/navermaps": "^3.7.5", "@quasar/vite-plugin": "^1.9.0", "@vitejs/plugin-vue": "^5.2.1", "sass-embedded": "^1.83.0", diff --git a/src/components/NaverMap.vue b/src/components/NaverMap.vue new file mode 100644 index 0000000..4c81697 --- /dev/null +++ b/src/components/NaverMap.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/src/env.d.ts b/src/env.d.ts index e679178..67d58be 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -1,4 +1,5 @@ /// +/// declare module '*.vue' { import type { DefineComponent } from 'vue' @@ -8,8 +9,14 @@ declare module '*.vue' { interface ImportMetaEnv { readonly VITE_API_BASE_URL: string + readonly VITE_NAVER_MAP_CLIENT_ID: string + readonly VITE_NAVER_MAP_AUTH_PARAM?: string } interface ImportMeta { readonly env: ImportMetaEnv } + +interface Window { + naver: typeof naver +} diff --git a/src/lib/naverMap.ts b/src/lib/naverMap.ts new file mode 100644 index 0000000..527ead9 --- /dev/null +++ b/src/lib/naverMap.ts @@ -0,0 +1,30 @@ +// 네이버 지도 JS SDK 동적 로더 (싱글턴) +// 키는 네이버 클라우드 플랫폼(Maps)에서 발급. .env 의 VITE_NAVER_MAP_CLIENT_ID 로 주입. +let loadPromise: Promise | null = null + +export function loadNaverMaps(): Promise { + if (typeof window !== 'undefined' && window.naver?.maps) { + return Promise.resolve(window.naver) + } + if (loadPromise) { + return loadPromise + } + + const clientId = import.meta.env.VITE_NAVER_MAP_CLIENT_ID + // 신규 키는 ncpKeyId, 구형 키는 ncpClientId. 필요 시 env 로 전환. + const authParam = import.meta.env.VITE_NAVER_MAP_AUTH_PARAM || 'ncpKeyId' + + loadPromise = new Promise((resolve, reject) => { + if (!clientId) { + reject(new Error('VITE_NAVER_MAP_CLIENT_ID 가 설정되지 않았습니다.')) + return + } + const script = document.createElement('script') + script.src = `https://openapi.map.naver.com/openapi/v3/maps.js?${authParam}=${clientId}` + script.async = true + script.onload = () => resolve(window.naver) + script.onerror = () => reject(new Error('네이버 지도 SDK 를 불러오지 못했습니다.')) + document.head.appendChild(script) + }) + return loadPromise +} diff --git a/src/pages/HomePage.vue b/src/pages/HomePage.vue index 443869d..1083b22 100644 --- a/src/pages/HomePage.vue +++ b/src/pages/HomePage.vue @@ -1,13 +1,8 @@