Coordinator Pattern

Zedd
12 min readNov 15, 2020

--

안녕하세요 :) Zedd입니다.

오늘은 Coordinator 패턴 대해서 공부해보겠습니다.

# Coordinator?

Coordinator는 coordinate라는 동사에서 나온 것 같은데요.

coordinate는 뜻이 여러개 있지만..제 추측상

오늘 우리가 배울 Coordinator는 움직임을 조정하는 사람을 뜻한다고 볼 수 있을 것 같아요.

# Coordinator Pattern?

Coordinator Pattern을 공부하려고 시작하면,

대부분의 article에서 Coordinator 프로토콜을 만들고…start메소드를 구현하고….하는것을 볼 수 있는데요,

일단 왜 이런식으로 시작하는지부터…궁금해서요.

이 Coordinator의 시작부터 알아가보면 좋을 것 같습니다.

Coordinator Pattern은 2015년, Soroush KhanlouThe Coordinator라는 글을 쓰면서 소개됩니다.

이때 Khanlou는

Khanlou : Massive View Controller의 가장 큰 문제 중 하나는 flow logic(흐름 로직) 및 business logic이 얽혀있다는 것이다!

라고 생각합니다.

다음은 Khanlou가 예로 든 코드입니다. (원문에는 objc로 쓰여있으나, 제가 보기 편하게 Swift로 컨버팅했습니다.)

TableViewCell을 클릭하면 호출되는 didSelectRowAt메소드입니다.

단 3줄입니다.

1. 객체를 가져옵니다.

2. ViewController를 만듭니다.

3. ViewController를 보여줍니다.

간단한 앱에서는 아무 문제없이 완벽하게 동작합니다.

하지만 앱이 복잡해짐에 따라, ViewController가 새로운 방식 또는 새로운 위치에서 사용될 수 있죠.

위에서 만든 DetailViewController가 저기서 한번만 쓰이는게 아니라

또 다른 곳에서 만들어지고 또 보여질 수 있다는 것이죠.

복잡한 앱에서 위 코드를 다시 살펴보겠습니다.

1. 객체를 가져옵니다.
→ 첫번째 줄은 괜찮습니다. datasource는 viewController의 논리적인 자식이며, 참조해야하는 객체를 요청하고 있습니다.

2. ViewController를 만듭니다.
→ 여기서부터 문제가 생기기 시작합니다. ViewController는 flow의 다음 단계를 “인식”하게 됩니다.

3. ViewController를 보여줍니다.
→ 여기서부터는 완전히 벗어나는 곳입니다. ViewController는 이제 부모까지 잡습니다.
부모 ViewController에게 해야할 일에 대한 정확한 메세지를 보내고 있습니다.
(한마디로 부모에게 이래라 저래라 하고 있다는 겁니다.)

더 큰 문제는 이러한 flow logic이 여러 ViewController에 분산되어 있을 수 있다는 겁니다.

Khanlou는 이렇게 생각하죠.

Khanlou : ViewController 기본 클래스는 UI로 시작되며(UIViewController), View객체이고, 사용자 흐름을 처리하는것은 범위(scope)를 벗어난다!

Khanlou는 ViewController를 높은 수준의 객체로 관리하게 되면 많은 이점을 얻는다는 것을 알게 됩니다.

이 높은 수준의 객체의 역할은 모든 ViewController를 모으고(marshal) 관리하는 것이죠.

Khanlou는 이 높은 수준의 객체를 Coordinators 또는 Directors라고 부르기로 합니다.

그리고 이 패턴을 제대로 실행하려면 전체 앱을 direct하는 high-level coordinator가 필요하다고 합니다.

AppDelegate(SceneDelegate)는 AppCoordinator를 유지하며, 모든 Coordinator에는 일련의 하위 Coordinator가 있습니다.

이정도 컨셉만 알고가면 됩니다.

자세한 내용은

The Coordinator

Coordinators Redux

를 참고하세요. 일단은

이러한 간단한 코드가 문제가 있을 수 있구나..라는 생각만 들면 됩니다.

# Coordinator Pattern 사용해보기

그럼 본격적으로 Coordinator Pattern을 사용해보겠습니다.

Khanlou가 주장하는 것은 위에서도 말했듯이,

1. AppCoordinator가 존재.

2. 모든 Coordinator에는 일련의 하위 Coordinator가 있음.

입니다.

Coordiantor의 구현방법은 앱 시나리오에 따라 조금씩 다릅니다.

저는 Khanlou의 Coordinators Redux를 보고,

이런 앱을 만들어볼겁니다.

처음에는 왼쪽의 로그인 화면으로 시작되며, 우상단의 로그인 버튼을 누르면 오른쪽 그림처럼 화면이 바뀝니다.

그리고 오른쪽 화면에서 다시 우상단의 로그아웃버튼을 누르면 왼쪽 그림처럼 바뀝니다.

⚠️Coordinators Redux에서 위와같은 앱을 만든건 아닙니다! 제가 참고만 한거고, 코드는 다를 수 있습니다.⚠️

이런식으로 구조가 만들어질 것 같습니다.

1. Coordiantor 프로토콜을 만든다.

2. Coordinator프로토콜을 채택하는 AppCoordinator를 만든다.

Coordiantor프로토콜이 childCoordinators와 start메소드를 요구하기 때문에, 구현해줍니다.

3. isLoggedIn이 false면 Login화면을, 아니라면 Main화면을 보여준다.

4. SceneDelegate설정.

window와 UINavigationController를 만듭니다.

그리고 AppCoordinator를 만든 UINavigationController를 넘겨 만들어주고, start메소드를 호출합니다.

5. showLoginViewController구현.

