diff --git a/Projects/DesignKit/Sources/Component/BloomState/BloomStatus.swift b/Projects/DesignKit/Sources/Component/BloomState/BloomStatus.swift index c09b295a..b6b94678 100644 --- a/Projects/DesignKit/Sources/Component/BloomState/BloomStatus.swift +++ b/Projects/DesignKit/Sources/Component/BloomState/BloomStatus.swift @@ -9,7 +9,7 @@ import Foundation import SwiftUI -public enum BloomStatus: String, Sendable { +public enum BloomStatus: String, Sendable, Equatable { case little = "LITTLE" case bloomed = "BLOOMED" case withered = "WITHERED" diff --git a/Projects/Feature/FlowerSpotDetail/FlowerSpotDetailFeature/Sources/FlowerSpotDetailFeature.swift b/Projects/Feature/FlowerSpotDetail/FlowerSpotDetailFeature/Sources/FlowerSpotDetailFeature.swift index 4af6d54c..29baf857 100644 --- a/Projects/Feature/FlowerSpotDetail/FlowerSpotDetailFeature/Sources/FlowerSpotDetailFeature.swift +++ b/Projects/Feature/FlowerSpotDetail/FlowerSpotDetailFeature/Sources/FlowerSpotDetailFeature.swift @@ -192,13 +192,14 @@ extension FlowerSpotDetailFeature { if shouldUpdateMap { // 딥링크 진입: 지도 위치 이동 + 마커 표시 return .concatenate( + checkBloomStatus(status: state.flowerSpotData.bloomingStatus), .send(.prefetchImages), - .send(.delegate(.showOnMap(item))), .send(.delegate(.didUpdateFlowerSpot(item))) ) } else { // 마커 탭/검색: 프리페치 + 부모 동기화 return .concatenate( + checkBloomStatus(status: state.flowerSpotData.bloomingStatus), .send(.prefetchImages), .send(.delegate(.didUpdateFlowerSpot(item))) ) @@ -207,12 +208,12 @@ extension FlowerSpotDetailFeature { case let .bloomingResponse(item): state.bloomingStatus = item checkLoadingComplete(&state) - return .none + return checkBloomStatus(status: state.flowerSpotData.bloomingStatus) case let .verifyTodayBlooming(item): state.isVotedBlooming = item checkLoadingComplete(&state) - return .none + return checkBloomStatus(status: state.flowerSpotData.bloomingStatus) // MARK: - Analytics @@ -263,11 +264,17 @@ extension FlowerSpotDetailFeature { state.isDetailLoading = false state.isNeedDrawPath = true - if let bloomStatus = BloomStatus(rawValue: state.flowerSpotData.bloomingStatus) { - state.updateMarkerStatus = bloomStatus - } + // Analytics 트래킹 trackDetailsStart(&state) + + } + + private func checkBloomStatus(status: String) -> Effect { + if let bloomStatus = BloomStatus(rawValue: status) { + return .send(.delegate(.updateMarkerStatus(bloomStatus))) + } + return .none } /// details_start 및 map_spot_selected 이벤트 트래킹 diff --git a/Projects/Feature/FlowerSpotDetail/FlowerSpotDetailFeatureInterface/Sources/FlowerSpotDetailFeature.swift b/Projects/Feature/FlowerSpotDetail/FlowerSpotDetailFeatureInterface/Sources/FlowerSpotDetailFeature.swift index 397bef9f..3d946426 100644 --- a/Projects/Feature/FlowerSpotDetail/FlowerSpotDetailFeatureInterface/Sources/FlowerSpotDetailFeature.swift +++ b/Projects/Feature/FlowerSpotDetail/FlowerSpotDetailFeatureInterface/Sources/FlowerSpotDetailFeature.swift @@ -58,7 +58,6 @@ public struct FlowerSpotDetailFeature { public var isShowLoginAlert: Bool = false public var isVotedBlooming: VerifyBloomingStateEntity = .init(isBlooming: false) public var isDetailLoading: Bool = false - public var updateMarkerStatus: BloomStatus? = nil public var userLocation: Coordinate? = nil // MARK: - Navigation State @@ -148,6 +147,7 @@ public struct FlowerSpotDetailFeature { case presentToLogin(id: Int) case showOnMap(FlowerSpotEntity) case didUpdateFlowerSpot(FlowerSpotEntity) + case updateMarkerStatus(BloomStatus) } public var body: some ReducerOf { diff --git a/Projects/Feature/Map/MapFeature/Sources/MapFeature/MapFeature.swift b/Projects/Feature/Map/MapFeature/Sources/MapFeature/MapFeature.swift index 4d3ba783..e97e0e1d 100644 --- a/Projects/Feature/Map/MapFeature/Sources/MapFeature/MapFeature.swift +++ b/Projects/Feature/Map/MapFeature/Sources/MapFeature/MapFeature.swift @@ -58,17 +58,19 @@ extension MapFeature { return .none case let .requestMapBounds(isRequest): - state.requestMapBound = isRequest + state.shouldRequestInitialBounds = true state.researchButtonEnable = false if isRequest { analyticsClient.track(MapEvent.researchClicked(currentPage: "map")) + state.flowerSpots.removeAll() + return .send(.addMapAction(.requestBounds)) } return .none // 마커 탭 시, 디테일정보 불러오기 및 바텀시트 on case let .markerTapped(id): guard let id = id else { - state.isNeedDeleteMarker = true + state.mapActions.append(.deletePath) state.flowerSpotDetail = nil // 바텀시트 닫기 return .none } @@ -85,7 +87,7 @@ extension MapFeature { case let .fetchPathLines(id): if let data = state.flowerSpots[id] { state.selectedPathLines = data.path - state.isNeedDrawMarker = true + state.mapActions.append(.drawPath(data, data.path)) } else { state.selectedPathLines = [] } @@ -164,11 +166,10 @@ extension MapFeature { data.forEach { state.flowerSpots[$0.id] = $0 } - // SearchRegionListFeature가 활성화되어 있으면 데이터 전달 - if state.searchRegionList != nil { - return .send(.searchRegionList(.storeFlowerSpots(data))) - } - return .none + return .concatenate( + .send(.addMapAction(.updateMarkers(state.flowerSpots))), + state.searchRegionList != nil ? .send(.searchRegionList(.storeFlowerSpots(data))) : .none + ) case let .storeUserLocation(location): state.userLocation = location @@ -189,6 +190,10 @@ extension MapFeature { case let .presentAlert(type): return .send(.presentAlert(type: type)) + + case let .moveToLocation(location): + state.mapActions.append(.moveToUserLocation(location)) + return .none } // MARK: - Delegate @@ -203,7 +208,7 @@ extension MapFeature { case .dismiss: // 바텀시트 닫기: Optional State를 nil로 설정 state.flowerSpotDetail = nil - state.isNeedDeleteMarker = true + state.mapActions.append(.deletePath) return .none case let .presentToBlooming(id, streetName, distance): @@ -223,22 +228,27 @@ extension MapFeature { state.flowerSpots[item.id] = item } return .none + + case let .updateMarkerStatus(bloomStatus): + return .send(.addMapAction(.updateMarkerStatus(bloomStatus))) } case let .searchRegionList(.delegate(action)): switch action { case let .showFlowerSpotDetail(data): return .concatenate( - .send(.mapSearch(.showRegionList(data: nil))), - .send(.mapSearch(.setNavigationFromRegionList)), - .send(.fetchDetailInfo(data.id)), - .send(.location(.moveLocation(data.pinPoint))) + .send(.addMapAction(.changeActiveMarker(data))), + .send(.markerTapped(id: data.id)) ) } case .flowerSpotDetail: return .none + case let .addMapAction(action): + state.mapActions.append(action) + return .none + case .binding, .delegate, .alertAcceptTapped, .location, .searchRegionList, .mapSearch: return .none @@ -258,8 +268,10 @@ extension MapFeature.Core { if let result = result { await send(.mapSearch(.setSearchBarText(result.streetName))) await send(.location(.moveLocation(result.pinPoint))) - await send(.fetchPathLines(result.id)) + await send(.addMapAction(.showFocus(result))) await send(.flowerSpotDetail(.requestDetailInfo(result.id))) + } else { + await send(.addMapAction(.clearFocus)) } } } diff --git a/Projects/Feature/Map/MapFeature/Sources/MapFeature/SubFeature/LocationFeature.swift b/Projects/Feature/Map/MapFeature/Sources/MapFeature/SubFeature/LocationFeature.swift index 32c80577..7983b5e0 100644 --- a/Projects/Feature/Map/MapFeature/Sources/MapFeature/SubFeature/LocationFeature.swift +++ b/Projects/Feature/Map/MapFeature/Sources/MapFeature/SubFeature/LocationFeature.swift @@ -37,7 +37,7 @@ extension LocationFeature { case let .moveLocation(point): state.point = point - return .none + return .send(.delegate(.moveToLocation(point))) case let .currentButtonTapped(isTapped): state.isCurrentButtonTap = isTapped @@ -77,10 +77,7 @@ extension LocationFeature { extension LocationFeature.Core { - private func moveUserLocation(isCurrentButtonTap: Bool) -> Effect { - - return .run { send in if let location = await locationClient.requestUserLocation() { await send(.saveUserLocation(location)) diff --git a/Projects/Feature/Map/MapFeatureInterface/Sources/MapFeature/MapFeature.swift b/Projects/Feature/Map/MapFeatureInterface/Sources/MapFeature/MapFeature.swift index 01e2adb3..002e2d51 100644 --- a/Projects/Feature/Map/MapFeatureInterface/Sources/MapFeature/MapFeature.swift +++ b/Projects/Feature/Map/MapFeatureInterface/Sources/MapFeature/MapFeature.swift @@ -47,15 +47,14 @@ public struct MapFeature { public var flowerSpots: [Int: FlowerSpotEntity] = [:] /// 현재 그려져있는 경로 public var selectedPathLines: [Coordinate] = [] - /// 지도에 마커 및 경로 비활성화 트리거 - public var isNeedDeleteMarker: Bool = false - /// 지도에 마커 및 경로 그리기 트리거 - public var isNeedDrawMarker: Bool = false - /// 지도 범위 요청 트리거 - public var requestMapBound: Bool = false + /// 초기 지도 범위 요청 트리거 (초기 진입용) + public var shouldRequestInitialBounds: Bool = false /// 현위치 재검색 버튼 활성화 여부 public var researchButtonEnable: Bool = false + /// 지도 액션 명령 큐 + public var mapActions: [MapAction] = [] + public var toastMessage: String? = nil public var toastLabel: String? = nil @@ -91,6 +90,7 @@ public struct MapFeature { case viewDidAppear case requestMapBounds(Bool) + case addMapAction(MapAction) case markerTapped(id: Int?) case fetchPathLines(Int) case fetchDetailInfo(Int) diff --git a/Projects/Feature/Map/MapFeatureInterface/Sources/MapFeature/SubFeature/LocationFeature.swift b/Projects/Feature/Map/MapFeatureInterface/Sources/MapFeature/SubFeature/LocationFeature.swift index 6fb57a3d..2748d34c 100644 --- a/Projects/Feature/Map/MapFeatureInterface/Sources/MapFeature/SubFeature/LocationFeature.swift +++ b/Projects/Feature/Map/MapFeatureInterface/Sources/MapFeature/SubFeature/LocationFeature.swift @@ -56,6 +56,8 @@ public struct LocationFeature { case storeFlowerData([FlowerSpotEntity]) /// 상위로 전달하여 하위 state에 동기화 case storeUserLocation(Coordinate?) + /// 지도 위치 이동 요청 + case moveToLocation(Coordinate) case showToastView(message: String?, buttonLabel: String?) case presentAlert(type: AlertType) diff --git a/Projects/Feature/Map/MapFeatureInterface/Sources/MapView/MapView.swift b/Projects/Feature/Map/MapFeatureInterface/Sources/MapView/MapView.swift index 0edc1629..88601e25 100644 --- a/Projects/Feature/Map/MapFeatureInterface/Sources/MapView/MapView.swift +++ b/Projects/Feature/Map/MapFeatureInterface/Sources/MapView/MapView.swift @@ -88,24 +88,14 @@ extension MapView { @ViewBuilder private var mapView: some View { MapViewRepresentable( - userLocation: $store.location.point, flowerPositions: $store.flowerSpots, - newPath: $store.selectedPathLines, - requestBounds: $store.requestMapBound, isCameraMove: $store.researchButtonEnable, - focusData: $store.mapSearch.searchResult, - isNeedDeleteMarker: $store.isNeedDeleteMarker, - isNeedDrawMarker: $store.isNeedDrawMarker, - updateMarkerStatus: Binding( - get: { store.flowerSpotDetail?.updateMarkerStatus }, - set: { _ in } - ), - hasBottomSheet: $store.mapSearch.isShowRegionList + hasBottomSheet: $store.mapSearch.isShowRegionList, + mapActions: $store.mapActions, + shouldRequestInitialBounds: $store.shouldRequestInitialBounds ) .onReceiveMapBounds { - if store.requestMapBound { - store.send(.location(.fetchFlowers($0))) - } + store.send(.location(.fetchFlowers($0))) } .onMarkerTapped { id in store.send(.markerTapped(id: id)) diff --git a/Projects/Feature/Map/MapFeatureInterface/Sources/MapView/View/MapViewAction.swift b/Projects/Feature/Map/MapFeatureInterface/Sources/MapView/View/MapViewAction.swift new file mode 100644 index 00000000..abb8186e --- /dev/null +++ b/Projects/Feature/Map/MapFeatureInterface/Sources/MapView/View/MapViewAction.swift @@ -0,0 +1,58 @@ +// +// MapViewAction.swift +// MapFeature +// +// Created by Jiyeon on 3/14/25. +// Copyright © 2025 com.yongin.pida. All rights reserved. +// + +import FlowerSpotClient +import BloomingClient +import Shared +import DesignKit + +/// 지도 뷰에서 발생하는 액션들을 정의하는 열거형 +public enum MapAction: Equatable { + /// 현재 화면에 보이는 지도 영역의 좌표 범위를 요청 + /// - Note: 지도 이동 후 해당 영역의 꽃 명소 데이터를 가져올 때 사용 + case requestBounds + + /// 앱 시작 시 또는 최초 위치 설정 후 초기 지도 영역 요청 + /// - Note: 사용자 현재 위치로 이동 완료 후 자동으로 실행 + case requestInitialBounds + + /// 특정 꽃 명소의 개화 상태에 따라 마커 표시 상태를 업데이트 + /// - Parameter BloomStatus: 개화 상태 (피지않음, 피기시작, 만개, 져감) + case updateMarkerStatus(BloomStatus) + + /// 지도에 그려진 경로선과 마커들을 모두 삭제 + /// - Note: 새로운 경로를 그리기 전이나 초기화할 때 사용 + case deletePath + + /// 사용자의 현재 위치로 지도 카메라를 이동 + /// - Parameter Coordinate: 이동할 좌표 (위도, 경도) + case moveToUserLocation(Coordinate) + + /// 특정 꽃 명소까지의 경로를 지도에 그리기 + /// - Parameters: + /// - FlowerSpotEntity: 목적지 꽃 명소 정보 + /// - [Coordinate]: 경로를 구성하는 좌표 배열 + case drawPath(FlowerSpotEntity, [Coordinate]) + + /// 활성화된 마커를 변경 (선택된 상태로 표시) + /// - Parameter FlowerSpotEntity: 활성화할 꽃 명소 정보 + case changeActiveMarker(FlowerSpotEntity) + + /// 특정 꽃 명소에 포커스 (경로 그리기 + 마커 활성화) + /// - Parameter FlowerSpotEntity: 포커스할 꽃 명소 정보 + /// - Note: 검색 결과나 상세 정보에서 해당 위치를 강조할 때 사용 + case showFocus(FlowerSpotEntity) + + /// 현재 포커스된 요소들을 모두 제거 + /// - Note: 검색 결과나 상세 정보 해제 시 사용 + case clearFocus + + /// 지도에 표시할 꽃 명소 마커들을 업데이트 + /// - Parameter [Int: FlowerSpotEntity]: 꽃 명소 ID를 키로 하는 딕셔너리 + case updateMarkers([Int: FlowerSpotEntity]) +} diff --git a/Projects/Feature/Map/MapFeatureInterface/Sources/MapView/View/MapViewCoordinator.swift b/Projects/Feature/Map/MapFeatureInterface/Sources/MapView/View/MapViewCoordinator.swift index 1ffc6996..12be357d 100644 --- a/Projects/Feature/Map/MapFeatureInterface/Sources/MapView/View/MapViewCoordinator.swift +++ b/Projects/Feature/Map/MapFeatureInterface/Sources/MapView/View/MapViewCoordinator.swift @@ -56,7 +56,6 @@ extension MapViewRepresentable { func mapView(_ mapView: NMFMapView, didTapMap latlng: NMGLatLng, point: CGPoint) { if let onMarkerTapped = parent.onMarkerTapped { onMarkerTapped(nil) - parent.isNeedDeleteMarker = true } if let _ = focusMarker { deleteSearchResult() @@ -75,7 +74,6 @@ extension MapViewRepresentable { func deleteSearchResult() { deletePath() - parent.focusData = nil if let searchMarker = focusMarker { searchMarker.mapView = nil } @@ -86,10 +84,8 @@ extension MapViewRepresentable { func mapViewCameraIdle(_ mapView: NMFMapView) { // 앱 처음 진입 시 카메라 이동 완료 후 지도 범위 값 가져오도록 처리 - if isInitialBounds, parent.requestBounds { - parent.currentVisibleBounds(on: mapView) - parent.requestBounds = false - isInitialBounds = false + if isInitialBounds, parent.shouldRequestInitialBounds { + parent.mapActions.append(.requestInitialBounds) } } @@ -140,7 +136,6 @@ extension MapViewRepresentable { } deletePath() deleteMarker() - parent.isNeedDeleteMarker = false selectedPin = data activeMarker = marker @@ -149,9 +144,11 @@ extension MapViewRepresentable { /// 마커 개화 상태 값 업데이트 func updateMarker(state: BloomStatus) { if let activeMarker = activeMarker { + guard selectedPin?.bloomingStatus != state.rawValue else { return } selectedPin?.bloomingStatus = state.rawValue activeMarker.iconImage = NMFOverlayImage(image: state.activeImage) } else if let focusMarker = focusMarker { + guard focusData?.bloomingStatus != state.rawValue else { return } focusMarker.iconImage = NMFOverlayImage(image: state.activeImage) } if let path = paths, @@ -162,7 +159,6 @@ extension MapViewRepresentable { startMarker.iconImage = NMFOverlayImage(image: state.circleImage) endMarker.iconImage = NMFOverlayImage(image: state.circleImage) } - parent.updateMarkerStatus = nil } } diff --git a/Projects/Feature/Map/MapFeatureInterface/Sources/MapView/View/MapViewRepresentable.swift b/Projects/Feature/Map/MapFeatureInterface/Sources/MapView/View/MapViewRepresentable.swift index ac737916..d7411df1 100644 --- a/Projects/Feature/Map/MapFeatureInterface/Sources/MapView/View/MapViewRepresentable.swift +++ b/Projects/Feature/Map/MapFeatureInterface/Sources/MapView/View/MapViewRepresentable.swift @@ -14,38 +14,21 @@ import BloomingClient import Shared struct MapViewRepresentable: UIViewRepresentable { - /// 사용자의 현재 위치 정보 - /// - /// - 초기 및 현위치 버튼 탭 시에만 값이 채워져 있음 - /// - 현 위치 이동 플래그 기능과 유사하게 동작 - @Binding var userLocation: Coordinate? /// 지도에 보여줄 데이터 @Binding var flowerPositions: [Int: FlowerSpotEntity] - /// 마커 탭 시 경로를 보여주기 위한 프로퍼티 - @Binding var newPath: [Coordinate] - - /// 지도 범위 요청 프로퍼티 - @Binding var requestBounds: Bool - /// 지도를 움직일 경우 현 위치 재검색 버튼 활성화 하기 위한 트리거 @Binding var isCameraMove: Bool - /// 지도에 특정 위치를 표시하기 위한 프로퍼티 - @Binding var focusData: FlowerSpotEntity? - - /// 마커 삭제 트리거 - @Binding var isNeedDeleteMarker: Bool - - /// 마커 그리기 트리거 - @Binding var isNeedDrawMarker: Bool - - /// 활성화 된 마커 상태를 업데이트 - @Binding var updateMarkerStatus: BloomStatus? - /// 바텀시트 표시 여부 (카메라 중앙 위치 조정용) @Binding var hasBottomSheet: Bool + /// 지도 액션 명령 큐 + @Binding var mapActions: [MapAction] + + /// 초기 bounds 요청 여부 (현재 위치 이동 완료 후 자동 실행용) + @Binding var shouldRequestInitialBounds: Bool + /// 마커 탭 시 id값을 전달하기 위한 클로저 var onMarkerTapped: ((Int?) -> Void)? = nil /// 지도 범위 좌표 값을 전달하기 위한 클로저 @@ -78,68 +61,95 @@ struct MapViewRepresentable: UIViewRepresentable { } func updateUIView(_ uiView: NMFNaverMapView, context: Context) { - if let userLocation = userLocation { - moveUserLocation(uiView, to: userLocation, context: context) - } - - // 마커 데이터가 변경되었을 때 마커 업데이트 - if context.coordinator.currentFlowerPositions != flowerPositions { - // 마커 ID 세트가 변경된 경우에만 카메라 이동 (데이터만 갱신된 경우 스킵) - let shouldMoveCamera = Set(context.coordinator.currentFlowerPositions.keys) != Set(flowerPositions.keys) - - // 기존 마커 삭제 - if !context.coordinator.markers.isEmpty { - context.coordinator.deleteAllMarkers() - } - - if !flowerPositions.isEmpty { - // 새로운 마커 표시 - presentMarkers(uiView, flowers: flowerPositions, context: context, shouldMoveCamera: shouldMoveCamera) - context.coordinator.currentFlowerPositions = flowerPositions - } - } - // 마커 탭 이벤트 시 - if let data = context.coordinator.selectedPin { - if isNeedDrawMarker, newPath != context.coordinator.drawPathPoints { - drawPathLine(uiView, data: data, for: newPath, context: context) - - } else if isNeedDeleteMarker { // 그려져있는 마커 및 경로 삭제 - context.coordinator.deletePath() - context.coordinator.deleteMarker() + // 액션 큐 기반 명령 처리 + if !mapActions.isEmpty { + let actionsToExecute = mapActions + mapActions.removeAll() // 먼저 큐 클리어 + for action in actionsToExecute { + executeAction(action, on: uiView, context: context) } } - - // 현 위치 재검색 액션 - if requestBounds, !context.coordinator.isInitialBounds { - currentVisibleBounds(on: uiView.mapView) - deleteDrawMarker(context: context) - requestBounds = false - } - - // 특정 위치에 나타날 데이터가 있을 경우 - if let focusData = focusData, context.coordinator.focusData != focusData { - drawPathLine(uiView, data: focusData, for: focusData.path, context: context) - drawFocusMarker(uiView, result: focusData, context: context) - } else if focusData == nil, context.coordinator.focusData != nil { - context.coordinator.deleteSearchResult() - } - - if let state = updateMarkerStatus { - context.coordinator.updateMarker(state: state) - } } func makeCoordinator() -> Coordinator { return Coordinator(self) } + /// 액션 기반 명령 실행 + private func executeAction( + _ action: MapAction, + on uiView: NMFNaverMapView, + context: Context + ) { + switch action { + case .requestBounds: + if !context.coordinator.isInitialBounds { + currentVisibleBounds(on: uiView.mapView) + deleteDrawMarker(context: context) + } + + case .requestInitialBounds: + currentVisibleBounds(on: uiView.mapView) + context.coordinator.isInitialBounds = false + shouldRequestInitialBounds = false + + case .updateMarkerStatus(let state): + context.coordinator.updateMarker(state: state) + + case .deletePath: + context.coordinator.deletePath() + context.coordinator.deleteMarker() + + case .moveToUserLocation(let location): + moveUserLocation(uiView, to: location, context: context) + + case .drawPath(let data, let pathCoordinates): + drawPathLine(uiView, data: data, for: pathCoordinates, context: context) + + case let .changeActiveMarker(data): + drawFocusMarker(uiView, result: data, context: context) + + case .showFocus(let data): + drawPathLine(uiView, data: data, for: data.path, context: context) + drawFocusMarker(uiView, result: data, context: context) + + case .clearFocus: + context.coordinator.deleteSearchResult() + + case .updateMarkers(let flowers): + updateMarkersAction(uiView, flowers: flowers, context: context) + + } + } + } // MARK: - Action extension MapViewRepresentable { + /// 마커 데이터 업데이트 액션 + private func updateMarkersAction( + _ view: NMFNaverMapView, + flowers: [Int: FlowerSpotEntity], + context: Context + ) { + flowerPositions = flowers + // 기존 마커 삭제 + if !context.coordinator.markers.isEmpty { + context.coordinator.deleteAllMarkers() + } + + if !flowers.isEmpty { + // 새로운 마커 표시 + let shouldMoveCamera = Set(context.coordinator.currentFlowerPositions.keys) != Set(flowerPositions.keys) + presentMarkers(view, flowers: flowers, context: context, shouldMoveCamera: shouldMoveCamera) + context.coordinator.currentFlowerPositions = flowers + + } + } + /// 특정 위치에 마커를 표시 func drawFocusMarker(_ view: NMFNaverMapView, result: FlowerSpotEntity, context: Context) { // 중복 그리기 방지 @@ -147,7 +157,6 @@ extension MapViewRepresentable { if let marker = context.coordinator.focusMarker { marker.mapView = nil } - isNeedDeleteMarker = false context.coordinator.focusData = result let state = BloomStatus(rawValue: result.bloomingStatus) @@ -184,9 +193,7 @@ extension MapViewRepresentable { view.mapView.positionMode = .normal moveCamera(view, to: userLocation) context.coordinator.lastCameraPoint = userLocation - } - self.userLocation = nil } /// 카메라 이동 메서드 @@ -295,7 +302,6 @@ extension MapViewRepresentable { /// 지도에 올라와있는 마커 삭제 func deleteDrawMarker(context: Context) { if !context.coordinator.markers.isEmpty { - flowerPositions.removeAll() context.coordinator.deleteAllMarkers() } } @@ -363,8 +369,6 @@ extension MapViewRepresentable { // 모든 경로 포인트가 보이도록 카메라 조정 fitCameraToPath(view, path: newPath) - - isNeedDrawMarker = false } /// 지도 위에 비활성화 마커를 표시하기 위한 메서드