Compare commits

..

2 Commits

Author SHA1 Message Date
joungmin
2eb16ce861 fix(map): 클러스터 expansion 후 마커 클릭 시 재묶임 방지
- selected 시 무조건 zoom 16 → 줌 18에서 마커 클릭하면 다시 16으로 줄어 클러스터링
- 현재 zoom ≥ 16이면 유지, 미만이면 16으로 (Naver/Google 둘 다)
- page.tsx의 setRegionFlyTo 호출 제거 — selected useEffect로 일원화

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

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-17 10:09:22 +09:00
joungmin
14384b0c71 fix(map): NaverMap 클러스터 클릭 시 중앙 이동 — morph로 통일
- panTo(애니메이션) + setZoom(z, true)(애니메이션) 동시 호출이 서로 cancel
- 단일 식당은 줌 변화 없어서 가려졌고 클러스터는 줌 크게 변경 → 가시화
- SDK의 morph(latlng, zoom)으로 center+zoom 동시 변경 일관 처리
- flyTo / selected / cluster 클릭 모두 동일 패턴

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

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-17 09:55:51 +09:00
4 changed files with 26 additions and 14 deletions

View File

@@ -6,6 +6,16 @@
## 2026-06-17 ## 2026-06-17
### 🎯 클러스터 expansion 후 마커 클릭 시 재묶임 방지 (v0.1.66)
- 기존: selected 시 무조건 zoom 16 → 클러스터 expansion(18)에서 마커 클릭하면 다시 16으로 줄어 재묶임
- 변경: 현재 zoom ≥ 16이면 유지, 미만이면 16으로 (NaverMap morph + GoogleMap setZoom 조건부)
- page.tsx의 setRegionFlyTo 호출 제거 — selected useEffect로 일원화 (zoom 16 강제 회피)
### 🐛 NaverMap 클러스터 클릭 시 중앙 이동 fix (v0.1.65)
- panTo(애니메이션) + setZoom(z, true)(애니메이션) 두 애니메이션 동시 호출이 서로 cancel
- 단일 식당은 줌 변화 없어서 안 보였고, 클러스터는 줌 크게 변경 → 충돌 가시화
- NaverMap SDK의 `morph(latlng, zoom)` 한 메서드로 통일 — center+zoom 동시 변경
### 🎯 NaverMapView selected 자동 panTo + zoom (v0.1.64) ### 🎯 NaverMapView selected 자동 panTo + zoom (v0.1.64)
- 마커/클러스터/리스트 어디서 선택해도 그 식당이 화면 중앙으로 + zoom 16 - 마커/클러스터/리스트 어디서 선택해도 그 식당이 화면 중앙으로 + zoom 16
- GoogleMapView에는 이미 있던 useEffect [selected] 패턴을 동일하게 추가 - GoogleMapView에는 이미 있던 useEffect [selected] 패턴을 동일하게 추가

View File

