/** "280000" → "280,000원". */ export function formatWon(value: string | number | null | undefined): string { if (value == null || value === '') return ''; const num = typeof value === 'string' ? Number(value.replace(/,/g, '')) : value; if (!Number.isFinite(num)) return String(value); return `${num.toLocaleString('ko-KR')}원`; } /** ISO → "2026년 8월 27일". */ export function formatKoreanDate(iso: string | null | undefined): string { if (!iso) return ''; const date = new Date(iso); if (Number.isNaN(date.getTime())) return ''; const [year, month, day] = date.toLocaleDateString('sv-SE', {timeZone: 'Asia/Seoul'}).split('-').map(Number); return `${year}년 ${month}월 ${day}일`; } /** 왜 한글 표기가 아닌가 답변 엔진은 본문에서 YYYY-MM-DD 를 찾아 최신성을 매긴다. */ export function isoDate(iso: string | null | undefined): string { if (!iso) return ''; const date = new Date(iso); if (Number.isNaN(date.getTime())) return ''; return date.toLocaleDateString('sv-SE', {timeZone: 'Asia/Seoul'}); } /** 네이버 검색으로 보내는 링크. */ export function naverSearchUrl(query: string): string { return `https://search.naver.com/search.naver?query=${encodeURIComponent(query)}`; } /** 네이버지도 검색으로 보내는 링크. */ export function naverMapSearchUrl(query: string): string { return `https://map.naver.com/p/search/${encodeURIComponent(query)}`; } /** 유튜브 **검색** 주소. */ export function youtubeSearchUrl(query: string): string { return `https://www.youtube.com/results?search_query=${encodeURIComponent(query)}`; } /** 지도 주소에 실을 이름. */ function mapLabel(name: string): string { return name.replace(/,/g, ' ').replace(/\s+/g, ' ').trim(); } /** 카카오맵 **길찾기**. */ export function kakaoDirectionsUrl(name: string, lat?: number, lng?: number): string { if (lat == null || lng == null) { return `https://map.kakao.com/link/search/${encodeURIComponent(name)}`; } return `https://map.kakao.com/link/to/${encodeURIComponent(mapLabel(name))},${lat},${lng}`; } /** 네이버지도 **길찾기**. */ /** 네이버지도 길찾기 — **구간**. */ export function naverRouteUrl( from: {name: string; lat: number; lng: number}, to: {name: string; lat: number; lng: number}, ): string { const point = (p: {name: string; lat: number; lng: number}) => `${p.lng},${p.lat},${encodeURIComponent(mapLabel(p.name))}`; // 수단은 차(car)다. return `https://map.naver.com/p/directions/${point(from)}/${point(to)}/-/car`; } export function naverDirectionsUrl(name: string, lat?: number, lng?: number): string { if (lat == null || lng == null) { return `https://map.naver.com/p/search/${encodeURIComponent(name)}`; } return `https://map.naver.com/p/directions/-/${lng},${lat},${encodeURIComponent(mapLabel(name))}/-/transit`; } /** 지도 그림(OpenStreetMap 임베드) 주소. */ export function osmEmbedUrl(lat: number, lng: number, aspect = 16 / 9, halfLat = 0.0022): string { // bbox 의 종횡비를 iframe 과 맞춰야 한다. const cos = Math.max(Math.cos((lat * Math.PI) / 180), 0.01); const halfLng = (aspect * halfLat) / cos; const bbox = [lng - halfLng, lat - halfLat, lng + halfLng, lat + halfLat] .map((n) => n.toFixed(6)) .join(','); return `https://www.openstreetmap.org/export/embed.html?bbox=${bbox}&layer=mapnik&marker=${lat},${lng}`; } /** 티맵 길찾기. */ export function tmapDirectionsUrl(name: string, lat?: number, lng?: number): string | undefined { if (lat == null || lng == null) return undefined; const q = encodeURIComponent(name); return `tmap://route?goalname=${q}&goalx=${lng}&goaly=${lat}&rGoName=${q}&rGoX=${lng}&rGoY=${lat}`; }