Rx Input, Output이란?
- Rx 반응형 프로그래밍을 사용하여 효율적으로 MVVM 디자인 패턴을 적용하기 위해 사용하는 패턴이다.
- View에서 Input을 정의하고 ViewModel의 transform 함수를 호출해 Output 을 반환해 사용한다.
- Input: 말 그대로 유저가 하는 액션이다.
- Output: 유저가 하는 액션에 따라 행해지는 액션이다.
- ex) button1을 tap했을 때 다음 화면으로 이동
- Input: button1을 tap
- Output: 다음 화면으로 이동
- ex) button1을 tap했을 때 다음 화면으로 이동
- 간단하게 생각해서 InterAction과 Output을 나눠서 정의해 사용해준다고 보면 된다.
Rx Input, Output을 쓰는 이유
- InterAction에 따른 Output을 정의해 사용하기 때문에 확실한 로직 분리가 가능해진다.
- 또한 다른 사람이 봤을 때 Input, Output 객체만 보고도 화면에서 행해지는 액션들을 알 수 있어 유지보수에 용이하다.
- ViewController, ViewModel을 확실히 분리해 의존성을 줄여줄 수 있다.
- 우선 나 같은 경우에는 코드의 흐름을 잘 생각하지 않고 작성하던 안 좋은 버릇이 있었다.
- 그러나 Input, Output 패턴을 사용하다 보면 데이터의 흐름을 강제적으로 머릿속으로 그리게 되고 거기에 적합한 Interaction과 Output을 정의해야 하기 때문에 그 흐름을 익혀가기에 정말 좋고 만족스러웠다.
Rx Input, Output의 장단점
- Rx를 바탕으로 사용하는 패턴이기 때문에 기본적으로 반응형의 장단점들을 모두 가지고 있다.
장점
- 확실한 로직 분리 + 코드의 가독성이 좋아진다.
- 왼쪽 코드는 ViewModel에 정의해준 Input, Output 객체
- 오른쪽 코드는 ViewController에 연결해준 Input 객체와 ViewModel transform 함수를 호출해 반환해 오는 Output 객체
- 아래와 같이 기능이 많은 코드를 작성해도 Input, Output을 명확하게 나눠 연결해줘 Interaction, 기능들을 한눈에 보기 편해진다.
- 지원하는 Operator들이 많아 코드를 명료하게 작성할 수 있다.
- throttle, skip, distinctUntilChanged, withLatestFrom 등 조금은 생소하지만 편하게 사용할 수 있는 문법들을 익혀야 조금 더 효율적이게 작성이 가능할거 같다.(Rx의 장점)
- throttle: 타이머를 지정해두고 이벤트가 처음 방출됐을 때 타이머 동안 이벤트가 중복 방출되지 않는다.
- skip: 정수를 인자로 받고 그 수 만큼 이벤트를 스킵해준다.
- distinctUntilChanged: 연달아 중복된 값이 올 때 무시해준다.
- withLatestFrom: 두 Observable 중 첫번째 Observable에서 아이템이 방출될 때마다 그 아이템을 두번째 Observable의 가장 최근 아이템과 결합해 방출해준다.
- throttle, skip, distinctUntilChanged, withLatestFrom 등 조금은 생소하지만 편하게 사용할 수 있는 문법들을 익혀야 조금 더 효율적이게 작성이 가능할거 같다.(Rx의 장점)
- callBack 지옥에서 벗어나기 좋다.
- Retry 등등
단점
- 클로저 사용이 많아져서 강한 순환 참조를 신경써주어야 한다.
- 위의 예시들을 봐도 weak self가 많은 것을 볼 수 있다.
- 맨 아래에 강한 순환 참조 관련 겪었던 이슈를 정리해봤습니다.
- 간단하게 작성할 수 있는 코드도 Interaction, Output으로 나눠서 작성해줘야한다.(View 이동 등등)
- 아래의 왼쪽 코드와 같이 뷰 이동 함수들을 정의할 때 매번 모든 로직에 weak self를 넣어줘 강한 순환 참조를 고려해야 한다.
- 오른쪽 코드의 Output을 보면 ViewModel에서 행해지는 액셔는 토스트 메시지, 화면 이동 코드 밖에 없지만 한줄 한줄 다 정의해주고 구독해줘야하기 때문에 코드 양이 길어지는 경우도 있다.
- ViewController에 한줄이면 될걸 따로 선언해줘야하니 간단한 기능에선 코드 양이 기존 보다 길어지는 문제가 있었습니다.
- 러닝커브
- 간단하게 적용하긴 쉽지만 조금도 효율적으로 작성하기 위해선 많은 Operator들을 적재적소에 사용할 수 있어야 한다.
- 위에 지원하는 operator이 많은게 장점이라고 적었지만 그것을 적재적소에 효율적이게 작성하기 위해선 러닝커브가 적진 않다고 생각한다.
- 물론 이쪽은 Rx Input, Output의 러닝커브라기 보단 Rx의 러닝 커브가 더 적합한 것 같습니다.
Input Output의 Relay 사용
위의 예시들을 보면 Input, Output 외에도 PublishRelay, BehaviorRelay 등이 있는 것을 볼 수 있습니다.
Relay를 사용한 이유, 사용법에 대해서 말해보겠습니다.
- Relay 사용 이유
- 순서를 생각했을 때 ViewModel에서 정의한 Input을 ViewController에서 Input 설정 -> ViewModel의 transform 함수내에 Input을 구독하던 Output의 로직 실행 -> 데이터 방출 -> ViewController의 Output에서 방출된 데이터로 화면에 바인딩순으로 실행되는게 기본적인 Input Output 패턴이다.
- Relay는 Subject와는 다르게 .complete, .error가 없기에 UI에 보여지는 값이 에러가 발생할 수는 없으니 Relay를 사용하는 것이 더 적합하다.
- Driver, Signal 사용 이유
- 둘 다 MainScheduler에서 돌아가기 때문에 observeOn()과 같이 강제적으로 쓰레드를 변경해주는 함수를 사용하지 않아도 된다.
- error를 방출하지 않기 때문에 UI에 적합하다고 판단했다.
- bind도 error를 방출하지만 bind는 Stream을 공유하기 때문에 메모리적으로 Driver, Signal을 사용하는 것이 더 적합하다고 생각했다.
간단히 Relay, Signal, Driver 정리
- Relay
- PulbishRelay: 에러를 방출하지 않고 초기값을 방출하지 않는 Relay
- BehaviorRelay: 에러를 방출하지 않고 초기값이 있고 초기값 혹은 최신값을 방출하는 Relay
- Signal
- MainScheduler에서 돌아간다.
- 에러를 반환하지 않음(따라서 UI에 적합함)
- 구독 이후부터 방출되는 이벤트를 얻을 수 있다.(초기값이 없음)
- .emit으로 구독해준다.
- Driver
- MainScheduler에서 돌아간다.
- 에러를 반환하지 않음(따라서 UI에 적합함)
- 구독 이전의 이벤트를 방출해준다.(초기값이 있음)
- .drive로 구독해준다.
- 따라서 UI에 관련된 값들을 처리해줄 때는
- 초기값이 있을 경우
- Signal -> input을 emit으로 구독 -> PublishRelay를 accept해줘 방출해주면 된다.
- 초기값이 없을 경우
- Driver -> input을 drive으로 구독 -> BehaviorRelay를 accept해줘 방출해주면 된다.
- 초기값이 있을 경우
- 초기값이 없을 경우
- 초기값이 있을 경우
'IS' 카테고리의 다른 글
iOS Swift - SocketIO(양방향 통신) (0) | 2023.02.28 |
---|---|
iOS Swift - 개인 앱 출시 프로젝트 - FURY (0) | 2023.02.28 |
iOS Swift 카카오 소셜 로그인(Kakao Social Login) (0) | 2023.02.13 |
iOS Swift - Service Level Project(SLP) 회고 (0) | 2023.02.13 |
RxSwift(Observable, Observe, Subject, Relay etc) (0) | 2022.10.29 |