Rx Input, Output이란?

  • Rx 반응형 프로그래밍을 사용하여 효율적으로 MVVM 디자인 패턴을 적용하기 위해 사용하는 패턴이다.
  • View에서 Input을 정의하고 ViewModeltransform 함수를 호출해 Output 을 반환해 사용한다.
  • Input: 말 그대로 유저가 하는 액션이다.
  • Output: 유저가 하는 액션에 따라 행해지는 액션이다.
    • ex) button1을 tap했을 때 다음 화면으로 이동
      • Input: button1을 tap
      • Output: 다음 화면으로 이동
  • 간단하게 생각해서 InterActionOutput을 나눠서 정의해 사용해준다고 보면 된다.

Rx Input 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, 기능들을 한눈에 보기 편해진다.

인턴 어플 개발에 작성한 Input Output 예시

 

  • 지원하는 Operator들이 많아 코드를 명료하게 작성할 수 있다.
    • throttle, skip, distinctUntilChanged, withLatestFrom 등 조금은 생소하지만 편하게 사용할 수 있는 문법들을 익혀야 조금 더 효율적이게 작성이 가능할거 같다.(Rx의 장점)
      • throttle: 타이머를 지정해두고 이벤트가 처음 방출됐을 때 타이머 동안 이벤트가 중복 방출되지 않는다.
      • skip:  정수를 인자로 받고 그 수 만큼 이벤트를 스킵해준다.
      • distinctUntilChanged: 연달아 중복된 값이 올 때 무시해준다.
      • withLatestFrom:  두 Observable 중 첫번째 Observable에서 아이템이 방출될 때마다 그 아이템을 두번째 Observable의 가장 최근 아이템과 결합해 방출해준다.

SeSAC SLP project에서 작성한 Input Output 예시

  • callBack 지옥에서 벗어나기 좋다.
    • Retry 등등

 

단점

  • 클로저 사용이 많아져서 강한 순환 참조를 신경써주어야 한다.
    • 위의 예시들을 봐도 weak self가 많은 것을 볼 수 있다.
    • 맨 아래에 강한 순환 참조 관련 겪었던 이슈를 정리해봤습니다.
  • 간단하게 작성할 수 있는 코드도 Interaction, Output으로 나눠서 작성해줘야한다.(View 이동 등등)
    • 아래의 왼쪽 코드와 같이 뷰 이동 함수들을 정의할 때 매번 모든 로직에 weak self를 넣어줘 강한 순환 참조를 고려해야 한다.
    • 오른쪽 코드의 Output을 보면 ViewModel에서 행해지는 액셔는 토스트 메시지, 화면 이동 코드 밖에 없지만 한줄 한줄 다 정의해주고 구독해줘야하기 때문에 코드 양이 길어지는 경우도 있다.
    • ViewController에 한줄이면 될걸 따로 선언해줘야하니 간단한 기능에선 코드 양이 기존 보다 길어지는 문제가 있었습니다.

인턴 어플 개발에 작성한 Input Output 예시

 

  •  러닝커브
    • 간단하게 적용하긴 쉽지만 조금도 효율적으로 작성하기 위해선 많은 Operator들을 적재적소에 사용할 수 있어야 한다.
    • 위에 지원하는 operator이 많은게 장점이라고 적었지만 그것을 적재적소에 효율적이게 작성하기 위해선 러닝커브가 적진 않다고 생각한다. 
    • 물론 이쪽은 Rx Input, Output의 러닝커브라기 보단 Rx의 러닝 커브가 더 적합한 것 같습니다.

Input Output의 Relay 사용

위의 예시들을 보면 Input, Output 외에도 PublishRelay, BehaviorRelay 등이 있는 것을 볼 수 있습니다.

Relay를 사용한 이유, 사용법에 대해서 말해보겠습니다.

 

  • Relay 사용 이유
    • 순서를 생각했을 때 ViewModel에서 정의한 InputViewController에서 Input 설정 -> ViewModel의 transform 함수내에 Input을 구독하던 Output의 로직 실행 -> 데이터 방출 -> ViewController의 Output에서 방출된 데이터 화면에 바인딩순으로 실행되는게 기본적인 Input Output 패턴이다.
    • RelaySubject와는 다르게 .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해줘 방출해주면 된다.

 

  • 초기값이 없을 경우

SeSAC SLP Project PublishRelay 예시

 

  • 초기값이 있을 경우

SeSAC SLP Project BehaviorRelay 예시

 

+ Recent posts