From 587462552b0e034308c2470b1b9cb4f4663c55dd Mon Sep 17 00:00:00 2001 From: Haewon Kam Date: Fri, 11 Sep 2026 11:38:07 +0900 Subject: [PATCH] =?UTF-8?q?feat(supporters):=20=EC=98=A4=EC=8B=9C=EB=8A=94?= =?UTF-8?q?=20=EA=B8=B8=20=EC=A7=80=EB=8F=84=C2=B7=EA=B8=B8=EC=B0=BE?= =?UTF-8?q?=EA=B8=B0=20(VisitMap)=20=EB=B0=A9=EB=AC=B8=20=EC=95=88?= =?UTF-8?q?=EB=82=B4=EC=99=80=20=EC=98=81=EC=96=B4=20=ED=99=88=EC=97=90=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OpenStreetMap 타일 + Leaflet(cdnjs) 지도, 병원 핀, 주소 복사, 문의 전화, 네이버·카카오 길찾기(영어 화면은 구글 길찾기 추가). 벤치마크 2026-09-11 - 좌표는 factSheet.geo(운영 기록 source 는 화면에 내지 않음), 없으면 관광공사 수집 메타, 둘 다 없으면 지도 없이 주소·버튼만 Co-Authored-By: Claude Fable 5.1 --- supporters/src/components/VisitMap.astro | 99 ++++++++++++++++++++++++ supporters/src/data/factSheet.json | 7 +- supporters/src/pages/en/index.astro | 2 + supporters/src/pages/visit.astro | 2 + 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 supporters/src/components/VisitMap.astro diff --git a/supporters/src/components/VisitMap.astro b/supporters/src/components/VisitMap.astro new file mode 100644 index 0000000..91ee282 --- /dev/null +++ b/supporters/src/components/VisitMap.astro @@ -0,0 +1,99 @@ +--- +// 오시는 길: 지도 + 주소 복사 + 길찾기 버튼. 방문 안내(/visit) 3. 찾아가기와 영어 홈 Getting Here 에서 쓴다. +// 좌표는 factSheet.geo {lat,lng,source} 를 먼저 보고(source 는 운영 기록이라 화면에 내지 않는다), 없으면 관광공사 수집 메타(medicalTourismKo.json meta.origin)를 쓴다. 둘 다 없으면 지도 없이 주소·버튼만 낸다. +// 지도는 OpenStreetMap 타일 + Leaflet(cdnjs). 길찾기는 네이버·카카오 지도의 공개 URL 형식(벤치마크 2026-09-11 확인)이고, 영어 화면에는 구글 지도 길찾기를 더한다. +import { fact, has, v } from '../lib'; +import tour from '../data/medicalTourismKo.json'; +interface Props { lang?: 'ko' | 'en'; nameEn?: string; addressEn?: string; title?: string; sub?: string; compact?: boolean; hideHead?: boolean } +const { lang = 'ko', nameEn, addressEn, title, sub, compact = false, hideHead = false } = Astro.props; +const f = fact as Record; +const origin = (tour as any)?.meta?.origin ?? {}; +const geo = f.geo && f.geo.lat && f.geo.lng ? f.geo : (origin.lat && origin.lng ? { lat: origin.lat, lng: origin.lng, source: origin.coordSource || '관광공사 수집 기준 좌표' } : null); +const nameKo = v(f.shortName, '병원'); +const name = lang === 'en' ? (nameEn || f.shortNameEn || f.nameEn || nameKo) : nameKo; +const address = lang === 'en' ? (addressEn || f.address?.fullEn || f.address?.full || '') : (f.address?.full || ''); +const phone: string = f.phone || ''; +const enc = encodeURIComponent; +const naverUrl = geo ? `https://map.naver.com/p/directions/-/${geo.lng},${geo.lat},${enc(nameKo)}/-/transit` : (has(f.address?.full) ? `https://map.naver.com/p/search/${enc(f.address.full)}` : ''); +const kakaoUrl = geo ? `https://map.kakao.com/link/to/${enc(nameKo)},${geo.lat},${geo.lng}` : (has(f.address?.full) ? `https://map.kakao.com/link/search/${enc(f.address.full)}` : ''); +const googleUrl = geo ? `https://www.google.com/maps/dir/?api=1&destination=${geo.lat},${geo.lng}&destination_place_id=&travelmode=transit` : (address ? `https://www.google.com/maps/search/?api=1&query=${enc(address)}` : ''); +const T = lang === 'en' + ? { title: title ?? 'Getting Here', sub: sub ?? 'Address, and directions from where you are.', address: 'Address', copy: 'Copy', copied: 'Copied', phone: 'Phone', naver: 'Naver Map directions', kakao: 'Kakao Map directions', google: 'Google Maps directions', noMap: 'Map is added once the clinic confirms its location.', attribution: 'Map data © OpenStreetMap contributors' } + : { title: title ?? '오시는 길', sub: sub ?? '주소와 현재 위치에서 오는 길을 안내합니다.', address: '주소', copy: '복사', copied: '복사했습니다', phone: '문의', naver: '네이버 길찾기', kakao: '카카오 길찾기', google: '구글 지도 길찾기', noMap: '지도는 병원이 위치를 확인한 뒤 싣습니다.', attribution: '지도 데이터 © OpenStreetMap 기여자' }; +const mapId = `visit-map-${Math.random().toString(36).slice(2, 8)}`; +--- +{geo && } +
+ {!compact && !hideHead &&
Directions

{T.title}

{T.sub}

} +
+
+ {geo + ? + :

{T.noMap}

} + {geo &&

{T.attribution}

} +
+
+
{T.address}
+
{address || v(null)}{address && }
+ {phone &&

{T.phone}: {phone}

} + {lang === 'ko' && has(f.transit) &&

{f.transit}

} +
+ {lang === 'en' && googleUrl && {T.google}} + {naverUrl && {T.naver}} + {kakaoUrl && {T.kakao}} +
+
+
+
+ + + + diff --git a/supporters/src/data/factSheet.json b/supporters/src/data/factSheet.json index 2c1b0df..da0001a 100644 --- a/supporters/src/data/factSheet.json +++ b/supporters/src/data/factSheet.json @@ -213,5 +213,10 @@ "sideEffectNotice": "수술 후 개인에 따라 염증, 출혈, 신경손상 등의 부작용이 있을 수 있습니다.", "businessNo": "220-08-86777", "checkedAt": "2026-09-04", - "nextCheck": "2026-10-04" + "nextCheck": "2026-10-04", + "geo": { + "lat": 37.5049823, + "lng": 127.0254182, + "source": "네이버 지역검색 실측 2026-09-09" + } } diff --git a/supporters/src/pages/en/index.astro b/supporters/src/pages/en/index.astro index 8c89fb8..482e76e 100644 --- a/supporters/src/pages/en/index.astro +++ b/supporters/src/pages/en/index.astro @@ -4,6 +4,7 @@ import Base from '../../layouts/Base.astro'; import { fact, site as S, clinicSchema, surface, has, v } from '../../lib'; import EN from '../../data/factSheetEn.json'; +import VisitMap from '../../components/VisitMap.astro'; const f = fact as Record; const site = Astro.site!.toString().replace(/\/$/, ''); const yt = surface('youtube'); const gu = surface('gangnamunni'); const gg = surface('google'); @@ -60,6 +61,7 @@ const ld = [
From the airport

Incheon to Sinnonhyeon

{EN.airport}

On arrival

Where to go in the building

{EN.arrival}

{EN.parking}

+

Opening hours

{(f.hours?.rows ?? []).map((r: any) => )} diff --git a/supporters/src/pages/visit.astro b/supporters/src/pages/visit.astro index 398cdae..2ce23ae 100644 --- a/supporters/src/pages/visit.astro +++ b/supporters/src/pages/visit.astro @@ -2,6 +2,7 @@ // 방문 안내. 병원 정보(/clinic)가 사실 표라면 이 페이지는 "예약하고, 찾아가고, 도착해서 뭘 하는지"의 행동 순서다. 값은 모두 factSheet.json에서 가져온다. import Base from '../layouts/Base.astro'; import Gallery from '../components/Gallery.astro'; +import VisitMap from '../components/VisitMap.astro'; import '../styles/plan.css'; import { fact, site as S, SITE_NAME, clinicSchema, reservationUrl, v, has } from '../lib'; const f = fact as Record; @@ -32,6 +33,7 @@ const ld = [

주소 {v(f.address?.full)}{has(f.address?.postalNote) && <> (지번 {f.address.postalNote})}

지하철 {v(f.transit)}.{has(f.transitNote) && <> {f.transitNote}}

차로 올 때 {v(f.parking, `주차 안내 ${v(null)}`)}.

+ {S.visitMaps?.length > 0 && }

4. 도착하면

DayHours
{hoursDays[r.days] ?? r.days}{r.open ? `${r.open} to ${r.close}` : EN.hoursClosed}