From 59a5e6beac58962460f979d5cdf22bd80af6e63d Mon Sep 17 00:00:00 2001 From: htlee Date: Tue, 17 Feb 2026 16:47:38 +0900 Subject: [PATCH] =?UTF-8?q?fix(map):=20=ED=8C=A8=EB=84=90=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=20fly-to=20=EC=A6=89=EC=8B=9C=20=EB=B0=98=EC=9D=91=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - shipData를 ref로 관리하여 AIS poll마다 effect 재실행 방지 - isStyleLoaded 가드 제거 → try/catch로 즉시 실행 - duration 700→400ms로 단축 - selectedMmsi만 의존성으로 → 선택 시 1회만 fly-to 실행 Co-Authored-By: Claude Opus 4.6 --- apps/web/src/widgets/map3d/hooks/useFlyTo.ts | 47 +++++++------------- 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/apps/web/src/widgets/map3d/hooks/useFlyTo.ts b/apps/web/src/widgets/map3d/hooks/useFlyTo.ts index 18bf356..0a66809 100644 --- a/apps/web/src/widgets/map3d/hooks/useFlyTo.ts +++ b/apps/web/src/widgets/map3d/hooks/useFlyTo.ts @@ -1,6 +1,5 @@ -import { useEffect, type MutableRefObject } from 'react'; +import { useEffect, useRef, type MutableRefObject } from 'react'; import type maplibregl from 'maplibre-gl'; -import { onMapStyleReady } from '../lib/mapCore'; import type { MapProjectionId } from '../types'; export function useFlyTo( @@ -19,7 +18,11 @@ export function useFlyTo( ) { const { selectedMmsi, shipData, mapInitiatedSelectRef, fleetFocusId, fleetFocusLon, fleetFocusLat, fleetFocusZoom } = opts; - // 패널(좌측 목록)에서 선택 시 해당 선박 위치로 이동 + // shipData를 ref로 — 의존성에서 제외하여 AIS poll마다 재실행 방지 + const shipDataRef = useRef(shipData); + useEffect(() => { shipDataRef.current = shipData; }, [shipData]); + + // 패널(좌측 목록)에서 선택 시 해당 선박 위치로 즉시 이동 useEffect(() => { // 지도 내부 클릭에서 발생한 선택이면 스킵 if (mapInitiatedSelectRef.current) { @@ -30,53 +33,37 @@ export function useFlyTo( const map = mapRef.current; if (!map || selectedMmsi == null) return; - const target = shipData.find((t) => t.mmsi === selectedMmsi); + const target = shipDataRef.current.find((t) => t.mmsi === selectedMmsi); if (!target || !Number.isFinite(target.lon) || !Number.isFinite(target.lat)) return; - const apply = () => { - const flyOpts = { center: [target.lon, target.lat] as [number, number], duration: 700 }; + try { + const flyOpts = { center: [target.lon, target.lat] as [number, number], duration: 400 }; if (projectionRef.current === 'globe') { map.flyTo(flyOpts); } else { map.easeTo(flyOpts); } - }; - - if (map.isStyleLoaded()) { - apply(); - return; + } catch { + // ignore — style not ready 등 } - - const stop = onMapStyleReady(map, apply); - return () => { stop(); }; - }, [selectedMmsi, shipData]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedMmsi]); // 선단 포커스 이동 useEffect(() => { const map = mapRef.current; if (!map || fleetFocusLon == null || fleetFocusLat == null || !Number.isFinite(fleetFocusLon) || !Number.isFinite(fleetFocusLat)) return; - const lon = fleetFocusLon; - const lat = fleetFocusLat; - const zoom = fleetFocusZoom ?? 10; - const apply = () => { - const flyOpts = { center: [lon, lat] as [number, number], zoom, duration: 700 }; + try { + const flyOpts = { center: [fleetFocusLon, fleetFocusLat] as [number, number], zoom: fleetFocusZoom ?? 10, duration: 500 }; if (projectionRef.current === 'globe') { map.flyTo(flyOpts); } else { map.easeTo(flyOpts); } - }; - - if (map.isStyleLoaded()) { - apply(); - return; + } catch { + // ignore } - - const stop = onMapStyleReady(map, apply); - return () => { - stop(); - }; }, [fleetFocusId, fleetFocusLon, fleetFocusLat, fleetFocusZoom]); }