Reactor Kit을 이용해 모작 개발기

Eli·2021년 2월 20일
3

Design Pattern

목록 보기
2/2

그동안 입사 이후 시간이 조금 생겨 이런저런 공부를 했다.
Swift를 한번 톺아보기도 했고 클린 아키텍처를 읽어보기도 했다.

그를 바탕으로 회사의 몇몇기능을 따라서 구현하는 것이다.
목적은 이번 앱에서 구현한 내용과 구조를 바탕으로 현재 앱에 조금씩 이관하는 것을 목적으로 한다.

먼저 디자인패턴과 관련해서는 Reactor Kit을 채택했다.

지난 에코모드를 개편한 부분에 있어서 MVVM으로 작업을 했었는데 몇가지 아쉽고 부족한 부분이 있었다.
1. 이 부분에서 아직 내가 RxSwift에 완벽하게 적응하지 못했다는 점.
2. Status의 기본값을 위해 Relay와 Subject를 많이 사용한다는 점. (그리 권장하지 않는다고 한다. 또한 사용하면서도 이렇게 만드는게 맞는가?에 대한 고민을 했다.)
3. MVVM에 어색해 VM에 있어야하는 코드인가? V에 있어야하는 코드인가? 등을 고민했다. (역할 분담의 어색함.)

이런 부분을 고민하다보니 Reactor Kit을 알게되었으며, 국내의 개발자분이 만드셨다고 한다.
아무래도 커뮤니티에서 한국어로 물어볼 수도 있기도해서 이부분이 좋았다.

토스, 하이퍼커넥트, 라인페이 등 많은 조직들에서 채택하기도 해서 신뢰가 가는부분도 있었다.

가장 중요한 점은 나처럼 Rx가 어색한 사람들, 디자인 패턴에 어색한 사람들에게 어느정도 프레임워크 수준에서의 제약이 생겨 그것대로 따라하기 쉬워 적응을 하기에 적합하다는 생각을 했다.

Reactor Kit Git

사용한 라이브러리들은 아래와 같다.

# UI
  pod 'SnapKit'
  pod 'RxKeyboard'

# Networking
  pod 'Moya/RxSwift'
  
# Misc.
  pod 'RxSwift'
  pod 'RxCocoa'
  pod 'RxViewController'
  pod 'R.swift'
  pod 'Then'
  pod 'ReactorKit'
  pod 'SwiftLint'
  pod 'ReusableKit/RxSwift'

UI

SnapKit으로 모두 작업을 했다.
StoryBoard를 사용하지 않는 이유는 해당 게시글에 작성해두었다.

Networking

Moya로 작업이 되었다.
좀 복잡하게 내가 Networking Manager 같은 것을 만들어도 되지 않고, enum으로 깔끔하게 인터페이스를 정의할 수 있다.

ETC

lint: SwiftLint로 default option들을 따랐다. 몇몇 자동생성 파일들은 제거하고 모두 따르도록 설정을 했다.
then: UI Components 들을 선언해서 옵션들을 곧바로 설정하기에 좋다. 그외에도 여러부분에서 init 코드들을 간결하게 정의할 수 있어서 사용했다.
r.swift: 이 라이브러리에 대한 설명은 요기에 있다.

뭐 간단한 구조는 아래와 같다.

Service Code

Reactor에서 의존하게 될 Service의 기본적인 구조는 아래와 같다.

// 한번 감싸주기 위한 Protocol
protocol CartService: class {
  func getItems() -> Observable<[OrderProduct]>
  func setItem(_ item: Product, _ count: Int) -> Observable<OrderProduct?>
}

// Protocol의 구현부
final class CartServiceImpl: CartService {
  // MARK: Implimentaion
  func getItems() -> Observable<[OrderProduct]> {
    return}

  func setItem(_ item: Product, _ count: Int) -> Observable<OrderProduct?> {
  	return}
}

ViewController

// BaseVC와 ReactorKit의 View를 따름
final class MainVC: BaseVC, View {
	// Mark: UI Components
    private let testView = UIView().then {
    	$0.backgroundColor = .black
    }
    
    // MARK: LifeCycle
    override func viewDidLoad() {
    	super.viewDidLoad()
        self.addView()
    }
    
    // MARK: Layout
    override func makeConstraints() {
    	super.makeConstraints()
        
        //SnapKit을 이용해 Layout Code
    }
    
    private func addView() {
    	//view들을 addSubView 해주는 부분
    }
    
    // MARK: Binding
    func bind(reactor: MainReactor) {
    	// Action 유저의 행동에 의해 발생하는 코드
        
        // State의 값들을 bind해주는 코드
    }
}

Reactor

class MainReactor: Reactor {
  // 유저 액션을 정의
  enum Action {
    case viewWillAppear
  }

  // state를 변경되는 것을 정의
  enum Mutation {
    case setFollowing(Bool)
  }

  // View에서 보여줄 데이터들을 정의
  struct State {
    var isFollowing: Bool = false
  }

  let initialState: State = State()
  
  func mutate(action: Action) -> Observable<Mutation> {
  	switch action {
    case .viewWillAppear
    }
  }
  
  func reduce(state: State, mutation: Mutation) -> State {
  	var state = state
    switch mutation {
    
    }
    
    return state
  }
}

크게 소개하면 위의 내용들로 코드들을 구성했다.

평가

긍정적인 측면
1. 확실히 구조나 코드의 목적의 분리가 확실히 잘되었다.
2. 굉장히 심플하다. 유저 액션 > 받고 서비스나 데이터 생성 > 데이터를 반영 > 뷰에 바인딩 단방향으로 굉장히 쉬워졌다.
3. Rx를 아직 접하지 않은 나에게도 Rx를 어느정도 잘 사용해볼 수 있었다.

아쉬운 측면
1. 여전히 아직 Rx가 자유자제는 아니라는 점
2. View의 Segue를 어떻게 다룰까 고민된다는 것. reactor에서 행동해주는게 좋을 것 같은데 View 코드이다 보니. 좀 찾아보니 오른쪽의 RxFlow라는 라이브러리가 있는듯하다. 다음 작업에서 사용해보도록 해야겠다. RxSwiftCommunity/RxFlow
3. MVVM에서 VM은 다른 View와 재사용이 가능할듯 했는데 Reactor Kit은 1:1 매칭으로 화면마다 하나씩 따라다녀야한다는 부분이 있는 것 같다. (view에서 사용하는 action이 정의돼 있다보니)

개선
1. RxSwift는 계속 사용을 하면서 시간이 해결하리라 믿는다.
2. RxFlow를 사용해본다.
3. RIBs나 Viper등의 아키텍쳐의 관점을 더 고민해야할듯 하다.

profile
애플을 좋아한다. 그래서 iOS 개발을 한다. @Kurly

0개의 댓글