티스토리 뷰

왜 아키텍쳐를 고려해야만 할까?

아키텍쳐를 고려하지 않는 다면 매우 비대해진 클래스를 마주하게 됩니다. 그리고 이를 디버깅하는 동료 뿐만 아니라 본인 조차 이해하고 수정하는 데 너무나도 많이 시간을 허비해야만 합니다. 결국 수정을 포기하게 되죠(?)

서비스 규모가 커짐에 따라 코드 또한 빠른 속도로 방대해 집니다. 그래서 우리는 이러한 상황에 대비하기 위하여 좋은 설계가 필요합니다.

좋은 아키텍처란 ?

  1. 각 객체들이 구체적이고 명확한 역할을 가지며, 그 역할이 적절하게 분배되어 있다.
  2. 데이터의 흐름이 단순하다.
  3. Testability 하다.
  4. 코드의 위치가 명확하다.

이 기준들을 가지고 하나씩 장단점을 따져봅시당


MVC

Pros

  • 개발자에게 너무나도 익숙한 구조.

Cons

  • Controller가 View Lifecycle과 강하게 연결되어있어 View와 Controller 간의 분리가 힘들다
  • 역할의 분리가 되어있지 않아 Testability x
  • Controller(View)에 모든 로직이 들어가 매우 비대해진다.

Cocoa MVC 의 현실... (View=Controller)

 

이러한 Cocoa Framework의 특성탓에 아래부터는 View와 Controller를 View로 묶어서 정의합니다.


 

MVP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let model = Person(firstName: "David", lastName: "Blaine")
/*  Model
    모델은 모델
*/
let view = GreetingViewController()
/*  View
    - UI로 시작하는 모든 클래스들을 뿌리는 역할.
    - view 는 model 에 대해서 알지 못함.
*/
let presenter = GreetingPresenter(view: view, person: model)
/*  Presenter
    - View를 뿌리는 layout code에는 관심 x.
    - 단지 View의 state, data를 update 한다.
    - server 나 db 에서 받아온 데이터를 직접 관리한다.
*/
view.presenter = presenter
// View와 Presenter 가 1:1로 대응된다.
cs

 

Pros

  • View를 정적인 상태로 만든다 (할일 줄여주자)
  • Presenter는 ViewLifecycle과 전혀 상관없다 (오!)
  • Presenter는 Layout에 관련된 코드가 일체 없다. (관심사 분리 오!)
  • Presenter는 단지 View의 state와 data 를 업데이트한다.
  • Testability : Present를 보면 unit test case 들을 알 수 있다.

Cons

  • View와 Presenter가 1:1로 대응된다.
  • Presenter 가 단순 중개자이기 때문에 여전히 View의 역할이 크다. (또는 불분명해진다.)
  • Presenter의 코드가 빠르게 방대해진다...

MVVM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let model = Person(firstName: "David", lastName: "Blaine")
/*  Model
    모델은 모델
*/
let viewModel = GreetingViewModel(person: model)
/*  ViewModel
    - viewModel은 view에 대해서 알 필요가 없다..!!
    - 로직의 흐름을 가지고 상태를 update 한다.
*/
let view = GreetingViewController()
view.viewModel = viewModel
/*  View
    - model 에 대해서 알필요 x.
    - server 나 db 에서 받아온 데이터를 ViewModel에 넘겨준다.
    - ViewModel 의 상태변화를 감지해서 스스로 update 한다.
*/
cs

Pros

  • MVP 의 Presenter가 가지는 장점을 그대로 갖는다.
  • View → ViewModel 바인딩 → View와 ViewModel 간의 의존성 제거 (단방향 이벤트 수신)
  • Rx를 적극 활용할 수 있다.
  • 데이터의 정제(상태변화)를 직접 관리하기 때문에 View의 역할을 줄여준다.

Cons

  • MVP에 비해 View의 역할이 많다. (ViewModel은 View를 직접 update 시키지 않는다)
  • 아키텍쳐의 이해가 저마다 달라 ViewModel 역할 범위에 대한 논쟁이 많다...

Viper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
let view = GreetingViewController()
/*  View
    사용자 입력을 Presenter에 보내고 Layout 작업을 수행.
*/
let interactor = GreetingInteractor()
/*  Interactor
    Use case 에 따라서 모델 개체를 조작.
*/
let presenter = GreetingPresenter()
/*  Presenter
    Interactor로부터 데이터를 받아와 View에 언제 보여줄지 결정.
*/
view.eventHandler = presenter
presenter.view = view
presenter.greetingProvider = interactor
interactor.output = presenter
 
/*  Entity
    모델은 모델
*/
/*  Router
    - 화면 전환을 담당
    - 화면전환을 어떻게 할 것인지 결정.
*/
cs

Pros

  • MV(X) 들과 달리 화면 전환에 대한 역할 또한 분리
  • 역할 분리에 있어서 최강자
  • Testability 또한 최강

Cons

  • 이걸 지킬 수 있는 곳이 있을까...