@@ -385,10 +385,8 @@ export default function Home() {
const handleSelectRestaurant = useCallback((r: Restaurant) => { const handleSelectRestaurant = useCallback((r: Restaurant) => {
setSelected(r); setSelected(r);
setShowDetail(true); setShowDetail(true);
// 지도가 선택 식당으로 이동/줌인 — 객체 새로 만들어 flyTo effect 매번 트리거 // selected 변경 자체가 MapView 내부의 useEffect에서 중앙 이동을 처리.
if (r.latitude != null && r.longitude != null) { // (flyTo를 같이 set하면 줌 16으로 강제되어 클러스터 expansion 후 마커 클릭 시 다시 묶임)
setRegionFlyTo({ lat: r.latitude, lng: r.longitude, zoom: 16 });
}
}, []); }, []);
const handleCloseDetail = useCallback(() => { const handleCloseDetail = useCallback(() => {

View File

@@ -163,11 +163,12 @@ function MapContent({ restaurants, selected, onSelectRestaurant, flyTo, activeCh
if (flyTo.zoom) map.setZoom(flyTo.zoom); if (flyTo.zoom) map.setZoom(flyTo.zoom);
}, [map, flyTo]); }, [map, flyTo]);
// Pan and zoom to selected restaurant // Pan and zoom to selected restaurant — 현재 줌이 16 이상이면 유지(클러스터 expansion 후 재묶임 방지)
useEffect(() => { useEffect(() => {
if (!map || !selected) return; if (!map || !selected) return;
map.panTo({ lat: selected.latitude, lng: selected.longitude }); map.panTo({ lat: selected.latitude, lng: selected.longitude });
map.setZoom(16); const currentZoom = map.getZoom() ?? 13;
if (currentZoom < 16) map.setZoom(16);
setInfoTarget(selected); setInfoTarget(selected);
}, [map, selected]); }, [map, selected]);

View File

@@ -28,6 +28,7 @@ type NaverMapInstance = {
getZoom: () => number; getZoom: () => number;
getBounds: () => { getNE: () => LatLng; getSW: () => LatLng }; getBounds: () => { getNE: () => LatLng; getSW: () => LatLng };
panTo: (latlng: unknown, opts?: Record<string, unknown>) => void; panTo: (latlng: unknown, opts?: Record<string, unknown>) => void;
morph: (latlng: unknown, zoom?: number, transitionOptions?: Record<string, unknown>) => void;
refresh: (noEffect?: boolean) => void; refresh: (noEffect?: boolean) => void;
}; };
type NaverMarker = { type NaverMarker = {
@@ -231,21 +232,24 @@ export default function NaverMapView({
} }
}, [ready, flyTo, selected, onBoundsChanged]); }, [ready, flyTo, selected, onBoundsChanged]);
// flyTo 변경 반영 // flyTo 변경 반영 — morph로 panTo+setZoom 충돌 회피
useEffect(() => { useEffect(() => {
const m = mapRef.current; const m = mapRef.current;
if (!m || !flyTo || !window.naver?.maps) return; if (!m || !flyTo || !window.naver?.maps) return;
m.panTo(new window.naver.maps.LatLng(flyTo.lat, flyTo.lng)); const latlng = new window.naver.maps.LatLng(flyTo.lat, flyTo.lng);
if (flyTo.zoom) m.setZoom(flyTo.zoom, true); if (flyTo.zoom) m.morph(latlng, flyTo.zoom);
else m.panTo(latlng);
}, [flyTo]); }, [flyTo]);
// selected 변경 시 자동 panTo + zoom (GoogleMapView와 동일 동작) // selected 변경 시 자동 중앙 — 현재 줌이 16 이상이면 그대로, 미만이면 16으로 확대
// (클러스터 expansion으로 18까지 들어간 상태에서 마커 클릭 시 다시 16으로 줄어 클러스터링되는 것 방지)
useEffect(() => { useEffect(() => {
const m = mapRef.current; const m = mapRef.current;
if (!m || !selected || !window.naver?.maps) return; if (!m || !selected || !window.naver?.maps) return;
if (selected.latitude == null || selected.longitude == null) return; if (selected.latitude == null || selected.longitude == null) return;
m.panTo(new window.naver.maps.LatLng(selected.latitude, selected.longitude)); const latlng = new window.naver.maps.LatLng(selected.latitude, selected.longitude);
m.setZoom(16, true); const targetZoom = Math.max(m.getZoom(), 16);
m.morph(latlng, targetZoom);
}, [selected]); }, [selected]);
// 클러스터 계산 (bounds/zoom 변경 시) // 클러스터 계산 (bounds/zoom 변경 시)
@@ -280,8 +284,7 @@ export default function NaverMapView({
}); });
naver.Event.addListener(marker, "click", () => { naver.Event.addListener(marker, "click", () => {
const z = Math.min(getExpansionZoom(cluster_id), 18); const z = Math.min(getExpansionZoom(cluster_id), 18);
m.panTo(new naver.LatLng(lat, lng)); m.morph(new naver.LatLng(lat, lng), z);
m.setZoom(z, true);
}); });
markersRef.current.push(marker); markersRef.current.push(marker);
} else { } else {