-
Notifications
You must be signed in to change notification settings - Fork 0
Feat/#60 - SearchView 및 카메라 이동 구현 + Dispatcher 레이어 추가 #67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
What? - SearchView의 높이 값과 백그라운드 컬러를 수정할 수 있도록 변경했습니다. - discardableResult 패턴 그대로 사용해서 적용했습니다. Why? - SearchView의 텍스트필드와 다른 값을 가지고 있었음!! 기존 백그라운드 컬러로 하면 다르게 보이는데 새로 컴포넌트 만들기 아쉬워서.
What? - 특정 뷰에서 네이버 맵을 원격 컨트롤(?)하기 위한 중간 디스패처를 도입하였습니다. - 이례적으로 맵뷰에서 해당 디스패처를 주입받도록 구현했습니다. 객체의 변경을 감지하기 위함.(최선이었다.) - 이외 네이버 맵 제어가 필요한 뷰에서는 store에서 주입받습니다. - 팩토리에서 주입되며, 지연 프로퍼티로 선언해주었고, 팩토리와 같은 라이프사이클을 가집니다. 즉, 최초 생성 후 앱 종료시까지 살아있음. [작동방식] 1. requestType을 프로퍼티로 가지며, 다른 뷰에서는 해당 프로퍼티를 업데이트 하여 사용합니다. 2. 다른 뷰에서 프로퍼티를 업데이트하면, 맵뷰에서 감지가 됨. 3. 맵뷰에서 onChange로 요청 케이스마다 로직을 처리함. 4. 그 로직은 mapFeature에서 다뤄야함.
What? - 헤더와 리스트뷰를 구현했는데 특이사항은 없습니다! - 백그라운드 컬러를 clear로 하고 적용했습니다! why? - 기존 컴포넌트에서 글래스 이펙트를 걸면 이상하게 보여서 clear 후 적용
What? - 좌표를 통해 카메라를 이동시키는 함수를 추가하였습니다. - defualt zoom level은 15로 맞췄습니다. - 카메라 이동 완료 상태를 알리는 콜백을 추가하여 안전성을 높였습니다.
What? - 리듀서 스타일에 맞는 검색 피처 구현!! [상태] - searchText: 현재 입력 중인 검색어 - searchResults: Kakao API 결과를 SearchResultItem으로 변환한 목록 - isSearchLoading: 검색 API 요청 진행 여부 - shouldDismiss: 검색 결과 선택 후 화면을 닫아야 하는지 표시하는 플래그 [액션] - onAppear: 화면 진입 시 초기화용 - updateSearchText(text): 검색어 변경 - searchKeyword(query): Kakao 키워드 검색 API 호출 트리거 - updateSearchResults(results): 비동기 요청이 끝난 뒤 결과 반영 - closeSearch: 검색 상태 초기화 - selectSearchResult(item): 결과 항목을 탭했을 때 호출 - consumeDismissSignal: shouldDismiss를 다시 false로 돌려서 1회성으로 사용 [리듀스] - updateSearchText: 빈 문자열이면 결과/로딩 상태 초기화 - searchKeyword: 디바운스 이후 호출된 액션에서 API 요청을 비동기로 실행 - updateSearchResults: 로딩 상태 false, 결과 목록 갱신 - closeSearch: 텍스트/결과/로딩/닫기 플래그 초기화 - selectSearchResult: 좌표와 PlaceInfo를 만들고 dispatcher.request = .moveToSearchResult(...)로 MapScene에 명령 전달, shouldDismiss = true - consumeDismissSignal: shouldDismiss 리셋 즉, 검색어 입력 → 디바운스와 API 호출 → 결과 목록 표시 → 항목 선택 시 MapScene으로 명령 전파 + 화면 닫기까지 이어지는 전체 흐름을 담당합니다.
What? - SearchView를 구현하였습니다. - Store를 주입받으며, Store 내부에는 dispatcher도 주입된 상태입니다. - 특이 사항으로는 검색 시 API 호출 안정성을 위해 0.3초 디바운싱을 적용해서 0.3초마다 체크하도록 했습니다. - 테스트 해봤을 때 포커스필드가 잘 안잡혀서 0.1초정도 딜레이주고 하니까 잘 작동됩니다. 그래서 onAppear부분 추가함! - 항목 누르면 store.send.selectSearchResult가 실행 -> 디스패처 업데이트 -> 맵뷰 감지 -> 카메라 이동 및 바텀시트 뾰로롱~~ - 리스트에 들어갈 형태의 구조체 모델도 만들었어요.
…g model Equatable What? - 타입 안정성을 위해 네이버 좌표에 맞는 구조체 모델을 만들었습니다. - 기존 PlaceInfo의 타입비교를 위해 Equatable 프로토콜을 추가했습니다.
What? - MapFeature에서 MapDispatcher 의존성을 받아 moveToSearchResult 요청을 처리하도록 변경했습니다. [동작 원리] - SearchFeature가 dispatcher.request = .moveToSearchResult(좌표, placeInfo)를 설정합니다. - MapView에서 .onChange(of: dispatcher.request)가 이 변화를 감지하고 store.send(.moveToSearchResult(coordinate, placeInfo))를 호출합니다. - MapFeature가 해당 액션을 받으면 state.targetCoordinate = coordinate로 저장하고, 바텀시트에 보여줄 placeInfo도 상태에 넣은 뒤 dispatcher.request = nil로 리셋합- 니다. - MapView의 뷰 트리에 있는 NaverMapView는 항상 targetCoordinate: store.state.targetCoordinate를 받고 있어요. 이 값이 바뀌면 updateUIView가 호출되어 moveCamera가 실행됩니다. - NaverMapView는 카메라 이동이 끝나면 onMoveConsumed를 통해 MapView로 콜백을 보내고, 그 안에서 store.send(.consumeTargetCoordinate)가 호출되어 다시 nil로 초기화됩니다.
What? - 잡다한 주석 수정
SUSA24-iOS/SUSA24-iOS/Sources/Presentation/SearchScene/SearchView.swift
Outdated
Show resolved
Hide resolved
SUSA24-iOS/SUSA24-iOS/Sources/Presentation/SearchScene/SearchView.swift
Outdated
Show resolved
Hide resolved
What? - Task.sleep 매개변수 nonoseconds -> for: seconds로 변경 - SearchView onAppear에 send 메서드를 Task 내로 이동 Why? - 가독성 향상을 위함!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
싱글톤 말고 앱 scope단에서 관리되게하셨군요.
제가 생각하기에 지도 소비자가 MapView하나라서 괜찮은거같아요. 근데 ModuleFactory에서 delegate를 주입하려면 mapView를 만든 시점에 delegate를 어딘가에서 잡아두고 SearchFeature에 이걸 넘겨줘야하는 구조인거죠 ?
이런점에서 Delegate는 좀 굳이 같은 느낌이긴합니다. 하지만 구조적고민 아주좋아요.
delightPIP
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고민을 엄청 많이 하신게 보여서, 박수를 드리고 싶어용👏
SUSA24-iOS/SUSA24-iOS/Sources/Presentation/SearchScene/SubView/SearchResultsList.swift
Outdated
Show resolved
Hide resolved
MapDispatcher는 델리게이트는 아닙니다. 외부 뷰에서 명령 자체를 맵뷰에 전달해주는 역할이고 액션자체는 맵피처에서 하는구조에요. |
What? - DWCircleButton -> DWGlassEffectCircleButton으로 컴포넌트 수정 - 간격, 아이콘, 버튼 크기 조절
What? - 기존 임시로 만들어둔 base64기반의 방식을 File Path 방식으로 변경합니다. How? - Data 파일에 `ImageFileStorage.swift` 전역 객체, 메서드 활용 - CoreData에는 String 타입의 파일 경로만 저장. 실제 이미지는 FileManager 상에 우리가 생성한 임의의 경로에 저장됨. Why? - base64방식은 이미지 파일의 용량이 매우 증가되어 저장되는 방식 (우리 앱에서 불필요) - 효율이 떨어지고, 디코딩 시 성능 저하를 일으킬 수 있는 방식 -> FIleManager는 성능면에서 가장 높은 효율을 보이는 방식 - 개선된 방식은: CoreData 상에서는 실제 파일이 아니라 경로만 저장되므로, Core Data DB에 영향을 끼치지 않고 / 추후 공유시 이미지 캐싱이나 백업 처리에 유효한 방식으로 파악됨.
What? - OnePageView의 ProfileImage, suspectName, crime 정보를 CoreData 엔티티와 연동했습니다. - OnePageFeature에 Case 정보를 받아오기 위한 caseRepositoryProtocol 의존성을 Factory 상에 추가했습니다.
What? - enumerate 함수 제거 후 마지막 요소 id로 체크하도록 변경 - MapDispatcher의 request를 private로 전환하고, 내부 함수 send로 request를 변경하도록 수정하였습니다. 또, 사용하는 곳에서 nil으로 갱신해야하는 부분도 consume 함수로 가독성을 높였습니다. - 이외 사소한 문법 수정입니다.
📝 Summary
검색 기능 및 카메라 이동을 구현하였습니다.
그리고.. mapView가 naverMap을 소유하고 있지만 기존의 구조로는 다른뷰에서 네이버 뷰를 제어할 수 없어 하나의 계층을 추가하였습니다.
🔨 What
Simulator.Screen.Recording.-.iPhone.17.-.2025-11-08.at.23.18.01.mov
일단 검색 잘 돌아가는가.. -> yyess
이례적으로 디스패처는 MainView에도 주입이 됩니다. MapView에서 Dispatcher를 감시하도록 하였음.
이에 따른 onChange 함수로 디스패처의 request 프로퍼티를 감지합니다.
[정말 간단한 사용법]
MapFeature에서 해당 requestType에 맞는 로직을 구현해줘요.MapView에서onChange를 보면 RequstType 별로 실행 할 메서드가 정의되어있어요. 여기에 정의하고 쓰면 됨!👀 Review Notes
DI 주입부분 봐주시고 현재처럼 팩토리 내부에 지연 프로퍼티로 생성하고 주입해주는데 사실상 이러면 싱글톤이랑 다를게 없는.. 그래도 어느 뷰에서 네이버맵의 제어가 필요하구나 하는 걸 알 수 있음. ㅎㅎ
현재 계층 하나 추가한 것이 어떤지.. 더 나은 방법이 있을지!!!!
Dispatcher가 무엇이냐. 새로운 계층 도입 설명