본문 바로가기
🍎 iOS/DevNote

[iOS] Coordinator Pattern을 사용해보자

by @Eddy 2023. 3. 23.
728x90

Coordinator 패턴을 이해하기 위한 선행학습


시작하기에 앞서, Coordinator가 왜 생겼는지 알아보자.

  1. ViewController가 너무 많은 역할을 담당하고 있다.
  2. ViewController에서 화면전환의 역할을 덜어주고 싶다.
  3. 화면전환 역할을 분리하자. ( = Coordinator )

 


Coordinator 패턴은 왜 사용할까?

Coordinator Khanlou라는 분이 만든 패턴이다.

Khanlou가 말하길, ViewController가 아래와 같이 너무 많은 역할을 담당하고 있다는 것에서 출발한다.

기존 ViewController의 역할
1. Model-View Binding
2. Subview Allocation
3. Data Fetching
4. Layout
5. Data Transformation
6. Navigation Flow
7. User Input
8. Model Mutation
9. and many more besides

기존 Khanlou의 코드는 15년도이기도 하고, Objective-C로 되어있어 Zedd님 블로그를 참고해 구현했다.

실제 프로젝트에서는 13개의 Coordinator 파일이 생성되었으나, 간단한 예제를 위해 2개로 진행했다.

 

기존 ViewController만을 이용해 화면전환을 할 때에는 단순히 아래와 같은 구조가 된다.

이와 같은 구조에서 알 수 있는 문제점은,

  1. Navigation(Push, Pop, Present, Dismiss 등)을 이용한 화면 전환을 ViewController가 부담하고 있다.
  2. 각각의 복잡한 ViewController에서 화면전환 기능을 확인하기 어렵다
  3. 높은 결합도로, 유지보수에 불리하다.
  4. 같은 ViewController로 화면전환을 하더라도, ViewController마다 새롭게 인스턴스를 생성해야 한다.
class AViewController: UIViewController {
    ...
    // BViewController로 Push
    @objc func didTapFirstButton() {
        let bVC = BViewController()
        navigationController?.pushViewController(bVC, animated: true)
    }
}

Coordinator 구조

Coordinator를 적용하면 아래와 같은 구조가 된다.

  1. AppCoordinator와, 하위 Coordinator를 생성한다.
  2. 하위 Coordinator는 각각의 ViewController마다 존재.


Coordinator패턴 사용 예제

1. Coordinator 프로토콜 생성 및 AppCoordinator 생성

  • childCoordinators: 하위 Coordinator들을 저장하는 배열
  • navigationController: 현재 Coordinator에서 사용할 Navigation Controller
  • start: Coordinator의 시작점을 나타내는 메서드

 

2. 첫 번째 AViewController 생성

 

3. AppCoordinator 하위의, ACoordinator생성

  • 시작하는 뷰라서 navigationController.viewControllers =[aVC]로 작성했다.
  • 만약 로그인 유무조건 분기로 기존 스타트 지점을 변경할 때 유용하다.
var isloggedIn = true

if isloggedIn {
  showACoordiator()
} else {
  showBCoordinator()
}

 

4. AppCoordinator에 ACoordinator 실행 코드 추가

 

5. BViewController에서는 Pop 작업을 해보자. ( 방법은 동일하다.)

 

6. BCoordinator 추가

 

7. AppCoordinator에 BCoordinator 실행코드 및 pop추가

  • childCoordinator.removeLast()를 하는 이유: 단순히 Push로 생성된 '뒤로 가기 버튼'을 클릭하면 Navigation의 최상단 Stack은 사라지지만, childCoordinators에 BCoordianator가 그대로 남아있게 된다.
  • Coordinator가 쌓인다는 것은, 메모리에 ViewController가 계속 누적되고 있어 Memory Leak을 유발하므로, pop작업을 할 때 childCoordinator의 마지막 배열을 제거해줘야 한다.
  • 따라서 childCoordinator에 메모리가 누적되는 것을 방지하기 위해 UIBackBarButton을 Custom 해야 하는데, 개인적으로 BackBarButtonItem은 Action에 추가 동작을 부여하기 까다로워 BViewController에서 LeftBarButtonItem을 활용했다. 다만, leftBarButtonItem은 BackBarButtonItem과 형태가 완전히 같지 않기에, 모든 뒤로 가기 버튼을 LeftBarButtonItem으로 커스텀해줘야 하는 불편함이 있다.

8. 코드 중복 최소화를 위한 BaseCoordinator 작성


