안녕하세요 이누입니다.
개인적으로 아키텍처 부분이 많이 부족하다는 생각이 들어서 Clean Architecture 이론과 MVC, MVP, MVVM 아키텍처의 개념을 최대한 iOS의 관점에서 바라보며 공부하고 정리해봤습니다. 그럼 시작할게요!
Clean Architecture
먼저 Clean Architecture에 대해서 간단하게 이야기해보겠습니다. Clean Architecture는 Robert C. Martin(a.k.a Uncle Bob)님이 제안하신 '좋은 아키텍처'에 대한 이론입니다. (위 그림에 나오는 각 레이어의 자세한 역할과 의미에 대해서는 원글(https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)을 참고해주세요.)
Clean Architecture의 핵심은 바깥쪽 레이어가 안쪽 레이어에 대한 정보는 알아도 되지만, 안쪽 레이어는 바깥쪽 레이어에 대해 최대한 몰라야 한다는 것입니다. 그냥 레이어들끼리 왔다 갔다 하면 좀 더 자유로운 구현이 가능할 텐데 왜 이렇게까지 하는 것일까요?
이렇게 하면 프로젝트의 핵심 비즈니스와는 관련 없는 외부 사항에 대한 결정들을 최대한 미룰 수 있기 때문입니다. UI나 DB, API 등의 외부 사항은 서비스가 완성된 뒤에도 충분히 교체 및 수정의 가능성이 있는 반면, 비즈니스 룰은 그 변화의 가능성이 매우 적습니다. 이러한 비즈니스 룰의 구현을 외부 사항들에 의존적으로 하게 되면 외부 사항이 변경될 때마다 영향을 받게 됩니다. 따라서 외부 사항에 대한 결정을 확실히 하지 않으면 비즈니스 룰도 구현할 수 없는 상황이 발생합니다. 하지만 레이어의 의존관계를 확실하게 정리하면, 외부 사항에 대한 결정이 이루어지지 않은 상태에서도 비즈니스 룰에 대한 구현이 가능합니다. 비즈니스 룰이 추후 외부 사항의 교체나 제거, 추가 등에 있어 자유롭기 때문입니다. 완전히 독립적이기 때문에 외부에 의존하지 않고 테스트를 할 수 있는 것은 덤입니다.
MV(X)
이제 본격적으로 아키텍처 패턴들에 대해 이야기해봅시다. MVC, MVP, MVVM-이하 MV(X)-은 모두 Model과 View를 가진다는 점에서 패턴이 유사합니다. 조금씩 다르지만 가장 큰 차이점은 역시 Controller / Presenter / ViewModel 부분입니다. 간단하게 각 부분의 정의를 살펴보겠습니다.
- Model : 데이터를 조작하는 도메인 영역입니다. 서비스의 데이터와 관련된 핵심 비즈니스 로직이 들어갑니다.
- View : UI와 관련된 것들이 포함됩니다.
- Controller / Presenter / ViewModel : View로부터 유저 액션을 받아 Model을 변경하고 그에 따라 View를 관리하는 중재자 역할을 하는데, 그 수행방식은 각각 다릅니다. 각각의 역할에 대해서는 아래에서 하나씩 설명드리겠습니다.
MV(X) IS JUST UI Architecture
MV(X)는 UI 레이어 관련 부분을 설계하는 UI Architecture이지, 전체 System Architecture가 아닙니다. 따라서 이들은 모두 UI 레이어를 중심으로 데이터가 어떻게 흘러가고 책임이 나눠지는지를 설명합니다. 네트워킹이나 캐싱, 비즈니스 로직의 내부 사항들에 대해서는 설명하지 않습니다. 따라서 우리가 MV(X) 아키텍처들을 학습하면서 집중해야 하는 부분은 UI 데이터가 어떻게 전달되는가입니다. 네트워킹이나 캐싱, 비즈니스 로직은 모두 Model 너머에서 알아서 처리한다고 생각합시다.
자주 참고하던 레퍼런스에 좋은 이미지가 있어서 추가적으로 첨부합니다. 우리는 아래쪽에 'Presentation MVVM'이라고 되어있는 부분을 MV(X)로 구현하려는 상황인 겁니다. Domain이라고 되어있는 부분과 Data Repositories라고 되어있는 부분은 우리 입장에서는 Model너머에서 알아서 한다고 생각하면 돼요. 저 부분의 레이어가 어떻게 나뉘는지는 신경 쓰지 맙니다! 우리는 View와 Controller / Presenter / ViewModel 쪽만 신경 쓰면 되는 거예요. 이들을 중심으로 각 패턴들을 하나씩 살펴봅시다.
MVC
가장 기본적인 MVC 패턴입니다. MVC에서 View가 받은 유저 액션은 바로 Controller에게 전달됩니다. Controller는 이를 Model에 알려 적절한 비즈니스 로직을 수행하고 내용을 업데이트하도록 합니다. View는 Model의 상태 변경 혹은 Controller의 명령에 따라 자신을 업데이트합니다. 이러한 MVC 패턴은 세 가지 구조(View, Controller, Model)가 모두 얽혀있기 때문에 Clean Architecture의 관점에서 좋은 구조라고 보기 어렵습니다.
TMI : 사실 이는 최초의 MVC와는 조금 다릅니다.
최초의 MVC에서는 위 그림처럼 Controller에서 직접 유저의 명령을 받고 Model에 업데이트를 시키며 View는 Model을 관찰하여 상태를 변경하는 깔끔한 구조였습니다. 정확하지는 않지만 GUI가 발전하면서 유저가 화면과 직접적으로 소통할 수 있어짐에 따라 View에서 유저 액션을 최초로 받아올 필요성이 생겨 이렇게 변형된 것이 아닐까 싶네요.
Cocoa MVC
Apple이 고안한 Cocoa MVC는 위에서 이야기한 MVC와 다릅니다. 기존에 View가 Model을 Observing 하는 부분을 없애고, 대신 Controller가 이를 담당하고 View의 업데이트까지 담당하도록 했습니다. 그에 따라 Controller는 둘 사이의 중계자 역할을 맡게 되었으며, View와 Model은 완전히 분리되었습니다.
이러한 Cocoa MVC는 Model을 View에게서 확실히 독립시킨 데다, 다른 추가적인 코드가 필요 없어 개발 속도가 빠르다는 장점이 있습니다.
하지만 Apple의 Controller인 ViewController는 자체적으로 View와 많은 연관을 가지며 라이프 사이클과 관계를 맺기 때문에 이 둘 간의 독립성이 확보되었다고 보기는 어렵습니다. 이는 당연히 Clean Architecture의 관점에서 봤을 때도 좋은 구조가 아닙니다.
MVP
MVP에서는 Presenter가 Controller의 역할을 대신합니다. 그리고 iOS에서는 ViewController까지 View로 취급합니다. 자체적으로 View와 연관을 가지며 라이프 사이클을 가지는 ViewController를 우선 바깥쪽 View 쪽으로 미뤄낸 것입니다. 그리고 Presenter는 View들에 대한 참조를 직접 프로토콜 형태로 가지고 변경사항을 반영합니다.
따라서 Presenter는 UIKit을 import하지 않고 View의 변경사항을 관리할 수 있습니다. 이는 Presenter의 독립성까지 확보되었으며, 테스트까지 가능하다는 의미입니다.
하지만 MVC에 비해 구현해야 하는 코드가 많아 초보자에게는 어려울 수 있습니다. 또 Presenter가 View에 대한 변경사항을 결정하기 때문에 Presenter가 View에 대해 어느 정도 알고 있어야 합니다. 이에 따라 여러 View에서 하나의 Presenter를 통해 유연하게 다른 View를 구성하는 것이 매우 어려워집니다. Presenter와 View가 보이지 않는 연관성을 가지고 있는 것으로도 볼 수 있는 것입니다. 이를 좀 더 개선해보면 좋을 것 같습니다.
MVVM
MVVM은 ViewModel이 Presenter와 유사한 역할을 합니다. MVVM에서도 ViewController는 View로 취급됩니다. 다만 ViewModel이 직접 View를 참조하고 있지는 않고 View가 ViewModel의 정보를 바인딩하는 형태로 구성된다는 점이 다릅니다. 바인딩은 주로 RxSwift나 Combine과 같은 FRP 라이브러리를 사용하고, KVO 패턴이나 Delegate 패턴, Property Observer 등을 사용할 수도 있습니다. 어쨌든 바인딩을 하고 있기 때문에 ViewModel의 변경사항을 View 쪽에서 반응적으로 확인해서 반영할 수 있습니다. ViewModel은 이에 관해 알 필요가 없고, View가 자신의 변경사항을 반영하는 책임까지 지게 됩니다.
결과적으로 ViewModel은 데이터를 보내고 받는 역할만 하게 되었습니다. 따라서 ViewModel도 Presenter와 마찬가지로 테스트가 가능합니다. 거기에 더해 View에서 바인딩된 값을 이용해 독립적으로 변경사항을 결정하고 반영하기 때문에 ViewModel을 구현할 때는 View의 형태를 신경 쓸 필요가 없어졌습니다. 서로 간의 독립성이 완전히 확보된 것입니다. MVP에서 존재하던 보이지 않는 연관성까지 해소되었네요. 이제는 필요하다면 하나의 ViewModel을 여러 View에서 활용할 수도 있습니다.
그렇다고 MVVM이 완벽한 아키텍처인 것은 아닙니다. 이 역시 MVP만큼 구현해야 하는 코드가 많고 복잡하기 때문에 초보자에겐 어려울 수 있습니다. 또 데이터 바인딩을 많이 사용하면 메모리 소모가 심해질 위험이 존재하기 때문에 유의해야 합니다.
정리 및 마무리
MVVM이 MVC, MVP보다 많이 사용되는 이유는 ViewModel이 독립적으로 테스트가 가능해서 일뿐 아니라, ViewModel이 '어떻게 그릴지'에 대한 고려를 하지 않아도 되게 만들어서 한 층 위인 View부분에 대한 고려가 완전히 필요 없어졌기 때문이었습니다. 이는 Clean Architecture의 이념과도 아주 잘 맞았습니다. 솔직히 프로젝트에서 MVVM을 사용하면서도 이런 관점에 대해서는 잘 생각해보지 못했는데 정말 공부하길 잘한 것 같습니다!
이번 글을 작성하면서 아키텍처를 공부할 때 저를 가장 많이 괴롭혔던 것은 자료마다 아키텍처 패턴에 대해 정의하는 범위가 조금씩 다르다는 것이었습니다. 그래서 제가 작성한 이야기에 오류가 있을 가능성도 있습니다. 그런데 제가 느끼기에는 아키텍처 패턴이라는 게 어떤 기관에서 특별한 표준이나 문서로 관리되는 것이 아니라서 그런지 변형 및 오염이 많이 되는 것 같습니다. (클린 아키텍처를 고안하신 엉클 밥님께서 말씀하시길 MVC가 그랬고 애자일이 그랬고 객체지향도 그랬다고 했습니다...) 그렇기 때문에 특정 인물이 제시하는 아키텍처 패턴에 무작정 맞추려고 하기보단, 아키텍처 패턴을 참고하여 상황과 내 프로젝트에 맞도록 구조를 짜는 것이 더 올바른 자세라고 생각합니다.
긴 글 읽어주셔서 감사합니다. 👍 (오류 및 오타 지적 환영합니다!)
참고
'🍎 Apple > Patterns' 카테고리의 다른 글
[iOS Design Pattern] Decorator (0) | 2022.05.18 |
---|---|
[iOS Design Pattern] Factory Method (0) | 2022.04.26 |
[iOS Design Pattern] Coordinator (3) | 2022.04.12 |
[iOS Architecture] VIPER (0) | 2022.04.11 |
Clean Architecture and Design - Robert C. Martin (엉클 밥) (2) | 2022.01.21 |