[iOS] Coordinator pattern 공부 - 1
안녕하세요 Pingu입니다.🐧
오늘은 iOS에서의 Coordinator Pattern이라는 패턴에 대해 알아보는 글을 써보려고 합니다.
공부하다 보니 이 패턴을 만든 사람이 있었습니다. KHANLOU라는 블로그를 운영하는 사람인데.. 거기에 The Coordinator라는 글이 코디네이터 패턴의 시작..? 같습니다. 그리고 이 글을 쓰고 나서 몇 달 뒤에 좀 더 정리해서 Coordinators Redux라는 글을 또 작성했는데, 이 두 개의 글을 읽고 정리해 보겠습니다.
Coordinator Pattern 탄생 배경
글이 2015년에 작성되었는데 그때부터 iOS 개발자들은 이미 Massive ViewController가 문제가 되고 있었던 거 같습니다.
이 사람이 코디네이터 패턴을 생각하게 된 기존 문제점은 3개였다고 해요.
- AppDelegate에 너무 많은 코드
- (지금은 SceneDelegate이지만..) AppDelegate가 앱 진입점이다 보니 이것저것 넣기 쉬운 지점인데, rootViewController를 구성하기 위한 로직도 여기에 존재하는 경우가 많았다고 합니다.
- 근데 RootViewController를 구성하는 책임이 AppDelegate에 있느냐?에 대한 의문
- ViewController에 너무 많은 책임
- 이 사람이 생각하기에 문제 되는 VC는 아래와 같은 책임들을 가지고 있다고 합니다.
- Model - View Binding
- Subview Allocation
- Data Fetching
- Layout
- Data Transformation
- Navigation Flow
- User Input
- Model Mutation
- 기타 등등
- 이 모든 게 VC에 존재하니까 Massive VC가 되는 것;
- 그럼 VC는 뭘 해야 할까?를 고민하다 보면 우리는 Controller라는 단어에 가스라이팅 당한 걸 지도 모릅니다. 이름 때문에 뭐든지 다 VC가 해도 될 것만 같은 거죠.
- 이 사람이 생각하기에 문제 되는 VC는 아래와 같은 책임들을 가지고 있다고 합니다.
- Smooth Flow
- navigation flow를 VC에서 처리하게 되면 이는 VC가 자신의 부모인 navigationController에게 명령을 내리는 행위인데, 느낌상 자식이 부모에게 명령하는.. 버릇없는 행동이라고 생각한다고 합니다.ㅋㅋ
- 또한 이 사람은 코딩세계에서는 Child가 Parent를 모르는 게 좋다고 생각합니다.
근데 여기서 flow logic이 뭐냐!
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectData = self.data[indexPath.item]
let detailVC = DetailViewController(selectData)
self.navigationController.pushViewController(detailVC, animated: true)
}
위와 같이 어떤 VC를 생성해서 NavigationController에 push 하는 로직을 의미합니다.
이렇게 VC 자체에 flow logic이 존재하게 되면 해당 로직들이 분산될 뿐 아니라, 각각의 VC가 자신의 다음 flow에 해당하는 VC들의 정보를 모두 알고 있어야 한다는 문제가 발생합니다.
또 이 사람은 Library와 Framework 관점에서 개발자는 프레임워크인 UIKit를 마치 라이브러리처럼 다뤄야 한다고 합니다.
이 두 개의 차이점은 사용자는 라이브러리를 호출해서 사용하며, 프레임워크는 사용자를 호출해서 사용한다! 입니다.
저는 요걸 이렇게 이해했어요.
- UIKit을 Framework로 사용
- 프레임워크가 viewDidLoad를 호출해 줄 때까지 개발자는 원하는 코드를 실행할 수 없음.
- UIKit을 Library로 사용
- viewDidLoad를 기다리는 것이 아닌, 원하는 시점에 원하는 코드를 실행할 수 있음.
- 즉 UIKit에서 벗어나 코드의 흐름을 완전히 제어할 수 있음
그래서 Coordinator가 뭔데요
결론부터 말하면 Coordinator는 하나 이상의 VC를 지배하는 객체입니다.
뷰 컨트롤러를 가장 상위의 개념으로 보는 것이 아닌 이를 관리하는 하나의 객체를 더 두고 이들을 관리하는 거죠.
그래서 아까 그 많던 VC의 책임 중 어떤 것들을 Coordinator로 넘겨주느냐,, 하면 이 사람 생각은 flow logic, model mutation 책임을 넘겨주자고 합니다. (model mutation은 DB write 혹은 REST API PUT, POST 등을 의미한다고 해요)
이렇게 해야 VC는 데이터를 사용하기만 하고, 변경은 할 수 없게 된다고 합니다.
이렇게 되면 VC는 그저 데이터를 뷰로 보여주기만 할 뿐 아무것도 못하게 됩니다.
그래서 사용자 입력이나 이벤트들을 delegate와 같은 방법으로 처리해줘야 하죠.
Coordinator가 뭐가 좋은데요
그래서 이걸 쓰면 뭐가 좋냐!
- VC가 독립적으로 존재하게 된다
- VC는 데이터를 보여줄 뿐 아무것도 못 함
- 이벤트 발생 시 delegate와 같은 방법으로 처리해야 하는데 VC는 이를 처리하는 주체가 누군지도 모름
- A/B 테스트 혹은 여러 개의 플로우가 필요한 경우엔 VC에 조건문을 붙이는 게 아니라 코디네이터 객체를 바꾸면 된다
- 모든 flow logic이 각각의 VC에서 한 곳에 모이게 되므로 이해가 쉽다.
- VC가 재사용 가능해진다
- 버튼이 어떤 용도인지와 같은 것을 가정하지 않는다 (버튼이 눌렸다는 이벤트만 넘겨줄 뿐)
- flow logic을 복붙 할 필요 없이 그대로 재사용이 가능하다
- 앱의 모든 하위 작업을 일관된 방법으로 캡슐화가 가능
- 여러 개의 VC의 이벤트에 의해 동일한 작업이 수행될 때 하나의 코드로 처리 가능
- 뷰 바인딩과 side effect를 분리
- VC가 데이터를 변경할 수 없으니, VC에서 데이터가 잘못 변경될 것을 걱정할 필요가 없어짐 (어떻게 보면 단방향 개발?)
- Coordinator는 온전히 개발자가 지배할 수 있음
- 아까 위에서 언급한 대로 UIKit을 라이브러리처럼 활용할 수 있으므로 원하는 시점에 원하는 코드 호출이 가능
이렇게 많은 장점을 가진 Coordinator를 사용하면 앱과 코드를 관리하기 쉽게 만드는데 도움을 주고, VC가 쉽게 재사용가능하므로 앱을 성장하는데 많은 도움을 준다고 하며 글은 끝납니다.
근데 조금 아쉬운 게, 창시자가 코디네이터 패턴에 대한 라이브러리나 프로토콜 같은 걸 진심으로 정의해두진 않았습니다;
물론 예제 코드가 있긴 해서 코디네이터 패턴을 공부하신 분들의 코드를 보면 대체로 해당 예제 코드를 응용한 거 같았어요.
예제 코드는 다음글에서 작성해 보도록 하고 오늘은 여기서 글을 마무리해 보도록 하겠습니다.
감사합니다.