# 지도 기반 나침반(OSM, 노스업) 추가 + 타입 전환

**소요 시간**: 약 30분
**Context 사용량**: input ~340k / output ~9k tokens

## 요구
우측 상단 나침반을 2종 중 선택. 새 '지도 기반 나침반' = 나침반 안에 지도 + 실시간 위치.
사용자 선택: **OSM 온라인 타일 · 노스업(북쪽 고정) · 화면표시 옵션에 전환 버튼.**

## 구현
### 클라이언트
- [store/settingsStore.ts](../../client/src/store/settingsStore.ts): `compassType: 'analog'|'map'` + setter (persist).
- [components/overlay/MapCompass.tsx](../../client/src/components/overlay/MapCompass.tsx) (신규):
  - OSM 타일을 Web Mercator 로 배치, 원형 클립. 노스업(지도 고정) + 현재위치 중심 점 + 진행방향 화살표(yaw 회전) + 상단 N.
  - `poseRef`(부모 RAF 갱신 {lat,lon,yaw}) 를 내부 RAF 로 읽어: 화살표 즉시 회전 + 지도 팬(transform) + 임계 이동 시 타일 재배치(setState).
  - 타일 URL = **서버 프록시** `/api/tile/{z}/{x}/{y}.png` (외부 직접 로드는 CSP/COEP 로 차단됨).
- [components/overlay/StationOverlay.tsx](../../client/src/components/overlay/StationOverlay.tsx):
  - `mapPoseRef` 추가, RAF 나침반 블록에서 headingDeg 항상 계산해 기록(아날로그 회전은 렌더 중일 때만).
  - 렌더: compassType 에 따라 `<MapCompass>` / `<Minimap>` 선택.
  - 화면표시 옵션 '표시 토글'에 나침반 [눈금|지도] 버튼 추가.

### 서버 (프록시 필수)
- CSP `img-src 'self'` + COEP `require-corp` 라 외부 타일 `<img>` 직접 로드 불가(elevation.ts 와 동일 제약).
- [server/src/routes/tile.ts](../../server/src/routes/tile.ts) (신규): `GET /api/tile/:z/:x/:y` →
  OSM 타일 fetch(식별 User-Agent) 후 같은 출처로 중계. `Cross-Origin-Resource-Policy: same-origin`, 7일 캐시, 좌표 검증(범위 밖 400).
- [server/src/app.ts](../../server/src/app.ts): `app.use('/api/tile', tileRouter)`.

## 배포
- server/client 빌드 + **PM2 restart ghiVideo**(서버 라우트 반영 필수) 완료.
- 검증: `/api/tile/16/x/y.png` → 200 image/png, CORP same-origin, 7일 캐시. 범위 밖 → 400. 클라 `index-BS7bYMJ3.js`.

## 추가: 줌 + 호버 확대 + 휠 줌
- settingsStore `compassZoom`(12~19, 기본16) + setter(persist). MapCompass 에서 project(z 동적) 사용.
- 나침반 하단 −/z/+ 버튼(pointer-events auto). 줌 변경 시 현재위치 기준 타일 재배치.
- **마우스 오버 → 2배 확대**(transform scale, 우상단 고정 origin, 140ms), **벗어나면 원복**.
- **휠 줌**: 컨테이너 native wheel 리스너(passive:false) → deltaY 로 compassZoom ±1(위=확대).
- 호버/휠 위해 컨테이너 pointer-events auto(해당 영역만 이벤트 캡처).

## 개선(사용자 피드백)
- 확대: CSS scale(흐릿) → **위젯 지름 자체 확대**(BASE_D 150 → HOVER_D 300, 타일 재배치) → 더 넓은
  영역을 선명하게. 마우스 오버 시 확대, **벗어나면 기본 크기+기본 줌(16) 복귀**.
- 방향 표시: 빨간 화살표 → **반투명 역삼각형(▽)** = 드론 시야(중심에서 촬영방향으로 벌어짐, yaw 회전).
- **줌 +/− 버튼·z값 표시 삭제.** 줌은 휠만(오버 중), 벗어나면 기본값.
- 상단 **N**: SVG text 로(위젯 크기에 맞춰 스케일) **크게·붉은색·반투명**.
- settingsStore 의 compassZoom 제거(줌은 MapCompass 로컬 상태로 이동, 세션 임시).

## 추가: 위성지도 옵션
- 타일 프록시를 소스별로 확장: [server/routes/tile.ts](../../server/src/routes/tile.ts) `GET /api/tile/:source/:z/:x/:y`
  - source=osm → OpenStreetMap 일반, source=sat → **Esri World Imagery 위성**(z/row/col 순서, jpeg).
- settingsStore `compassMapStyle: 'street'|'sat'`(persist). MapCompass 타일 URL `/api/tile/${src}/...`.
- 화면표시 옵션에 지도 [일반|위성] 토글(지도 타입일 때만 노출).
- server 재빌드 + PM2 restart. 검증: osm→200 png, sat→200 jpeg.
- 주의: Esri World Imagery 는 출처표기 필요(내부 도구, 저부하). 대량이면 라이선스 검토.

## 주의/추후
- OSM 타일 사용 정책: 저부하 유지(줌16 고정 + 클라 96px 이동 시에만 재배치 + 서버 7일 캐시). 대량이면 자체 타일/유료 서비스 검토.
- 인터넷 없으면 타일 미표시(테두리/점/화살표만). 오프라인 필요 시 '노선 개요도(데이터 기반)' 타입 추가 검토.