showLoginViewController에서는 LoginCoordinator를 만들고 start를 호출해줄건데요,

먼저 LoginCoordinator를 만들겠습니다.

5–1. LoginCoordinator구현

LoginCoordinator의 start안에서 LoginViewController를 만들어주고있습니다.

LoginViewController를 만들어주겠습니다.

5–2. LoginViewController구현.

BarButtonItem을 만들어주고 넣어주었습니다. 이제 loginButtonDidTap을 구현해야합니다.

5–3. LoginViewControllerDelegate구현.

LoginViewController에서 Login버튼을 누르면 LoginCoordinator가 이를 알아야합니다.

그래서 LoginViewControllerDelegate를 구현해주겠습니다.

5–4. LoginCoordiantor에서 대리자 설정

LoinViewController의 로그인 버튼이 눌리면 LoginCoordinator가 알아야한다고 그랬죠?

그렇기에 delegate를 LoginCoordinator로 지정해줍니다.

그러기 위해서는 LoginCoordinator가 LoginViewControllerDelegate를 채택해야겠죠.

채택 후, login메소드를 구현해줍니다.

5–5. AppCoordinator에게 로그인 했다고 알려준다.

로그인이 됐으면 AppCoordinator에게 이 사실을 알려 MainViewController로 가는 작업을 실행해야합니다.

5–6. LoginCoordinatorDelegate구현.

이를 알리기 위해 LoginCoordinatorDelegate를 이용하여 LoginCoordinator와 AppCoordinator를 연결해줍니다.

그럼 이제야 위에서 하다 말았던

5. showLoginViewController구현.

를 할 수 있게 됩니다.

AppCoordinator로 가줍니다.

showLoginViewController에서 LoginCoordinator의 delegate를 AppCoordinator로 지정해줍니다.

그러기 위해선 역시 AppCoordinator가 LoginCoordinatorDelegate를 채택해야겠죠.

LoginCoordinatorDelegate가 요구하는 didLoggedIn메소드를 구현해줍니다.

  • showLoginViewController

LoginCoordinator를 만들어준 뒤, childCoordinators배열에 만든 coordinator를 넣어줍니다.

  • didLoggedIn

didLoggedIn은 LoginCoordinator파라미터를 받는데요,

AppCoordinator가 가지고있는 childeCoordinators에서 LoginCoordinator를 지워줘야하기 때문입니다.

지운 뒤 MainViewController 메소드를 호출합니다.

Q : childeCoordinators는 왜 있는거야?

A : (Coordinators Redux에 있는 말입니다)

child Coordinator가 할당 해제되는 것을 방지하기 위해 child Coordinators배열을 사용한다고 합니다.

이 childCoordinators로 인해 Coordinator Tree가 생성된다고 합니다.

Q : start에서 self.navigationController.viewControllers = [viewController]로 해준 이유

A : 이쪽은 여러 방법이 있을거라고 생각이 드는데..현재 Tree(?)에 지금 만드는 ViewController만 있었으면 해서 이렇게 해주었습니다.

이게 로그인/로그아웃 프로세스가 아니라

flow가 이어지게(?) navigation이 된다면 이렇게 하지말고 push를 이용해야겠죠.

자..지금까지

왼쪽 화면까지 한겁니다. 이제 오른쪽 화면을 보여줘야하는데요. 위에서 했던거랑 똑같은 작업을 해주면 됩니다.

6. Main작업

6–1. MainCoordinator작업.

6–2. MainViewController작업.

이렇게 하면 모든 작업이 끝나게 됩니다.

프로젝트는 github에 올려두었습니다.

Coordinators Redux를 많이 참고했으나, 어색하거나 틀린점이 있다면 댓글로 말씀해주시면 감사하겠습니다.

# Coordinator가 훌륭한 이유

1. 각 ViewController의 고립.

→ ViewController는 데이터를 표시하는 방법 이외에는 아무것도 모르며, 어떤일이 발생할 때마다 대리자(Delegate)에게 알리지만 Delegate가 누군지는 알 수 없습니다.

에전같았으면 ViewController에서

if Device.isiPad {
//
} else {
//
}

내가 iPad를 사용중인가? 아닌가? 이런식으로 분기했어야 한다면 이제는 ViewController가 이러한 조건을 검사할 필요가 없어졌습니다.

2. ViewController의 재사용.

ViewController는 어떤 context로 표시될지, 버튼이 어떤 용도로 사용될지에 대해 아무것도 가정하지 않고 있습니다.

앱의 iPad 버전을 작성하는 경우 Coordinator만 교체해야하며 모든 ViewController를 재사용 할 수 있습니다.

3. 앱의 모든 작업과 하위 작업은 전용 캡슐화 방법을 제공.

4. Coordinator는 display-binding을 side effects와 분리.

ViewController를 표시할 때 ViewController가 데이터를 엉망으로 만들지 여부에 대해 걱정할 필요가 없어집니다.

읽기 및 디스플레이만 할 수 있으며 데이터를 쓰거나 손상기키지 않습니다.

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

viewDidLoad가 호출 될 때 까지 기다리지 않고 전적으로 show를 제어할 수 있습니다.

호출을 받는 대신 호출을 시작합니다.(Instead of being called, you start doing the calling.)

Khanlou가 Backchannel SDK프로젝트를 글 하단에 첨부했습니다.

100% objc고 마지막 커밋이 4년전이지만..참고하시길 바랍니다.

저는 그냥

The Coordinator

Coordinators Redux

글을 한번 쯤 읽어보는 것을 추천드립니다.

오늘 하면서 느낀점은..

RIBs같은 라이브러리가 나오는게 당연한 수순이었다?..라는 생각이 드네요.

참고

The Coordinator

Coordinators Redux

--

--