ReactorKit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let view = GreetingViewController()
/*  View
    - no business logic, only layout
    - action stream과 state stream을 바인딩 하는 역할만 수행한다!
*/
let reactor = GreetingReactor()
view.reactor = reactor
/* Reactor
    - view의 state 만 관리.
    - business logic 담당.
    3 Streams
        - Action : user의 action.
        - Mutation : state 변화
        - State : 현재 view 의 state
    3 Funcs
        - mutate() : Action을 받아 Mutation 생성
        - reduce() : 이전의 State 와 Mutation을 받아 새로운 State 생성
        - transform() : 각 stream을 전송한다. Observable for (Action, Mutation, State)
*/
cs

Pros

  • 한국의 갇수열(전수열)님께서 만들어 주셨음 !
  • Unidirectional : View는 Action만, Reactor는 State만 보낸다 !
  • 부분적으로 차용하는 것이 가능한 작은 규모의 아키텍쳐
  • 위의 것(?) 들과 마찬가지로 View가 정적이라 Testability 확보.

Cons

  • -

RIBs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
let interactor = GreetingInteractor()
/*  Interactor
    - Business Logic 담당.
    - state 변화, data 저장장소, RIBs 이동 결정.
*/
let router = GreetingRouter()
/*  Router
    - Interactor의 변화를 감지하고 child RIBs를 연결할지 뗄지 결정
    - HumbleObjects : Interactor의 Testability 확보
    - RIBs 간의 직접적인 소통이 아니라 Router를 거쳐 Interactor 간의 소통
  - Routing 알고리즘을 통해 Interactor의 역할을 부담함으로써 core 비즈니스 로직에 집중.
*/
let builder = GreetingBuilder()
/*  Builder
    - RIBs의 모든 구성 class 작성 로직을 담당 
    - 로직을 분리함으로써 Mockability 확장 및 DI 독점
*/
let dependency = GreetingDependency()
/*  Dependency
    - 특정 RIB가 Parent RIB로부터 인스턴스화해서 받아야할 종속성(변수,함수)을 나열하는 프로토콜.
*/
let component = GreetingComponent()
/*  Component
    - RIB Dependency를 관리. (Dependency 프로토콜의 구현)
  - Builder를 보조하여 RIB 구성요소를 생성.
  - Parent RIB의 Component는 Parent RIB의 Dependency 사용을 위해 Child RIB의 Builder에 주입된다.
*/
 
// MARK: Optional
let presenter = GreetingPresenter()
/*  Presenter
    - 정적인 Stateless class 이다.
    - Business model 을 View model로 변환할때 사용
  - 하지만 이 변환이 너무 사소할 때가 많아 optional 이다.
*/
let view = GreetingViewController()
/*  View(Controller)
    - UI layout 작업
    - 가능한한 "dumb"하게 작성한다.
*/
 
component.dependency = dependency
builder.component = component
presenter.view = view
interactor.presenter = preseneter
builder.interactor = interactor
router.routing = builder.build()
cs

Pros

  • 템플릿화가 잘되어 있다.
  • 프로토콜 지향을 할 수 밖에 없는 구조 !
  • **View 단위가 아닌 프로세스 단위로 흘러간다
  • 복잡한 로직 흐름을 모듈화 및 재사용하는 데에 특화되어있다.
  • DI 로직이 깔끔하게 분리되었다.
  • VIPER의 상위호환 이라고 한다(?) (우버에서 쓴대잖아..)

Cons

  • 적용 사례가 많지 않아 도입 장벽이 존재한다.
  • 프로토콜 지향은 좋지만 강제성이 불편한 경우가 많다.
  • 파일이 정-말 많이 생성된다.

Conclusion

이렇게.. iOS 에서 주로 사용되는 아키텍쳐들에 대해서 알아 보았습니다.

어떤게 좋다 나쁘다 하는 것은 없지만 시간의 순서대로 작성하면서

 

- View는 정적인 상태로 만들어 Testability 를 확보하자.
- 프로젝트 확장에 대비하여 로직을 분리하자.
- 명확한 코드 위치 및 데이터 흐름을 유지하자.

 

위의 세 가지가 주요 관심사라고 생각합니다.

 

이 포스팅에서 간략한 요약과 장단점을 비교하였는데, 적용하실 프로젝트 스펙에 맞춰 튜토리얼을 진행해보시는 것이 가장 좋다고 생각합니다 ! (무책임..) 

 

그리고 Rx 등과의 시너지 효과 등 다양한 사항을 고려해야하기 때문에 뭐가 좋나요? 라는 것은 가장 어려운 질문인 것 같습니다 ㅠ

 

저도 공부하면서 작성하였기에 부족한 부분이 많네요 ㅠㅠ 댓글로 피드백 주시면 너무 감사드리겠습니다 !

 

그럼 20000 !

 

 

참고 : https://medium.com/ios-os-x-development/ios-architecture-patterns-ecba4c38de52

댓글
공지사항