Coordinator 활용 (Modal 화면에서 Push 하기)

  • 예시를 위한 구조이지만, 프로젝트에서는 Coordinator를 다중 사용해 이처럼 사용했다.
  • B -> C를 Present 하고, C -> D로 Push 하기 위해서는,
  • C에서 UINavigationController(rootViewController: )를 사용해 D로 화면전환 하기 때문에 하위 Coordinator를 추가 생성해야 했다.

 

동작은 기본 동작과 동일하다.

예제 코드는 깃허브를 참고

 

GitHub - JUNY0110/iOS_UIKit_DevNote: UIKit과 관련된 공부내용을 올리는 정리노트입니다.

UIKit과 관련된 공부내용을 올리는 정리노트입니다. Contribute to JUNY0110/iOS_UIKit_DevNote development by creating an account on GitHub.

github.com


Coordinator의 장점 ( Khanlou )

1. 각 ViewController가 고립된다.

(Each view controller is now isolated)

2. ViewController를 재사용할 수 있다.

(View controllers are now reusable.)

3. 앱의 모든 작업과 하위 작업에 전용 캡슐화 방식이 적용된다.

(Every task and sub-task in your app now has a dedicated way of being encapsulated.)

4. Coordinator가 Side effect로부터 display-binding을 분리한다.

(Coordinators separate display-binding from side effects.)

5. Coordinator는 완전히 제어할 수 있는 객체다.

(Coordinators are objects fully in your control.)


내가 경험한 Coordinator 장점

  • ViewController 재사용에 용이했다.
  • 화면전환 역할 분리로 ViewController의 결합도를 낮춰 유지보수하기 편리해졌다.
  • 화면전환 플로우에 대해 관리하기 좋았다.
  • 불필요한 ViewController가 Navigation stack에 쌓여있지 않도록 메모리 관리에 유용했다.

 

내가 경험한 Coordinator의 단점

  • 화면전환기능이 있는 ViewController마다 1:1 매칭이 되는 Coordinator를 생성해야 하기 때문에, 코드가 길어지며, 하드코딩이 될 가능성이 높다. 위에서 언급했듯 예제는 2개뿐이지만, 기존 프로젝트는 13개의 Coordinator가 작성되었다.. ( 제대로 이해하지 못했더라도, 하나만 제대로 작성되어 있으면 나머지를 작성하면서 이해하게 된다. )
  • Delegate와 상속에 대한 이해가 있어야 하며, 활용이 까다로워 러닝커브가 높다.( 개념은 간단하지만, 실제 구현에 대한 이해가 어려움 )
  • Coordinator패턴에 대한 레퍼런스가 부족하다고 느꼈다. 레퍼런스가 있더라도 이해하기 쉬운 코드가 많지 않았다.
  • 단점은 아니지만, 작은 프로젝트에서는 오버엔지니어링이 될 가능성이 높다.

 

사용 간 어려웠던 점

  • ViewController 간 데이터 전달 ( ViewController와 화면전환이 Delegate를 통해 완전히 분리되었기 때문 )
  • AppCoordinator의 CViewCoordinator로 DViewCoordinator를 만들어 활용할 때, 로직이 복잡하다는 생각이 들었다. (Present -> Push 작업)

 

느낀 점

  • 새로운 패턴을 익히는 게 얼마나 까다로운지 알게 되었다.
  • 또한 현 프로젝트에 적합한, 적정 엔지니어링일까?라는 생각도 들었고, Coordinator파일이 13개나 되다 보니 너무 하드코딩으로 느껴졌다. 코드를 줄일 수 있다면 좋을 텐데, 이는 Delegate를 사용하다 보니 어쩔 수 없는 듯..
  • 구현한 내용을 글로 풀어쓰는 작업이 꽤 어려웠다. 다른 블로그도 많이 참고하면서 다른 사람들이 말하는 좋다 나쁘다도 중요하지만, 온전히 와닿지 않은 부분들도 있어 내가 느낀 장단점을 나눠서 작성했다.
  • 이런 작은 부분에서도 문제인식을 하고 새로운 해결방안을 제시했다는 점이 Coordinator가 주는 교훈(?)이 아닐까 싶다.
  • 다음은 Coordinator에서 주구장창 사용한 Delegate에 대해 정리할 예정이다.

 

** 주요 참고자료 **

[iOS] Delegate패턴을 이해해보자

[Khanlou] Coordinators Redux

[Zedd] Coordinator Pattern

반응형

댓글