김경록의 앱 개발 여정

[ReactorKit] 공식문서 훑어보기 본문

TIL

[ReactorKit] 공식문서 훑어보기

Kim Roks 2025. 1. 9. 19:10

기본 개념

  • Flux와 Reactive Progamming의 조합
  • Observable 스트림을 통해 사용자의 작업(User Action)과 뷰 상태(View State)가 각 레이어로 전달 됨
  • 스트림은 기본적으로 단방향
  • 뷰는 Action을 방출(emit)하고 리액터는 States만 방출 할 수 있음

디자인 목표

  • 테스터블
    • 리액터는 뷰와 의존성이 없다
    • 뷰와 로직을 분리함으로 테스트에 용이해지는 장점
  • 작은 시작
    • 전체 프로젝트에 적용되는것이 아닌 작은 부분(특정 하나의 뷰)에서 부분적으로 ReactorKit을 채택할 수 있음
    • 기존 프로젝트에 ReacotrKit을 적용시키고 싶어서 모든걸 갈아엎지 않아도 됨
  • 적은 타이핑
    • 물리적인 코드가 적게 필요하다고 한다.
    • 높은 추상화 수준을 가졌을듯

View

ReactorKit에선 ViewController와 Cell을 View로 본다고 한다

MVVM처럼 뷰로 본다는 의미가 넘어서 진짜로 View를 채택한다.

class ProfileViewController: UIViewController, View {
  var disposeBag = DisposeBag()
}

profileViewController.reactor = UserViewReactor() // inject reactor

UIKit의 class인 UIView가 아닌 View라는 자체적인 프로토콜을 채택

그럼 reactor라는 프로퍼티가 생성,

리액터 프로퍼티가 변경되면 bind(reactor: )가 호출되고

이 메서드로 Action stream 과 State Stream을 정의

리액터란?

  • 리액터는 뷰의 State를 관리하는 UI 독립적인
  • (UI-independent) 레이어
  • 뷰에서 제어 흐름을 분리하는것이 가장 큰 역할
  • 모든 뷰에는 각 해당하는 리액터가 있고 모든 로직은 그 리액터가 위임 받는다.
  • 이 과정에서 리액터는 뷰에 대한 의존성이 없으므로 테스트하기 용이하다.

Reactor 프로토콜을 정의하여 리액터를 정의한다고 한다(방금은 view를 채택하면 알아서 준다며)

Reactor를 채택하기 위해선, 세가지 유형의 정의 필요

  1. Action
  2. Mutation
  3. state
  4. initialState 또한 필요함

Action

  • 말 그대로 사용자와의 상호 작용
  • 사용자의 입력, 인터렉션 또는 외부 이벤트를 나타냄
  • Reacotr에게 State의 변화를 트리거 하도록 요청
  • enum으로 정의 된다. 다양한 사용자 인터렉션을 나타냄

State

  • 뷰의 상태를 나타낸다
  • 뷰가 랜더링 할 상태를 의미
  • 뷰가 필요로 하는 모든 데이터를 포함함
  • Struct

Mutation

  • Action과 State의 다리역할.
  • Action에 대한 반응으로 State를 어떻게 변경할지를 나타내는 것
  • enum으로 정의, 상태 변화를 설명함
class ProfileViewReactor: Reactor {
  // represent user actions
  enum Action {
    case refreshFollowingStatus(Int)
    case follow(Int)
  }

  // represent state changes
  enum Mutation {
    case setFollowing(Bool)
  }

  // represents the current view state
  struct State {
    var isFollowing: Bool = false
  }

  let initialState: State = State()
}

뷰에서 유저의 Action이 일어나면 Reator로 가서 mutate와 reduce를 거쳐 State로의 변환을 거치고 다시 View로 이동

Mutate()

Action을 받아 Observable 을 생성해준다

비동기 작업이나 API 호출과 같은 SideEffect 작업을 이 메서드에서 수행

func mutate(action: Action) -> Observable<Mutation>

reduce()

이전 State와 Mutaion에서 새로운 State을 생성함

순수 함수이므로 새로운 State를 동기적으로 반환하는데에 목적을 둠

어떤 SideEffect 작업도 수행되어선 안됨

func reduce(state: State, mutation: Mutation) -> State

reduce에서 State를 반환하는 모습

ReactorKit은 각 과정을 거친 후 State를 구독한 View가 State가 업데이트 되면 자동으로 업데이트 될 것

궁금증

위에서 bind(reator:) 의 언급이 있었는데
RxSwift가 가진 bind()함수와 연관성이 있을까 해서 찾아봤다.

reactor의 bind(reator:)

  • ReactorKit의 bind(reactor:) 함수는 View와 Reactor 사이의 바인딩을 설정하는 역할을 한다. 이 함수는 주로 View 계층에서 구현되고, Reactor와 View 사이의 상호작용을 정의한다.
  • view는 REacotr를 소유한다. Reactor는 상태 관리와 비즈니스 로직을 처리한다.
  • 이 함수는 Action과 State를 연결한다
  • 리액터킷의 구조 내에서 VIew와 Reacotr 간의 데이터 흐름과 상태변화를 관리
  • Rx의 bind()
  • 주로 Observable을 Observer 또는 바인딩 할 수 있는 프로퍼티에 바인딩 하는데 사용
  • 주로 데이터 스트림 간에 연결하거나 UI요소와 데이터를 연결할 때 사용
  • reactorkit이 없이도 rx만 사용하는 모든 경우에도 사용 가능