훌이
후리스콜링개발
훌이

블로그 메뉴

  • 왈 (iOS APP)
  • Github
전체 방문자
오늘
어제
  • 전체 (171)
    • ⭐️ 개발 (140)
      • JAVA (4)
      • Web (5)
      • iOS & Swift (94)
      • iOS Concurrency (4)
      • Rx (18)
      • Git (6)
      • WWDC (1)
      • Code Refactor (3)
      • Server (1)
    • ⭐️ Computer Science (22)
      • 운영체제 (10)
      • 네트워크 (5)
      • PS (7)
    • 경제시사상식 (8)
    • 기타 등등 (0)

인기 글

최근 글

05-22 03:15

티스토리

hELLO · Designed By 정상우.
훌이

후리스콜링개발

[Rx] Input/Output 패턴 적용하기 - 비즈니스 로직 분리!!
⭐️ 개발/Rx

[Rx] Input/Output 패턴 적용하기 - 비즈니스 로직 분리!!

2022. 11. 2. 09:00
728x90
반응형

인풋, 아웃풋을 공부하면서 느낀 것은 완전 뇌절티비. 처음에 설명을 들으면서 그리고 후에 실습 코드를 따라치고, 내 과제에 적용하면서 구글링하며 머리에 껴넣으려고 했는데 도무지 이해가 안돼서 엉엉 울 뻔 하다가 같이 수업 듣는 수강생 두 분께 설명을 듣고 감이 왔다. 진쨔 감사합니다!!!!!!!!! 진심. 이거 이해하려는데 카페 노랫소리 왤케 시끄러; 집중 안돼서 다 뽀갤 뻔 했다.ㅠㅠ


Input/Output 패턴을 통한 비즈니스 로직 구분짓기

 

여튼,

Input/Output 패턴을 적용해주는 이유가 뷰컨에 있는 비즈니스 로직을 모두 뷰모델에서 처리해주기 위해서다.

내가 이해 안됐던 부분이 이 말을 듣고 이해가 됐다. 

 

<ViewModel>

그니까, 자잘자잘한 비즈니스 로직까지도 모두 뷰모델에서 처리해주기 위해서 
- 사용자의 입력 이벤트를 Input에 정의하고, 
- View에 넘겨줄 데이터를 Output에 정의하는 것이당. 

 

글구 이제 transform 메소드에서는 Input으로 받은 이벤트에 대해 비즈니스 로직 처리를 통해 Output으로 반환한다.

 

final class SearchViewModel: ViewModelType {
    
    var userList = PublishSubject<SearchUser>()
    
    struct Input {
        let searchText: ControlProperty<String?>
    }
    
    struct Output {
        let userList: PublishSubject<SearchUser>
        let searchText: Observable<String>
    }
    
    func transform(input: Input) -> Output {
        let userList = userList
        let searchText = input.searchText
            .orEmpty
            .debounce(.seconds(1), scheduler: MainScheduler.instance)
            .distinctUntilChanged()
        return Output(userList: userList, searchText: searchText)
    }
}

 


<View>

그러면 View(Controller)의 bindViewModel 함수에서는

- Input 객체를 만들어주고,

- transform 함수를 통해서 Output 객체를 반환해서 사용한다.

    private func bindViewModel() {
        
        let input = SearchViewModel.Input(searchText: searchView.searchBar.rx.text)
        let output = searchViewModel.transform(input: input)
        
        searchViewModel.userList // Output VM -> VC
            .withUnretained(self)
            .bind { (vc, user) in
                var snapshot = NSDiffableDataSourceSnapshot<Int, Result>()
                snapshot.appendSections([0])
                snapshot.appendItems(user.results)
                vc.dataSource.apply(snapshot, animatingDifferences: true)
            }
            .disposed(by: disposeBag)
        
//        searchView.searchBar.searchTextField.rx.text // Input VC -> VM
//            .orEmpty
//            .debounce(.seconds(1), scheduler: MainScheduler.instance)
//            .distinctUntilChanged() // 요놈들을 비즈니스 로직으로 생각했음
        
        output.searchText
            .withUnretained(self)
            .bind(onNext: { (vc, value) in
                vc.searchViewModel.requestSearchUser(query: value, page: 50)
            })
            .disposed(by: disposeBag)
        
        searchView.collectionView.rx.itemSelected // 이것은 코디네이터.. 그렇군..
            .withUnretained(self)
            .bind(onNext: { (vc, item) in
                vc.pushDetailView(item)
                vc.searchView.collectionView.deselectItem(at: item, animated: true)
            })
            .disposed(by: disposeBag)
    }

ViewModelType 프로토콜을 통한 ViewModel 추상화

Input/Output 패턴을 적용하다보면 ViewModel 코드가 반복되어 나타난다.

그럴 때 프로토콜을 통해서 ViewModel에 구현해줘야 하는 프로퍼티와 메소드를 선언해두면 해당 프로토콜을 채택 시에 어떤 프로퍼티와 메소드를 구현해줘야 하는지 쉽게 알 수 있다. 컴파일러가 알려주그든..

protocol ViewModelType {
    associatedtype Input
    associatedtype Output
    
    func transform(input: Input) -> Output
 }

 

여기 보면 associatedtype이 뭔가 싶은데,

Input / Output 구조체는 처음에 사용하기 전까지는 "특정 타입"이라고 단정지을 수 없다. Int인지 String인지 알 수가 없잖아??? 쓰기 전까지는??? 약간 제네릭이랑 비슷한 것임... 그래서 해당 프로토콜을 채택하면 처음에 typealias를 통해 Input / Output 이라는 별명을 가진 아이에게 붙여줄 type을 결정하라고 나온다. 결정하면 이때 타입이 정해지는 것인... 그런 바이브

 

여튼, 프로토콜을 만들어 줄 때는 타입을 모르니까 그때 타입을 단정짓기 어려워서 associatedtype을 써준다.

 

추가적으로 프로토콜이나 제약을 줄 수도 있다고 함..

 


기존 뷰컨에서 자잘한 비즈니스 로직들.. 긍까 아래 사진 코드 부분들까지도 뷰모델로 빼줬단 것임..


참고 블로그 

https://coding-idiot.tistory.com/7

728x90
반응형
저작자표시 비영리 변경금지 (새창열림)

'⭐️ 개발 > Rx' 카테고리의 다른 글

7. Traits - ControlProperty, ControlEvent, Driver  (0) 2023.01.16
6. Binder  (0) 2023.01.16
[Rx] Error Operator  (0) 2023.01.16
[Rx] TIL - Rx 개념 재복습 및 총정리  (0) 2022.10.31
[Rx] Observable, Observer, Disposable, Subject, Relay  (4) 2022.10.26
    '⭐️ 개발/Rx' 카테고리의 다른 글
    • 6. Binder
    • [Rx] Error Operator
    • [Rx] TIL - Rx 개념 재복습 및 총정리
    • [Rx] Observable, Observer, Disposable, Subject, Relay
    훌이
    훌이

    티스토리툴바