- 배포 흐름 v0.1.51~v0.1.57 정리 - 운영 진단 확인사항 (NCLOUD 도메인 형식, Search/Maps 별개 시스템) - NaverMapView 안정화 핵심 결정사항 (divRef 항상 마운트, ResizeObserver, try/catch) - 후속 분리 항목 명시 Refs: #363 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
113 lines
6.0 KiB
Markdown
113 lines
6.0 KiB
Markdown
# 설계서: 메인 지도 탭 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: 위도 33~38.7, 경도 124~132. 화면 중심좌표가 안에 있으면 한국.
|
||
|
||
## 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차 적용 후 결정.
|
||
|
||
## 11. 실제 구현 기록 (2026-06-16)
|
||
|
||
### 배포 흐름
|
||
| 버전 | 내용 |
|
||
|---|---|
|
||
| v0.1.51 | **1단계** — 식당 상세 외부 링크 좌표 기반 분기 (`RestaurantDetail.tsx`) |
|
||
| v0.1.52 | **2단계** — MapView dispatcher + NaverMapView/GoogleMapView 분리 + Dockerfile/deploy.sh build-arg |
|
||
| v0.1.53 | **fix**: 인증 파라미터 `ncpClientId` → `ncpKeyId` (NCLOUD 신 정책, 옛 NAVER Developers와 다름) |
|
||
| v0.1.55–56 | 임시 fallback (운영 일시 GoogleMap, 디버그) |
|
||
| v0.1.57 | **안정화 + 재활성** — divRef 첫 렌더 누락 fix, ResizeObserver/rAF, try/catch |
|
||
|
||
### 운영 진단에서 확인된 사항
|
||
- NCLOUD Maps Application의 Web 서비스 URL은 **스킴 포함**(`https://...`).
|
||
- 옛 NAVER Developers와 NCLOUD는 다른 시스템 — Search Application과 Maps Application은 도메인 중복 충돌 없음.
|
||
- NCLOUD 콘솔의 신규 경로: `Services > Application Services > Maps > Application`.
|
||
|
||
### NaverMapView 안정화 핵심 결정사항
|
||
- **`divRef` 항상 마운트** (early return 제거) — `ready=false` 동안에도 div를 두고 로딩 메시지는 overlay로 표시.
|
||
- **명시적 `width:100%; height:100%`** + 회색 배경 — 컨테이너 영역이 시각적으로 확인 가능.
|
||
- **ResizeObserver + requestAnimationFrame**으로 컨테이너 0×0 → 정상 크기 변경 시 `m.refresh(true)`.
|
||
- **try/catch + `initError` state** — init 실패 시 화면 가시화.
|
||
|
||
### 후속 (별도 PR)
|
||
- 사용자 토글 (네이버/구글 강제 선택) UI.
|
||
- mixed 화면(국경 근처) 동시 마커.
|
||
- 모바일 nearby 탭 동일 분기 검토.
|
||
- 채널 색상/InfoWindow 등 GoogleMapView 수준의 디테일을 NaverMapView에 도입.
|