[WIP] 검색 뷰 기능 구현 및 아키텍처 적용#39
Conversation
| // func fetchDreamSearchList(query: DreamSearchQuery, | ||
| // completion: @escaping(Result<DreamSearchEntity, Error>) -> Void) -> Cancellable? |
There was a problem hiding this comment.
- DreamSearchQuery, DreamSearchEntity 부분
| // func execute(requestValue: DreamSearchUseCaseRequestValue, | ||
| // completion: @escaping (Result<DreamSearchEntity, Error>) -> Void) -> Cancellable? { | ||
| // |
| struct DreamSearchResuestDTO: Encodable { | ||
| let query: String | ||
| } |
There was a problem hiding this comment.
요부분은 저번에 수연이가 얘기한 것처럼 DTO는 제거해도 될 것 같아요!
@Suyeon9911 그런 의도가 맞을까요?
| public enum Genre: Int { | ||
| case comedy | ||
| case romance | ||
| case action | ||
| case thriller | ||
| case mystery | ||
| case fear | ||
| case sf | ||
| case fantasy | ||
| case family | ||
| case etc | ||
| case none | ||
| } |
| init(provider: DefaultSearchService = DefaultSearchService.shared) { | ||
| self.provider = provider | ||
| self.initialize() | ||
| } |
There was a problem hiding this comment.
현재 service를 Presentation Layer에서 바로 참조하고 있는데, 저희가 의도한 아키텍쳐를 생각했을 때,
프레젠테이션에서는 유즈케이스의 메서드를 통해서 간접적으로 통신의 결과를 받아오는 쪽으로 하면 좋을 것 같습니다!
뷰모델은 유즈케이스에만 의존하도록 해서 책임을 확실하게 분리할 수 있게 되는...
| Log.event(type: .info, "사용자가 \(query)에 대한 검색을 시작함") | ||
| self.currentSearchQuery = query | ||
| self.resetCollectionViewDataSource() | ||
|
|
||
| return self.searchItemsForTerm() | ||
| .catch { error -> Observable<[DreamSearchResult]> in | ||
| self.reloadCollectionViewData.onNext(true) | ||
| return Observable.empty() | ||
| } |
There was a problem hiding this comment.
그래서 결론적으로 요 부분에 들어가는 로직이 useCase에 들어가고, 그럼으로써 ViewModel에서는 Input을 받아서 UseCase의 어떤 메서드를 실행해야 할지만 호출해주는 그런 식으로 역할을 분리하면 된다고 이해하고 있습니다.. 물론 구현은 하기 나름이니 취향껏 하시면 될 것 같아요!
| final class DreamSearchViewModel { | ||
| // Input | ||
| private var provider: DefaultSearchService! | ||
| private var collectionViewDataSource: [DreamSearchResultViewModel] = [] // 서브 뷰모델에 해당 |
| guard let self = self else { return } | ||
| DispatchQueue.main.async { | ||
| self.dreamSearchCollectionView.reloadData() | ||
| } |
There was a problem hiding this comment.
withunretained(Self)와 observeOnMain을 사용해서 선언형으로 프로그래밍할 수도 있을 것 같습니다!
| public protocol SearchService { | ||
| func searchDreamRecords(keyword: String) throws -> DreamSearchResponse | ||
| } | ||
|
|
||
| public class DefaultSearchService: BaseService { | ||
| public static let shared = DefaultSearchService() | ||
|
|
||
| private override init() { } | ||
| } | ||
|
|
||
| extension DefaultSearchService: SearchService { | ||
| public func searchDreamRecords(keyword: String) throws -> DreamSearchResponse { | ||
| AFManager.request(SearchRouter.searchRecord(keyword: keyword)) | ||
| .responseData { response in | ||
|
|
||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
사실 잘 모르겠어서 일단 만들었... 허허
There was a problem hiding this comment.
그렇다면 레코드 서비스에 넣어주시는건 어떤가요? ㅎㅎ 서비스 클래스가 많아지면 혼란이 올 것 같아서요~
|
|
||
| import Foundation | ||
|
|
||
| struct DreamSearchResponseDTO: Decodable { |
| public protocol UseCase { | ||
| @discardableResult | ||
| func start() -> Cancellable? | ||
| } |
There was a problem hiding this comment.
start 메서드가 서버통신을 위한 UseCase에만 특별하게 사용 될 수 있을 것 같은데, 네이밍이 조금 넓은 범위를 포함하게 되는 것 같아요... 그리고 start 메서드는 하나의 기능만 가지고 있는 UseCase에 사용될 수 있을 것 같은데 어떤 식으로 사용하실 예정인가요?
There was a problem hiding this comment.
아하! 레포지토리 인터페이스만으로는 부족한 걸까요?
There was a problem hiding this comment.
저 부분에서만 의존성 역전이 일어나는 것이기 때문에 따로 만들었던 것인데요...!
생각을 해보니 네이밍이 범용적이기도 하고, 아직 사용하고 있지 않기 때문에 수정을 해야겠다고 느껴지네욥


👻 작업한 내용
🎤 PR Point
start()함수가 담긴UseCase 프로토콜을 만들었습니다.문제 ㅡ.ㅡ
public을 붙이지 않았는데, 붙여 주지 않았던 것이란 말임? 혹시 이것 때문인가 싶어 붙여 주었지만 똑같이 오류가 사라지지 않음...📮 관련 이슈