diff --git a/.gitignore b/.gitignore index 3dd82707..03a865bc 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ fastlane/report.xml fastlane/Preview.html fastlane/screenshots/**/*.png fastlane/test_output +Poppool/Poppool/Infrastructure/*.mobileprovision diff --git a/Poppool/Poppool/Domain/UseCase/MapUseCase.swift b/Poppool/Poppool/Domain/UseCase/MapUseCase.swift index 660ee9d0..75aa2e85 100644 --- a/Poppool/Poppool/Domain/UseCase/MapUseCase.swift +++ b/Poppool/Poppool/Domain/UseCase/MapUseCase.swift @@ -44,7 +44,7 @@ class DefaultMapUseCase: MapUseCase { northEastLon: northEastLon, southWestLat: southWestLat, southWestLon: southWestLon, - categories: categories // ← 그대로 넘긴다 + categories: categories ) .map { $0.map { $0.toDomain() } } } diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift b/Poppool/Poppool/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift index b23047dc..0db59dc6 100644 --- a/Poppool/Poppool/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift +++ b/Poppool/Poppool/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift @@ -209,7 +209,6 @@ final class AdminBottomSheetView: UIView { // MARK: - Public Methods func updateContentVisibility(isCategorySelected: Bool) { - Logger.log(message: "높이 변경 시작: \(isCategorySelected ? "카테고리" : "상태값")", category: .debug) let newHeight: CGFloat = isCategorySelected ? 200 : 160 @@ -220,6 +219,5 @@ final class AdminBottomSheetView: UIView { setNeedsLayout() layoutIfNeeded() - Logger.log(message: "높이 변경 완료", category: .debug) } } diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/Poppool/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index 15e6855b..bd729aef 100644 --- a/Poppool/Poppool/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/Poppool/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -282,15 +282,6 @@ extension PopUpStoreRegisterViewController { self.mainView.timeButton.setTitle("\(startString) ~ \(endString)", for: .normal) } - private func showLoadingIndicator() { - // 로딩 인디케이터 표시 로직 구현 - // 예: Activity Indicator 또는 커스텀 로딩 뷰 표시 - } - - private func hideLoadingIndicator() { - // 로딩 인디케이터 숨김 로직 구현 - } - private func showSuccessAlert(isUpdate: Bool) { let message = isUpdate ? "팝업스토어가 성공적으로 수정되었습니다." : "팝업스토어가 성공적으로 등록되었습니다." let alert = UIAlertController( @@ -322,7 +313,6 @@ extension PopUpStoreRegisterViewController: View { func bind(reactor: Reactor) { // MARK: - Input (View -> Reactor) - // 텍스트 필드 바인딩 self.mainView.nameField.rx.text.orEmpty .distinctUntilChanged() .map { Reactor.Action.updateName($0) } diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminStoreCell.swift b/Poppool/Poppool/Presentation/Scene/Admin/AdminStoreCell.swift index 5ab3d4c4..ef61f033 100644 --- a/Poppool/Poppool/Presentation/Scene/Admin/AdminStoreCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Admin/AdminStoreCell.swift @@ -76,15 +76,10 @@ final class AdminStoreCell: UITableViewCell { // MARK: - Configure func configure(with store: GetAdminPopUpStoreListResponseDTO.PopUpStore) { - Logger.log(message: "셀 데이터 바인딩: \(store)", category: .debug) - titleLabel.text = store.name categoryLabel.text = store.categoryName statusChip.text = "운영" - - // mainImageUrl에서 baseURL 부분 제거 let imagePath = store.mainImageUrl.replacingOccurrences(of: KeyPath.popPoolS3BaseURL, with: "") - Logger.log(message: "이미지 경로: \(imagePath)", category: .debug) storeImageView.setPPImage(path: imagePath) } } diff --git a/Poppool/Poppool/Presentation/Scene/Map/MapView/MapReactor.swift b/Poppool/Poppool/Presentation/Scene/Map/MapView/MapReactor.swift index b0e82322..fdbde5eb 100644 --- a/Poppool/Poppool/Presentation/Scene/Map/MapView/MapReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Map/MapView/MapReactor.swift @@ -350,15 +350,6 @@ final class MapReactor: Reactor { updatedStores.insert(selectedStore, at: 0) // 🔥 선택된 마커를 캐러셀의 첫 번째로 설정 } - Logger.log( - message: """ - Updated viewport stores: - - Total: \(updatedStores.count) - - Selected Store: \(state.selectedStore?.name ?? "None") - """, - category: .debug - ) - newState.viewportStores = updatedStores case let .setSelectedStore(store): diff --git a/Poppool/Poppool/Presentation/Scene/Map/MapView/MapSearchInput.swift b/Poppool/Poppool/Presentation/Scene/Map/MapView/MapSearchInput.swift index e5532286..6e91abe5 100644 --- a/Poppool/Poppool/Presentation/Scene/Map/MapView/MapSearchInput.swift +++ b/Poppool/Poppool/Presentation/Scene/Map/MapView/MapSearchInput.swift @@ -71,14 +71,12 @@ final class MapSearchInput: UIView, View { } func bind(reactor: MapReactor) { - // 텍스트필드 입력 로그 (필요 시 확인) - searchTextField.rx.text.orEmpty - .subscribe(onNext: { text in - print("[DEBUG] TextField Input: \(text)") - }) - .disposed(by: disposeBag) + /// TODO : 검색 재활성화시 사용 +// searchTextField.rx.text.orEmpty +// .subscribe(onNext: { _ in +// }) +// .disposed(by: disposeBag) - // Reactor의 선택된 위치 필터를 텍스트필드에 바인딩 (없으면 기본 문구) reactor.state .map { $0.selectedLocationFilters.first ?? "팝업스토어명을 입력해보세요" } .distinctUntilChanged() @@ -92,14 +90,12 @@ final class MapSearchInput: UIView, View { // MARK: - Gesture Setup private func setupGesture() { - // containerView에 탭 제스처를 추가하여 화면 전환 트리거 let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture)) containerView.addGestureRecognizer(tapGesture) containerView.isUserInteractionEnabled = true } @objc private func handleTapGesture() { - // onSearch 클로저를 호출해서 화면 전환을 진행 onSearch?(searchTextField.text ?? "") } } diff --git a/Poppool/Poppool/Presentation/Scene/Map/MapView/MapViewController+Handlers.swift b/Poppool/Poppool/Presentation/Scene/Map/MapView/MapViewController+Handlers.swift new file mode 100644 index 00000000..be06270d --- /dev/null +++ b/Poppool/Poppool/Presentation/Scene/Map/MapView/MapViewController+Handlers.swift @@ -0,0 +1,344 @@ +// import UIKit +// import NMapsMap +// import ReactorKit +// +// extension MapViewController { +// +// // MARK: - Marker Style Handler +// func updateMarkerStyle(marker: NMFMarker, selected: Bool, isCluster: Bool, count: Int = 1, regionName: String = "") { +// if selected { +// marker.width = 44 +// marker.height = 44 +// marker.iconImage = NMFOverlayImage(name: "TapMarker") +// } else if isCluster { +// marker.width = 36 +// marker.height = 36 +// marker.iconImage = NMFOverlayImage(name: "cluster_marker") +// } else { +// marker.width = 32 +// marker.height = 32 +// marker.iconImage = NMFOverlayImage(name: "Marker") +// } +// +// marker.captionText = (count > 1) ? "\(count)" : "" +// marker.anchor = CGPoint(x: 0.5, y: 1.0) +// } +// +// // MARK: - Map Tap Handler +// @objc func handleMapViewTap(_ gesture: UITapGestureRecognizer) { +// // 리스트 뷰가 보이는 상태가 아닌 경우에만 처리 +// guard !isMovingToMarker else { return } +// +// // 선택된 마커 해제 +// if let currentMarker = self.currentMarker { +// updateMarkerStyle(marker: currentMarker, selected: false, isCluster: false) +// self.currentMarker = nil +// } +// +// // 툴팁 제거 및 관련 상태 초기화 +// currentTooltipView?.removeFromSuperview() +// currentTooltipView = nil +// currentTooltipStores = [] +// currentTooltipCoordinate = nil +// +// // 캐러셀 및 스토어 카드 숨김 처리 +// carouselView.isHidden = true +// carouselView.updateCards([]) +// currentCarouselStores = [] +// mainView.setStoreCardHidden(true, animated: true) +// +// // 클러스터 업데이트 (필요 시 재설정) +// updateMapWithClustering() +// } +// +// // MARK: - Pan Gesture Handler +// @objc func handlePanGesture(_ gesture: UIPanGestureRecognizer) { +// let translation = gesture.translation(in: view) +// let velocity = gesture.velocity(in: view) +// +// switch gesture.state { +// case .changed: +// if let constraint = listViewTopConstraint { +// let currentOffset = constraint.layoutConstraints.first?.constant ?? 0 +// let newOffset = currentOffset + translation.y +// let minOffset: CGFloat = filterContainerBottomY // 필터 컨테이너 바닥 높이 +// let maxOffset: CGFloat = view.frame.height // 최하단 위치 +// let clampedOffset = min(max(newOffset, minOffset), maxOffset) +// +// constraint.update(offset: clampedOffset) +// gesture.setTranslation(.zero, in: view) +// +// if modalState == .top { +// adjustMapViewAlpha(for: clampedOffset, minOffset: minOffset, maxOffset: maxOffset) +// } +// } +// case .ended: +// if let constraint = listViewTopConstraint { +// let currentOffset = constraint.layoutConstraints.first?.constant ?? 0 +// let middleY = view.frame.height * 0.3 +// let targetState: ModalState +// +// // 속도와 현재 오프셋에 따른 상태 결정 +// if velocity.y > 500 { +// targetState = .bottom +// } else if velocity.y < -500 { +// targetState = .top +// } else if currentOffset < middleY * 0.7 { +// targetState = .top +// } else if currentOffset < view.frame.height * 0.7 { +// targetState = .middle +// } else { +// targetState = .bottom +// } +// +// animateToState(targetState) +// } +// default: +// break +// } +// } +// +// // MARK: - Map Alpha Handlers +// func adjustMapViewAlpha(for offset: CGFloat, minOffset: CGFloat, maxOffset: CGFloat) { +// let middleOffset = view.frame.height * 0.3 +// if offset <= minOffset { +// mainView.mapView.alpha = 0 // 완전히 숨김 +// } else if offset >= maxOffset { +// mainView.mapView.alpha = 1 // 완전히 보임 +// } else if offset <= middleOffset { +// let progress = (offset - minOffset) / (middleOffset - minOffset) +// mainView.mapView.alpha = progress +// } else { +// mainView.mapView.alpha = 1 +// } +// } +// +// func updateMapViewAlpha(for offset: CGFloat, minOffset: CGFloat, maxOffset: CGFloat) { +// let progress = (maxOffset - offset) / (maxOffset - minOffset) +// mainView.mapView.alpha = max(0, min(progress, 1)) +// } +// +// // MARK: - Modal Animation Handler +// func animateToState(_ state: ModalState) { +// guard modalState != state else { return } +// self.view.layoutIfNeeded() +// UIView.animate(withDuration: 0.3, animations: { +// switch state { +// case .top: +// let filterChipsFrame = self.mainView.filterChips.convert(self.mainView.filterChips.bounds, to: self.view) +// self.mainView.mapView.alpha = 0 +// self.storeListViewController.setGrabberHandleVisible(false) +// self.listViewTopConstraint?.update(offset: filterChipsFrame.maxY) +// self.mainView.searchInput.setBackgroundColor(.g50) +// +// case .middle: +// self.storeListViewController.setGrabberHandleVisible(true) +// let offset = max(self.view.frame.height * 0.3, self.filterContainerBottomY) +// self.listViewTopConstraint?.update(offset: offset) +// self.storeListViewController.mainView.layer.cornerRadius = 20 +// self.storeListViewController.mainView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] +// self.mainView.mapView.alpha = 1 +// self.mainView.mapView.isHidden = false +// self.mainView.searchInput.setBackgroundColor(.white) +// +// if let reactor = self.reactor { +// reactor.action.onNext(.fetchAllStores) +// reactor.state +// .map { $0.viewportStores } +// .distinctUntilChanged() +// .filter { !$0.isEmpty } +// .take(1) +// .observe(on: MainScheduler.instance) +// .subscribe(onNext: { [weak self] stores in +// self?.fetchStoreDetails(for: stores) +// }) +// .disposed(by: self.disposeBag) +// } +// +// case .bottom: +// self.storeListViewController.setGrabberHandleVisible(true) +// self.listViewTopConstraint?.update(offset: self.view.frame.height) +// self.mainView.mapView.alpha = 1 +// self.mainView.mapView.isHidden = false +// self.mainView.searchInput.setBackgroundColor(.white) +// } +// self.view.layoutIfNeeded() +// }) { _ in +// self.modalState = state +// } +// } +// +// // MARK: - Tooltip & Marker Handlers +// +// func configureTooltip(for marker: NMFMarker, stores: [MapPopUpStore]) { +// // 기존 툴팁 제거 +// currentTooltipView?.removeFromSuperview() +// +// let tooltipView = MarkerTooltipView() +// tooltipView.configure(with: stores) +// tooltipView.selectStore(at: 0) +// +// let markerPoint = mainView.mapView.projection.point(from: marker.position) +// let markerHeight: CGFloat = 32 +// +// tooltipView.frame = CGRect( +// x: markerPoint.x, +// y: markerPoint.y - markerHeight - tooltipView.frame.height - 14, +// width: tooltipView.frame.width, +// height: tooltipView.frame.height +// ) +// +// mainView.addSubview(tooltipView) +// currentTooltipView = tooltipView +// currentTooltipStores = stores +// currentTooltipCoordinate = marker.position +// } +// +// func updateTooltipPosition() { +// guard let marker = currentMarker, let tooltip = currentTooltipView else { return } +// let markerPoint = mainView.mapView.projection.point(from: marker.position) +// var markerCenter = markerPoint +// markerCenter.y -= 20 +// let offsetX: CGFloat = -10 +// let offsetY: CGFloat = -10 +// +// tooltip.frame.origin = CGPoint( +// x: markerCenter.x + offsetX, +// y: markerCenter.y - tooltip.frame.height - offsetY +// ) +// } +// +// // MARK: - Store Selection Handlers +// func handleSingleStoreTap(_ marker: NMFMarker, store: MapPopUpStore) -> Bool { +// isMovingToMarker = true +// +// if let previousMarker = currentMarker { +// updateMarkerStyle(marker: previousMarker, selected: false, isCluster: false) +// } +// +// updateMarkerStyle(marker: marker, selected: true, isCluster: false) +// currentMarker = marker +// +// if currentCarouselStores.isEmpty || !currentCarouselStores.contains(where: { $0.id == store.id }) { +// let bounds = getVisibleBounds() +// let visibleStores = currentStores.filter { store in +// let storePosition = NMGLatLng(lat: store.latitude, lng: store.longitude) +// return NMGLatLngBounds(southWest: bounds.southWest, northEast: bounds.northEast).contains(storePosition) +// } +// +// if !visibleStores.isEmpty { +// currentCarouselStores = visibleStores +// carouselView.updateCards(visibleStores) +// if let index = visibleStores.firstIndex(where: { $0.id == store.id }) { +// carouselView.scrollToCard(index: index) +// } +// } else { +// currentCarouselStores = [store] +// carouselView.updateCards([store]) +// } +// } else { +// if let index = currentCarouselStores.firstIndex(where: { $0.id == store.id }) { +// carouselView.scrollToCard(index: index) +// } +// } +// +// carouselView.isHidden = false +// mainView.setStoreCardHidden(false, animated: true) +// +// if let storeArray = marker.userInfo["storeData"] as? [MapPopUpStore], storeArray.count > 1 { +// configureTooltip(for: marker, stores: storeArray) +// if let index = storeArray.firstIndex(where: { $0.id == store.id }) { +// (currentTooltipView as? MarkerTooltipView)?.selectStore(at: index) +// } +// } else { +// currentTooltipView?.removeFromSuperview() +// currentTooltipView = nil +// } +// +// isMovingToMarker = false +// return true +// } +// +// func handleRegionalClusterTap(_ marker: NMFMarker, clusterData: ClusterMarkerData) -> Bool { +// let currentZoom = mainView.mapView.zoomLevel +// let currentLevel = MapZoomLevel.getLevel(from: Float(currentZoom)) +// +// switch currentLevel { +// case .city: +// let districtZoomLevel: Double = 10.0 +// let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position, zoomTo: districtZoomLevel) +// cameraUpdate.animation = .easeIn +// cameraUpdate.animationDuration = 0.3 +// mainView.mapView.moveCamera(cameraUpdate) +// case .district: +// let detailedZoomLevel: Double = 12.0 +// let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position, zoomTo: detailedZoomLevel) +// cameraUpdate.animation = .easeIn +// cameraUpdate.animationDuration = 0.3 +// mainView.mapView.moveCamera(cameraUpdate) +// default: +// break +// } +// +// updateMarkersForCluster(stores: clusterData.cluster.stores) +// carouselView.updateCards(clusterData.cluster.stores) +// carouselView.isHidden = false +// currentCarouselStores = clusterData.cluster.stores +// return true +// } +// +// +// func handleMicroClusterTap(_ marker: NMFMarker, storeArray: [MapPopUpStore]) -> Bool { +// if currentMarker == marker { +// currentTooltipView?.removeFromSuperview() +// currentTooltipView = nil +// currentTooltipStores = [] +// currentTooltipCoordinate = nil +// carouselView.isHidden = true +// carouselView.updateCards([]) +// currentCarouselStores = [] +// updateMarkerStyle(marker: marker, selected: false, isCluster: false, count: storeArray.count) +// currentMarker = nil +// isMovingToMarker = false +// return false +// } +// +// isMovingToMarker = true +// currentTooltipView?.removeFromSuperview() +// currentTooltipView = nil +// +// if let previousMarker = currentMarker { +// updateMarkerStyle(marker: previousMarker, selected: false, isCluster: false) +// } +// +// updateMarkerStyle(marker: marker, selected: true, isCluster: false, count: storeArray.count) +// currentMarker = marker +// +// currentCarouselStores = storeArray +// carouselView.updateCards(storeArray) +// carouselView.isHidden = false +// carouselView.scrollToCard(index: 0) +// +// mainView.setStoreCardHidden(false, animated: true) +// +// let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position) +// cameraUpdate.animation = .easeIn +// cameraUpdate.animationDuration = 0.3 +// mainView.mapView.moveCamera(cameraUpdate) +// +// if storeArray.count > 1 { +// DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in +// guard let self = self else { return } +// self.configureTooltip(for: marker, stores: storeArray) +// self.isMovingToMarker = false +// } +// } +// +// return true +// } +// +// // MARK: - Toast Helper +// private func showNoMarkersToast() { +// Logger.log(message: "현재 지도 영역에 표시할 마커가 없습니다", category: .debug) +// } +// } diff --git a/Poppool/Poppool/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/Poppool/Presentation/Scene/Map/MapView/MapViewController.swift index e327b463..aef8a633 100644 --- a/Poppool/Poppool/Presentation/Scene/Map/MapView/MapViewController.swift +++ b/Poppool/Poppool/Presentation/Scene/Map/MapView/MapViewController.swift @@ -48,7 +48,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private var filterChipsTopY: CGFloat = 0 private var filterContainerBottomY: CGFloat { let frameInView = mainView.filterChips.convert(mainView.filterChips.bounds, to: view) - return frameInView.maxY // 필터 컨테이너의 바닥 높이 + return frameInView.maxY } enum ModalState { @@ -89,7 +89,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM if let reactor = self.reactor { reactor.action.onNext(.fetchCategories) - // 한국 전체 영역에 대한 경계값 설정 let koreaRegion = ( northEast: NMGLatLng(lat: 38.0, lng: 132.0), southWest: NMGLatLng(lat: 33.0, lng: 124.0) @@ -194,13 +193,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } private func configureTooltip(for marker: NMFMarker, stores: [MapPopUpStore]) { - Logger.log(message: """ - 툴팁 설정: - - 현재 캐러셀 스토어: \(currentCarouselStores.map { $0.name }) - - 마커 스토어: \(stores.map { $0.name }) - """, category: .debug) - // 기존 툴팁 제거 self.currentTooltipView?.removeFromSuperview() let tooltipView = MarkerTooltipView() @@ -217,11 +210,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.updateMarkerStyle(marker: marker, selected: true, isCluster: false, count: stores.count) tooltipView.selectStore(at: index) - Logger.log(message: """ - 툴팁 선택: - - 선택된 스토어: \(stores[index].name) - - 툴팁 인덱스: \(index) - """, category: .debug) } let markerPoint = self.mainView.mapView.projection.point(from: marker.position) @@ -272,8 +260,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM setupPanAndSwipeGestures() let mapViewTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleMapViewTap(_:))) -// mapViewTapGesture.cancelsTouchesInView = false // 중요: 다른 터치 이벤트를 방해하지 않음 - mapViewTapGesture.delaysTouchesBegan = false // 터치 지연 없음 + mapViewTapGesture.delaysTouchesBegan = false mainView.mapView.addGestureRecognizer(mapViewTapGesture) mapViewTapGesture.delegate = self } @@ -284,7 +271,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .skip(1) .withUnretained(self) .subscribe { owner, _ in - Logger.log(message: "⬆️ 위로 스와이프 감지", category: .debug) switch owner.modalState { case .bottom: owner.animateToState(.middle) @@ -300,7 +286,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .skip(1) .withUnretained(self) .subscribe { owner, _ in - Logger.log(message: "⬇️ 아래로 스와이프 감지됨", category: .debug) switch owner.modalState { case .top: owner.animateToState(.middle) @@ -330,7 +315,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM mainView.listButton.rx.tap .withUnretained(self) .subscribe { owner, _ in - owner.animateToState(.middle) // 버튼 눌렀을 때 상태를 middle로 변경 + owner.animateToState(.middle) } .disposed(by: disposeBag) @@ -382,7 +367,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // 필터 제거 액션 self.reactor?.action.onNext(.clearFilters(.category)) - // 현재 뷰포트의 바운드로 마커 업데이트 요청 let bounds = self.getVisibleBounds() self.reactor?.action.onNext(.viewportChanged( northEastLat: bounds.northEast.lat, @@ -402,7 +386,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM reactor.state.map { $0.selectedLocationFilters }.distinctUntilChanged(), reactor.state.map { $0.selectedCategoryFilters }.distinctUntilChanged() ) { locationFilters, categoryFilters -> (String, String) in - // 지역 필터 텍스트 포맷팅 let locationText: String if locationFilters.isEmpty { locationText = "지역선택" @@ -411,8 +394,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } else { locationText = locationFilters[0] } - - // 카테고리 필터 텍스트 포맷팅 let categoryText: String if categoryFilters.isEmpty { categoryText = "카테고리" @@ -482,14 +463,12 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .bind { [weak self] results in guard let self = self else { return } - // 이전 선택된 마커, 툴팁, 캐러셀 초기화 self.clearAllMarkers() self.storeListViewController.reactor?.action.onNext(.setStores([])) self.carouselView.updateCards([]) self.carouselView.isHidden = true self.resetSelectedMarker() // 추가된 부분 - // 결과가 없으면 스토어 카드 숨김 후 종료 if results.isEmpty { self.mainView.setStoreCardHidden(true, animated: true) return @@ -546,9 +525,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // 중요: 마커에 직접 터치 핸들러 추가 marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } - - Logger.log(message: "마커 터치됨! 위치: \(marker.position), 스토어: \(store.name)", category: .debug) - // 단일 스토어 마커 처리 return self.handleSingleStoreTap(marker, store: store) } @@ -584,7 +560,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM @objc private func handleMapViewTap(_ gesture: UITapGestureRecognizer) { // 리스트뷰가 현재 보이는 상태(중간 또는 상단)일 때만 내림 if modalState == .middle || modalState == .top { - Logger.log(message: "맵뷰 탭 감지: 리스트뷰 내림", category: .debug) animateToState(.bottom) } } @@ -618,10 +593,9 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM let middleY = view.frame.height * 0.3 // 중간 지점 기준 높이 let targetState: ModalState - // 속도와 위치를 기반으로 상태 결정 - if velocity.y > 500 { // 아래로 빠르게 드래그 + if velocity.y > 500 { targetState = .bottom - } else if velocity.y < -500 { // 위로 빠르게 드래그 + } else if velocity.y < -500 { targetState = .top } else if currentOffset < middleY * 0.7 { targetState = .top @@ -717,7 +691,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.view.layoutIfNeeded() }) { _ in self.modalState = state - Logger.log(message: ". 현재 상태: \(state)", category: .debug) } } @@ -741,7 +714,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM count: count, isMultiMarker: false) markerView.injection(with: input) - // 프레임이 설정되어 있지 않다면 적당한 크기로 지정 (예: 80x32) if markerView.frame == .zero { markerView.frame = CGRect(x: 0, y: 0, width: 80, height: 32) } @@ -751,8 +723,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private func updateMapWithClustering() { let currentZoom = mainView.mapView.zoomLevel let level = MapZoomLevel.getLevel(from: Float(currentZoom)) - // 클러스터 처리 시 현재 스토어 목록(currentStores)을 사용 - Logger.log(message: "현재 줌 레벨: \(currentZoom), 모드: \(level), 스토어 수: \(currentStores.count)", category: .debug) CATransaction.begin() CATransaction.setDisableActions(true) @@ -787,8 +757,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // 직접 터치 핸들러 추가 marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } - - print("개별 마커 터치됨! 스토어: \(store.name)") return self.handleSingleStoreTap(marker, store: store) } @@ -813,8 +781,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // 직접 터치 핸들러 추가 marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } - - print("마이크로 클러스터 마커 터치됨! 스토어 수: \(storeGroup.count)개") return self.handleMicroClusterTap(marker, storeArray: storeGroup) } @@ -848,7 +814,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM var marker: NMFMarker if let existingMarker = clusterMarkerDictionary[clusterKey] { marker = existingMarker - // 위치 업데이트가 필요하면 수정 if marker.position.lat != cluster.cluster.coordinate.lat || marker.position.lng != cluster.cluster.coordinate.lng { marker.position = NMGLatLng(lat: cluster.cluster.coordinate.lat, lng: cluster.cluster.coordinate.lng) @@ -859,17 +824,14 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } marker.position = NMGLatLng(lat: cluster.cluster.coordinate.lat, lng: cluster.cluster.coordinate.lng) - marker.userInfo = ["clusterData": cluster] // 중요: userInfo에 cluster 객체를 직접 저장 + marker.userInfo = ["clusterData": cluster] - // 여기서 커스텀 클러스터 마커 뷰를 이미지로 변환하여 적용합니다. if let clusterImage = createClusterMarkerImage(regionName: cluster.cluster.name, count: cluster.storeCount) { marker.iconImage = NMFOverlayImage(image: clusterImage) } else { - // 기본 에셋 fallback (원하는 경우) marker.iconImage = NMFOverlayImage(name: "cluster_marker") } - // 터치 핸들러 추가 - userInfo에서 클러스터 데이터를 직접 가져오기 marker.touchHandler = { [weak self] (overlay) -> Bool in guard let self = self, let tappedMarker = overlay as? NMFMarker, @@ -1018,7 +980,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM currentFilterBottomSheet = nil } - // 기본 마커 private func addMarkers(for stores: [MapPopUpStore]) { markerDictionary.values.forEach { $0.mapView = nil } markerDictionary.removeAll() @@ -1033,8 +994,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // 직접 터치 핸들러 추가 marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } - - print("검색 결과 마커 터치됨! 스토어: \(store.name)") return self.handleSingleStoreTap(marker, store: store) } @@ -1096,13 +1055,13 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM locationManager.requestWhenInUseAuthorization() case .authorizedWhenInUse, .authorizedAlways: locationManager.startUpdatingLocation() - mainView.mapView.positionMode = .direction // 내 위치 트래킹 모드 활성화 + mainView.mapView.positionMode = .direction case .denied, .restricted: Logger.log( message: "위치 서비스가 비활성화되었습니다. 설정에서 권한을 확인해주세요.", category: .error ) - mainView.mapView.positionMode = .disabled // 내 위치 트래킹 모드 비활성화 + mainView.mapView.positionMode = .disabled @unknown default: break } @@ -1111,14 +1070,11 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private func updateTooltipPosition() { guard let marker = currentMarker, let tooltip = currentTooltipView else { return } - // 마커 위치를 화면 좌표로 변환 let markerPoint = mainView.mapView.projection.point(from: marker.position) var markerCenter = markerPoint - // 마커 높이 고려 (네이버 마커는 크기가 다를 수 있음) - markerCenter.y = markerPoint.y - 20 // 마커 이미지 높이의 절반 정도 + markerCenter.y = markerPoint.y - 20 - // 오프셋 값 (디자인에 맞게 조정) let offsetX: CGFloat = -10 let offsetY: CGFloat = -10 @@ -1134,7 +1090,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM updateMarkerStyle(marker: currentMarker, selected: false, isCluster: false) } - // 툴팁 제거 currentTooltipView?.removeFromSuperview() currentTooltipView = nil currentTooltipStores = [] @@ -1148,7 +1103,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } private func updateMarkersForCluster(stores: [MapPopUpStore]) { - // 전체 개별 및 클러스터 마커 제거 for marker in individualMarkerDictionary.values { marker.mapView = nil } @@ -1159,7 +1113,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } clusterMarkerDictionary.removeAll() - // 클러스터에 포함된 스토어들만 새 마커 추가 for store in stores { let marker = NMFMarker() marker.position = NMGLatLng(lat: store.latitude, lng: store.longitude) @@ -1168,11 +1121,9 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM updateMarkerStyle(marker: marker, selected: false, isCluster: false) - // 직접 터치 핸들러 추가 marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } - print("클러스터 내 마커 터치됨! 스토어: \(store.name)") return self.handleSingleStoreTap(marker, store: store) } @@ -1182,7 +1133,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } private func findMarkerForStore(for store: MapPopUpStore) -> NMFMarker? { - // individualMarkerDictionary에 저장된 모든 마커를 순회 for marker in individualMarkerDictionary.values { if let singleStore = marker.userInfo["storeData"] as? MapPopUpStore, singleStore.id == store.id { return marker @@ -1203,7 +1153,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } private func fetchStoreDetails(for stores: [MapPopUpStore]) { - // 빈 목록이면 처리하지 않음 guard !stores.isEmpty else { return } // 먼저 기본 정보로 StoreItem 생성하여 순서 유지 @@ -1222,7 +1171,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // 리스트에는 모든 스토어 정보 표시 (필터링된 모든 스토어) self.storeListViewController.reactor?.action.onNext(.setStores(initialStoreItems)) - // 각 스토어의 상세 정보를 병렬로 가져와서 업데이트 (북마크 정보 등) stores.forEach { store in self.popUpAPIUseCase.getPopUpDetail( commentType: "NORMAL", @@ -1241,12 +1189,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } func bindViewport(reactor: MapReactor) { - // 카메라 이동 완료 시 이벤트 발생되는 Subject let cameraObservable = PublishSubject() - - // NMFMapViewCameraDelegate 메서드에서 호출할 수 있도록 설정 - - // 카메라 변경 감지해서 액션 전달 cameraObservable .throttle(.milliseconds(200), scheduler: MainScheduler.instance) .map { [unowned self] _ -> MapReactor.Action in @@ -1261,7 +1204,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .bind(to: reactor.action) .disposed(by: disposeBag) - // 현재 뷰포트 내의 스토어 업데이트 - 초기 1회 reactor.state .map { $0.viewportStores } .distinctUntilChanged() @@ -1373,7 +1315,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private func findAndShowNearestStore(from location: CLLocation) { guard !currentStores.isEmpty else { - Logger.log(message: "현재위치 표기할 스토어가 없습니다", category: .debug) return } @@ -1386,7 +1327,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } if let store = nearestStore, let marker = findMarkerForStore(for: store) { - // 카메라 이동 없이 선택된 마커만 업데이트 _ = handleSingleStoreTap(marker, store: store) } // 마커가 없다면 새로 생성 @@ -1413,7 +1353,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM func handleSingleStoreTap(_ marker: NMFMarker, store: MapPopUpStore) -> Bool { isMovingToMarker = true - // 이전 마커 선택 상태 해제 if let previousMarker = currentMarker { updateMarkerStyle(marker: previousMarker, selected: false, isCluster: false) } @@ -1476,18 +1415,12 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // 리전 클러스터 탭 처리 func handleRegionalClusterTap(_ marker: NMFMarker, clusterData: ClusterMarkerData) -> Bool { - print("handleRegionalClusterTap 함수 호출됨") let currentZoom = mainView.mapView.zoomLevel let currentLevel = MapZoomLevel.getLevel(from: Float(currentZoom)) - // 디버깅 - print("현재 줌 레벨: \(currentZoom), 모드: \(currentLevel)") - print("클러스터 정보: \(clusterData.cluster.name), 스토어 수: \(clusterData.storeCount)") - switch currentLevel { case .city: // 시 단위 클러스터 - print("시 단위 클러스터 처리") let districtZoomLevel: Double = 10.0 let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position, zoomTo: districtZoomLevel) cameraUpdate.animation = .easeIn @@ -1495,14 +1428,14 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM mainView.mapView.moveCamera(cameraUpdate) case .district: // 구 단위 클러스터 - print("구 단위 클러스터 처리") let detailedZoomLevel: Double = 12.0 let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position, zoomTo: detailedZoomLevel) cameraUpdate.animation = .easeIn cameraUpdate.animationDuration = 0.3 mainView.mapView.moveCamera(cameraUpdate) - default: + default: print("기타 레벨 클러스터 처리") + } // 클러스터에 포함된 스토어들만 표시하도록 마커 업데이트 @@ -1576,7 +1509,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } private func showNoMarkersToast() { - // 디자인 예정이므로 임시 구현 Logger.log(message: "현재 지도 영역에 표시할 마커가 없습니다", category: .debug) } } @@ -1604,33 +1536,26 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // MARK: - NMFMapViewTouchDelegate extension MapViewController { - // 마커 탭 이벤트 처리 // 마커 탭 이벤트 처리 func mapView(_ mapView: NMFMapView, didTap marker: NMFMarker) -> Bool { - Logger.log(message: "didTapMarker 호출됨: \(marker.position), userInfo: \(marker.userInfo)", category: .debug) // 클러스터 마커 확인 if let clusterData = marker.userInfo["clusterData"] as? ClusterMarkerData { - Logger.log(message: "클러스터 데이터 감지: \(clusterData.cluster.name), 스토어 수: \(clusterData.storeCount)", category: .debug) return handleRegionalClusterTap(marker, clusterData: clusterData) } // 마이크로 클러스터 또는 단일 스토어 마커 확인 else if let storeArray = marker.userInfo["storeData"] as? [MapPopUpStore] { if storeArray.count > 1 { - Logger.log(message: "마이크로 클러스터 감지: \(storeArray.count)개 스토어", category: .debug) return handleMicroClusterTap(marker, storeArray: storeArray) } else if let singleStore = storeArray.first { - Logger.log(message: "단일 스토어 감지: \(singleStore.name)", category: .debug) return handleSingleStoreTap(marker, store: singleStore) } } // 단일 스토어 마커 (배열이 아닌 경우) 확인 else if let singleStore = marker.userInfo["storeData"] as? MapPopUpStore { - Logger.log(message: "단일 스토어 감지: \(singleStore.name)", category: .debug) return handleSingleStoreTap(marker, store: singleStore) } - Logger.log(message: "인식할 수 없는 마커 타입", category: .error) return false } @@ -1717,7 +1642,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // MARK: - UIGestureRecognizerDelegate extension MapViewController { - // 맵뷰의 다른 제스처와 충돌하지 않도록 함 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { // 맵의 내장 제스처와 동시 인식 허용 return true @@ -1725,7 +1649,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // 리스트뷰가 보일 때만 커스텀 탭 제스처 허용 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { - // 터치가 리스트뷰 영역에 있으면 커스텀 제스처 트리거하지 않음 let touchPoint = touch.location(in: view) // 리스트뷰가 보이고 터치가 리스트뷰 위에 있으면 탭 처리하지 않음 diff --git a/Poppool/Poppool/Presentation/Scene/Map/MapView/MarkerTooltipView.swift b/Poppool/Poppool/Presentation/Scene/Map/MapView/MarkerTooltipView.swift index a1c2bffe..f53405ef 100644 --- a/Poppool/Poppool/Presentation/Scene/Map/MapView/MarkerTooltipView.swift +++ b/Poppool/Poppool/Presentation/Scene/Map/MapView/MarkerTooltipView.swift @@ -30,7 +30,7 @@ final class MarkerTooltipView: UIView, UIGestureRecognizerDelegate { // MARK: - Initialization override init(frame: CGRect) { super.init(frame: frame) - self.frame.size = CGSize(width: 200, height: 100) // 임시 높이로 시작 + self.frame.size = CGSize(width: 200, height: 100) setupLayout() // setupGestures() } @@ -71,15 +71,10 @@ final class MarkerTooltipView: UIView, UIGestureRecognizerDelegate { // 기존 뷰 제거 stackView.arrangedSubviews.forEach { $0.removeFromSuperview() } - print("🗨️ 툴팁 구성") - print("📋 입력받은 스토어: \(stores.map { $0.name })") - - // stores 배열 순서대로 처리 for (index, store) in stores.enumerated() { let rowContainer = createRow(for: store, at: index) stackView.addArrangedSubview(rowContainer) - // 구분선 추가 (마지막 아이템 제외) if index < stores.count - 1 { let separator = createSeparator() stackView.addArrangedSubview(separator) @@ -89,7 +84,6 @@ final class MarkerTooltipView: UIView, UIGestureRecognizerDelegate { // 레이아웃 업데이트 layoutIfNeeded() - // 컨텐츠 크기에 맞게 높이 조정 let height = stackView.systemLayoutSizeFitting( CGSize(width: 200, height: UIView.layoutFittingCompressedSize.height) ).height + 24 @@ -156,10 +150,6 @@ final class MarkerTooltipView: UIView, UIGestureRecognizerDelegate { @objc private func handleRowTap(_ gesture: UITapGestureRecognizer) { guard let row = gesture.view else { return } let index = row.tag - - print("🗨️ 툴팁 탭") - print("👆 탭된 인덱스: \(index)") - gesture.cancelsTouchesInView = true selectStore(at: index) onStoreSelected?(index) @@ -186,11 +176,9 @@ final class MarkerTooltipView: UIView, UIGestureRecognizerDelegate { else { continue } if row.tag == index { - // 선택된 행 label.font = .boldSystemFont(ofSize: 12) bulletView.backgroundColor = .jd500 } else { - // 선택되지 않은 행 label.font = .systemFont(ofSize: 12) bulletView.backgroundColor = .clear } diff --git a/Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListReactor.swift b/Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListReactor.swift index 21e54ec8..aeb6b948 100644 --- a/Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListReactor.swift @@ -9,10 +9,6 @@ final class StoreListReactor: Reactor { private let popUpAPIUseCase: PopUpAPIUseCaseImpl private let bookmarkStateRelay = PublishRelay<(Int64, Bool)>() -// private var currentPage = 0 -// private let pageSize = 10 -// private var hasMorePages = true - enum Action { case syncBookmarkStatus(storeId: Int64, isBookmarked: Bool) case didSelectItem(Int) @@ -76,13 +72,12 @@ final class StoreListReactor: Reactor { // Int64 → Int32 변환 필요 guard let idInt32 = Int32(exactly: store.id) else { - Logger.log(message: "ID 값이 Int32 범위를 초과했습니다: \(store.id)", category: .error) return .empty() } return popUpAPIUseCase.getPopUpDetail( commentType: "NORMAL", - popUpStoredId: Int64(idInt32) // Int32 → Int64 변환 + popUpStoredId: Int64(idInt32) ) .flatMap { detail -> Observable in if detail.bookmarkYn != store.isBookmarked { diff --git a/Poppool/Poppool/Presentation/Utills/Common/FilterType.swift b/Poppool/Poppool/Presentation/Utills/Common/FilterType.swift index b19e7c64..3825b486 100644 --- a/Poppool/Poppool/Presentation/Utills/Common/FilterType.swift +++ b/Poppool/Poppool/Presentation/Utills/Common/FilterType.swift @@ -1,7 +1,6 @@ import Foundation import UIKit -/// 맵과 리스트에서 공통으로 사용하는 필터 타입 enum FilterType { case location case category diff --git a/Poppool/Poppool/Presentation/Utills/Common/MapFilterChips.swift b/Poppool/Poppool/Presentation/Utills/Common/MapFilterChips.swift index 8006ce31..7322aa76 100644 --- a/Poppool/Poppool/Presentation/Utills/Common/MapFilterChips.swift +++ b/Poppool/Poppool/Presentation/Utills/Common/MapFilterChips.swift @@ -54,8 +54,6 @@ class MapFilterChips: UIView { // MARK: - Update State func update(locationText: String?, categoryText: String?) { - print("Updating chips - locationText: \(String(describing: locationText)), categoryText: \(String(describing: categoryText))") - updateChip(button: locationChip, text: locationText, placeholder: "지역선택", onClear: onRemoveLocation) updateChip(button: categoryChip, text: categoryText, placeholder: "카테고리", onClear: onRemoveCategory) }