본문 바로가기
아키텍처+디자인패턴

ReactorKit Framework

by 나리._. 2022. 6. 9.

안녕하세요. 이번 포스팅은 ReactorKit 프레임워크에 대해 정리하도록 하겠습니다. 😀

 

ReactorKit을 정리하기 전, 해당 프레임워크를 왜 쓰는지에 대해 알아보겠습니다.

 

ReactorKit을 쓰는 이유?

  • View Controller 무거워지는 현상을 피하고싶다.
  • RxSwift 장점을 취하고 싶다.
    (ReactorKit Rx 레이어 위에 만들어진 프레임워크)

이러한 이유가 있는데요! 

RxSwift + MVVM이랑 뭐가 다른지에 대해 궁금하신 분이 계실 것 같습니다. 제가 그랬거든요..ㅎ

 

RxSwift를 사용하다보면 Cyclic Data Dependencies (다음 작업을 위해 이전 작업의 결과가 필요할때 생기는 현상)이 발생합니다. 

 

MVVM 아키텍처에서는 이러한 상황에 중간상태를 담아두기 위해, Behavior Subject 사용을 남발하게 됩니다.

 

이와 달리, ReactorKit은 중간 상태를 reduce라는 함수로 관리를 하기 때문에 상태관리가 간결해집니다.

또한 상태를 변경하거나 읽을때 단방향으로 전파가 가능가 가능하다는 장점이 있습니다.

 

 

ReactorKit의 동작에 대해 자세히 살펴봅시다.

View

  • 비지니스 로직이 없다.
  • 사용자의 input을 Reactor의 Action에 바인딩하고, Reactor의 State를 UI 컴포넌트에 바인딩하는 단방향 흐름

View의 비지니스 로직은 Reactor에서 담당한다.

따라서 대부분 View 대응되는 Reactor 존재한다.

 

여기서

사용자의 input을 Reactor의 Action에 바인딩하고, Reactor의 State를 UI 컴포넌트에 바인딩

 하는 방법은 무엇일까요? 아래 bind(reactor:) 함수를 통해 이루어집니다.

bind(reator:) 

  • View와 Reactor 사이의 Action, State 스트림을 바인딩하기 위한 메서드
  • self.reactor가 변경되면 호출되기 때문에 새로운 값을 넣을때마다 bind 호출

Reactor

  • View의 모든 로직을 가지고 있다.
  • View로부터 제어 흐름을 분리한다.
  • 의존성이 없기 때문에 테스트하기가 용이하다.

Action이나 State와는 달리, Mutation는 리액터 클래스 밖으로 노출되지 않고 Action과 State를 연결하는 역할을 합니다.

 

즉, Reactor에서 Action이 State 바로 변경하는 것이 아니고  Action이 Reactor에 전달되면 mutate(), reduce() 두 단계를 거쳐 State를 변경합니다. 이 두 단계에서 Mutation을 파라미터로 이용합니다.

mutate(Action) -> Observable<Mutation>

  • View로부터 Action을 받고, Observable<Mutation> 을 생성한다.
  • 비동기 오퍼레이션, API 호출과 같은 Side Effect들은 이 메소드 안에서 수행한다.

reduce(State, Mutation) -> State

  • 기존 State와 Mutation으로부터 새로운 State를 생성한다.
  • 동기적으로 새로운 state를 리턴한다.

 

이제 코드로 살펴봅시당

 

새로고침 버튼을 눌렀을때, 새로고침 이벤트 Reactor 전달

  • View -> Action -> Reactor
// bind 함수 안
reloadButton.rx.tap
	.map { Reactor.Action.reload }
    	.bind(to: reactor.action)
    	.disposed(by: self.disposedBag)
  • Reactor에서 State 변경

mutate()에서 Action을 받아 사이드 이펙트 처리 -> Mutation을 방출

reduce()라는 함수에서 Mutation 받아서 State 변경

 

로딩 여부에 따라 Activity Indicator 작동시키기 

  • Reactor -> State -> View
// bind 함수 안
reactor.state (Observable: state 변경될때마다 값 방출)
	.map { $0.isReloading }
    	.bind(to: indicator.rx.isAnimating)
    	.disposed(by: self.disposedBag)

 

 

위와 같이, ReactorKit을 통해 View에 대응하는 Reactor를 생성하면 View는 Reactor로 Action을 보내고 State 받는 식의 단방향 데이터 흐름을 갖게 됩니다.

 

이상 ReactorKit 설명 마치겠습니다 😁