Files
tasteby/docs/design/363-map-sdk-branch/README.md
joungmin f17ba9e37a feat(map): #363 메인 지도 SDK 국내(네이버)/해외(구글) 분기
- MapView dispatcher: NAVER 키 + KR bbox 좌표 → NaverMapView
- NaverMapView 신규 (네이버 v3 직접 wrapper, Supercluster 재사용)
- GoogleMapView 신규 (기존 MapView 내용 rename)
- MapView.types.ts 공용 타입 + isKoreaCoord 헬퍼
- Dockerfile/deploy.sh: NEXT_PUBLIC_NAVER_MAP_CLIENT_ID build-arg
- 키 미설정 시 GoogleMap fallback (회귀 0)

Refs: #363

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 06:25:47 +09:00

4.4 KiB

설계서: 메인 지도 탭 SDK 국내/해외 분기 (#363)

상태: Approved 작성: [AI] Architect · 최종수정: 2026-06-16 추적성 — Redmine: #363 · 부모: v0.1.51 1단계(외부 링크 분기) · 관련: MapView.tsx, mobile nearby · 구현 파일: frontend/src/components/MapView.tsx(dispatcher), frontend/src/components/GoogleMapView.tsx(rename from 기존 MapView 내용), frontend/src/components/NaverMapView.tsx(신규), frontend/src/lib/map-utils.ts(공용 헬퍼) · 테스트: 본 범위 밖 (수동 — dev 브라우저 검증)

1. 목적 (Why)

현재 MapView는 @vis.gl/react-google-maps 단일 사용. 한국 식당은 네이버 지도가 지번/도로명/상호/길찾기에서 압도적으로 정확. 메인 지도 탭 자체를 국내/해외 분기.

2. 범위

  • 포함: MapView를 dispatcher로 전환, 좌표 기반 자동 분기(KR bbox), 네이버 키 미설정 시 GoogleMap fallback.
  • 제외 (별도 후속): 사용자 강제 토글 UI, mixed 화면(한국+해외 동시) 최적화, 모바일 nearby도 동일 분기는 1차 적용 후 검토.

3. 인수조건

  • NEXT_PUBLIC_NAVER_MAP_CLIENT_ID 환경변수 설정 + 화면 중심이 KR bbox 안이면 NaverMap 렌더.
  • 키 미설정 또는 화면이 KR 밖이면 GoogleMap 렌더 (현행 동일).
  • Supercluster + 클러스터/단일 마커 표시, 클릭 → onSelectRestaurant 콜백 동일.
  • flyTo, onBoundsChanged, 내 위치, 채널 색상 동일하게 동작.
  • 빌드/타입 회귀 없음.

4. 컨텍스트 & 제약

  • 네이버 지도 v3: https://oapi.map.naver.com/openapi/v3/maps.js?ncpClientId=<ID> 스크립트 로드.
  • 네이버 좌표계: 기본 WGS84 (naver.maps.LatLng(lat, lng)).
  • 직접 wrapper 채택 (react-naver-maps 의존성 제거 — 메인터넌스 리스크).
  • Supercluster는 SDK 독립이라 재사용.
  • KR bbox: 위도 3338.7, 경도 124132. 화면 중심좌표가 안에 있으면 한국.

5. 아키텍처 개요

MapView (dispatcher)
   │
   ├─ 화면 중심 좌표가 KR bbox AND 네이버 키 있음 → NaverMapView
   │     ├─ <script src=naver maps v3> 동적 로드
   │     ├─ useEffect: new naver.maps.Map(div, ...)
   │     ├─ Supercluster로 cluster 계산 → markers div overlay
   │     └─ flyTo: map.setCenter + setZoom
   │
   └─ 그 외 → GoogleMapView (기존 MapView 내용 그대로 이전)

6. 함수 명세

함수 책임 비고
MapView (dispatcher) 좌표 기반 분기 flyTo 또는 첫 마운트 좌표로 판정
GoogleMapView 기존 MapView 내용 rename만, 로직 변경 X
NaverMapView 신규 — 네이버 지도 + Supercluster + markers wrapper 직접
useNaverMaps(clientId) 스크립트 로드 + ready boolean 한 번만 로드
isKoreaBounds(lat, lng) KR bbox 판정 map-utils 공용

7. 흐름

  1. MapView 마운트 → flyTo or 첫 식당 평균 좌표로 초기 중심 계산.
  2. KR bbox + 키 있음 → NaverMapView 마운트.
  3. NaverMapView: useNaverMaps 훅으로 v3 스크립트 로드, ready되면 new naver.maps.Map(divRef, options) 생성.
  4. Supercluster로 cluster 계산 → 마커는 absolute positioned div overlay (네이버 OverlayView 또는 자체 좌표 변환).
  5. 사용자 줌/팬 → bounds_changed 이벤트 → 클러스터 재계산 + onBoundsChanged 콜백.

8. 엣지케이스

  • 네이버 스크립트 로드 실패: ready=false 유지, dispatcher가 다음 렌더 사이클에서 GoogleMap fallback.
  • flyTo가 해외 좌표인데 현재 NaverMap 중: dispatcher 재판정 → GoogleMap로 교체 (remount).
  • mixed 화면(한국+해외 식당): 화면 중심 기준 SDK 선택 → 다른 나라 식당은 화면 밖에 있어 무관.
  • 키 미설정: 항상 GoogleMap (회귀 0).

9. 리스크 & 대안

  • 선택: 직접 wrapper. 의존성 최소, 유지보수 자유.
  • 대안 A: react-naver-maps npm — 빠른 시작이지만 메인터넌스 상태 불확실.
  • 대안 B: 단일 SDK(Maplibre + 네이버 타일) — 타일 권리 이슈.
  • 트레이드오프: 직접 wrapper는 초기 코드 양 ↑이지만 한 번 만들면 안정.

10. 미해결 질문

  • 한 화면 mixed(국가 경계 근처) 동시 마커 — 후속.
  • 사용자 토글 UI — 후속.
  • 모바일 nearby 동일 분기 — 1차 적용 후 결정.