[Clean Architecture] Presentation(MVVM), Domain, Data

2024. 8. 30. 18:38🍏/Architecture

Clean Architecture and MVVM

  iOS에서 가장 유명한 Clean Layered Architecture and MVVM 클린 아키텍쳐에 대해서 얘기해 보려고 합니다.

  여태까지 UIKit으로 진행한 프로젝트들은 거의 MVVM으로 진행 했었고 나름 이정도면 MVVM에 대해서 잘 알고 있다고 생각했었는데요. 그냥 우물안의 개구리였을 뿐이라는 것을 깨달았습니다. 여태까지 한 프로젝트들은 Presentation의 MVVM 까지 구현하고 VM에서 Business 로직을 실행하고 있었습니다. 도메인과 데이터는 Clean Layered가 아닌 그냥 제가 편한대로 구현하고 있었는데요. 

  이번에 개인 프로젝트를 진행하면서 정리를 해볼겸, 설명을 좀 쉽게 해서 이해해볼 겸 작성합니다.

iOS 개발자라면 아래와 같은 그림을 보지 못한 사람은 아무도 없을거라 생각합니다. 오죽하면 저도 MVVM 겉핥기 밖에 못하는데 이 그림은 알고 있었을까요.. 그때로 돌아간다면 처음부터 끝까지 완독하라고 100번은 얘기했을 것 같습니다. 지금이라도 읽었으니 다행이네요. 😂

https://github.com/kudoleh/iOS-Clean-Architecture-MVVM

 

그림을 그냥 딱 봤을 때, 솔직히 머릿속에 들어오진 않습니다. 원본 Medium 블로그 글 이 글을 봐도 이게 무슨 소리야 😤 싶으실 겁니다.

이 그림에서 크게 중요하게 봐야할건 딱 두가지 입니다. 빨간 동그라미, 빨간 화살표 입니다.

여기에서 빨간 화살표는 종속성 규칙입니다.
1. 내부 레이어에서 외부 레이어로 종속성을 가지지 않는다는 뜻.
2. 외부 레이어에서는 내부로만 종속성이 있을 수 있다는 뜻.
그 말이 그 말이고 같은 말인데 이게 무슨 뜻이냐 ?

1. 가장 안쪽의 Domain Layer(비즈니스 로직)를 살펴 보겠습니다. Entities는 아무런 종속성도 갖지 못합니다. 이게 무슨 뜻이냐 ? 내부 레이어인 Entities는 Presentation Layer(UI 로직), Data Repositories Layer(데이터 접근 로직)와 같은 외부 레이어의 구현 세부 사항을 몰라야 한다는 뜻입니다. 예를 들어, Domain Layer에는 UIKit 또는 SwiftUI와 같은 프레임워크가 포함될 수 없습니다. 또한, JSON 파싱을 위한 Codable이나 네트워크 관련 코드도 포함되지 않습니다.

2. 외부 레이어(Presentation Layer와 Data Repositories Layer)는 내부 레이어인 (Domain Layer)의 비즈니스 로직을 기반으로 기능을 수행합니다. 외부 레이어는 내부 레이어에만 의존할 수 있으며, 다른 외부 레이어나 프레임워크와의 의존성은 가능하더라도, 이 의존성이 역방향으로 가는 것은 허용되지 않습니다. 예를 들어, ViewModel(Presentation Layer)은 Domain Layer의 Use Cases를 호출하여 데이터를 가져오고 처리할 수 있지만, 그 반대는 안 됩니다. Domain Layer의 Use Cases는 ViewModel의 모든 요소에 대해 아무것도 알지 못해야 합니다. 호출하거나 변경할 수 없습니다.

이것만 알아도 우리는 절반은 알게 된 것입니다. 🤓 그럼 이제 의문이 몇개 더 드실 겁니다.
그럼 외부의 두 레이어가 뭐 길래 가운데 레이어를 종속성으로 갖고 있느냐?
가운데 있는 녀석은 뭐길래 아무 종속성도 못 갖느냐?
그래서 뭐가 제일 중요하냐 ?
이제 빨간 동그라미를 보시면 됩니다. 빨간 동그라미는 아래와 같이 나타낼 수 있습니다. 

https://github.com/kudoleh/iOS-Clean-Architecture-MVVM

  우선 Domain Layer 입니다. 이녀석이 제일 중요합니다. 모든 종속성을 다 갖고 있기 때문입니다. 그래서 우리는 가운데에 있는 UseCases에 집중해서 이 프로젝트를 설계 해야합니다. 어떠한 프레임워크나, 도구 및 환경을 가져다가 해당하는 UseCases 입력에 넣어 실행 하였을때 같은 출력을 뱉어내는 UseCases를 설계 해야 한다고 합니다. (어떠한 프레임워크나 도구를 의존하지않기 때문이다! (종속성이 없기 때문에 가능하다!)) * 이 구조를 두고 스크리밍 아키텍쳐 라고 한댑니다.

  다음은 Presentation Layer 입니다. 이 레이어는 UIViewController 또는 SwiftUI View가 포함되어있습니다. 이 레이어에서 또 중요한것이 하나 더 있는데요. 바로 ViewModels(Presenters) 입니다.(눈썰미 좋으신 분들은 보셨을겁니다. 과녁 그림에서 초록 원안에 들어있는 그 presenters 맞습니다. 😁 ViewModel은 UI 관련 세부 사항이나 특정 프레임워크, 장치에 대한 구체적인 정보에 의존하지 않도록 설계 해야합니다. (코드에 담지 말아야합니다!)
  View Model은 뷰(View)를 조정하는 컴포넌트. 비즈니스 로직을 직접 실행하지 않고, Use Cases를 호출하여 필요한 데이터를 가져오거나 요청, 그리고 UI로직을 처리합니다.

  UI로직은 사용자가 보는 화면의 상태를 관리하고, 사용자와 상호작용을 처리하는 로직들 입니다. 예를 들어 로딩 인디케이터 표시 여부, 에러 메세지 출력, 버튼 활성화/비활성화 상태 데이터 포맷팅(날짜 형식 변환, 숫자 포팅, 문자열 연결), 상태 업데이트와 데이터 바인딩(UseCases호출에 따른 결과 상태 업데이트, View 구독 UI 업데이트)등. 이러한 로직들은 View Model에서 처리합니다.

  비즈니스 로직은 핵심 규칙, 절차, 또는 기능적 요구사항을 처리합니다. 예를 들어, 사용자 인증, 데이터 로드, 필터링 및 정렬등을 의미 합니다. 이들은 Use Cases에 정의 되어야합니다. 

  저는 그동안 VM에 데이터 비즈니스 로직들(CRUD)을 전부 담거나 Domain Layer를 거치지 않고 Data Layer를 바로 부르는 식으로 사용했었습니다. 클린 레이어 아키텍쳐와는 좀 거리가 멀었죠. 🥹
UseCase를 통해서 VM을 사용해보니, 확실히 비즈니스 로직의 분산을 막고, 테스트 코드 작성을 용이 하게 할 수 있을 듯 합니다. 하지만 역시 우려한대로 프로젝트가 굉장히 커지는 것을 볼 수 있었습니다. 🥹 * 100

글이 잠시 샜는데, 다음은 Data Layer 입니다. 이 레이어는 API(Network), Persistence DB, 그리고 Repositories Implementations를 포함합니다. 단어들만 딱 보시고 예상 하셨다시피 이 레이어에는 두 가지 주요한 데이터 소스가 있습니다.
- 원격(Remote, 예: 서버 API, 네트워크를 통한 데이터) 데이터 소스
- 로컬(Local, 예: 로컬 데이터베이스, 기기 자체의 데이터) 데이터 소스

  그 다음은 Repository Implementations입니다. 얘는 데이터 소스들과 도메인 레이어 사이에서 중개자 역할을 합니다.
레포지토리는 Domain Layer에서 정의한 인터페이스(프로토콜)를 구현하여, 다양한 데이터 소스에서 데이터를 가져오거나 저장하는 작업을 수행합니다.  예를 들어, 사진을 불러오는 PhotoRepository 인터페이스가 있다면, 이 인터페이스를 구현하는 PhotoDataSource 클래스가 API에서 사진을 가져올지, 아니면 로컬 데이터베이스에서 가져올지 결정하고 조정합니다. 이걸 파일 구조로는 network인지 db인지 구분하여 작성합니다. 

  또한 한가지 더 데이터 매핑을 하게 되는데, 얘는 뭐냐면 네트워크로부터 받은 JSON 데이터를 Domain Model에 맞게 변환하는 작업을 합니다 (Decodable 등을 사용). 내부 데이터를 가져오게 된다면 예를 들어 사진 같은 경우, 사진의 메타 데이터를 가져와서 매핑하는 작업을 맡게 되겠죠 ?

  하단 흐름도를 보면서 쉽게 설명을 하자면, Data Layer는 데이터의 출처(서버 또는 로컬)와 상관없이 데이터를 관리하고 제공하는 역할을 합니다. Data Repository는 데이터를 어디서 가져오든 상관없이, 항상 동일한 출력으로 Domain Use Case에 제공하도록 설계되어 있습니다. View Model이 Use Case에게 데이터를 요청할 때, Use Cases는 (Repository Interface. Dependency Inversion)을 통하여 Data Repository를 호출하고, Repository는 필요한 데이터를 가져와 (mapping을 거치던 안거치던) Domain UseCase로 전달합니다. 이렇게 하면 Domain Layer와 Presentation Layer는 데이터의 출처나 형식에 대해 걱정하지 않고, 오직 비즈니스 로직에만 집중할 수 있습니다. 이렇게 비즈니스 로직의 출력이 View Model의 데이터 응답으로 이루어지고 ViewModel은 UI로직에만 집중할 수 있습니다. 

https://github.com/kudoleh/iOS-Clean-Architecture-MVVM

 

레이어를 요약하자면 아래와 같습니다.

  • Domain Layer = App Business model (Entities + Use Cases + Repositories Interfaces)
  • Data Repositories Layer = Data Manufacturing (Repositories Implementations + API (Network) + Persistence DB)
  • Presentation Layer (MVVM) = UI Business Model (ViewModels + Views)

 


 

 아키텍처를 설계하면서 고민한 기술적 생각들과 느낀점

  간단해 보이지만 실제로는 복잡한 클린 아키텍처(Clean Architecture)에 대해 설명해 보았습니다. 클린 아키텍처를 적용하다 보면, 처음에는 비효율적인 상황에 직면할 수 있습니다. 예를 들어, ViewModel을 설명할 때 잠깐 언급했지만, 실제로 코드를 작성하다 보면 ViewController에서 Data Repository에 이르기까지 많은 파일을 생성해야 합니다.

  저는 이번 프로젝트에서 MVVM-C 아키텍처 패턴을 사용하여 Coordinator까지 적용했습니다. 하나의 ViewController를 만들 때마다 Coordinator, ViewModel, UseCaseProtocol, UseCase, RepositoryProtocol, Repository, Repository DataSource, DataSource Model 등 많은 구성 요소를 생성해야 했습니다. 나열만 해도 숨이 막히는 작업이었죠. 😔

  이렇게 많은 파일과 클래스를 생성하고, 각기 다른 레이어를 거치며 뷰를 만들어내는 과정에서 "이렇게까지 번거롭게 해야 할까?", "이 구조가 과연 유지보수에 유리할까?"라는 생각이 계속 들었습니다. 디버깅할 때에도 여러 단계를 타고 들어가야 하는 것이 번거롭게 느껴졌습니다. 하지만 클린 아키텍처를 적용하고 나니, 코드에 작은 수정 사항이 생겨도 그 수정이 미치는 영향을 한정된 레이어에서만 검토할 수 있다는 점이 큰 장점으로 다가왔습니다. 한 레이어의 코드에 문제가 생기면 그 레이어만 수정하면 되고, 다른 레이어는 건드릴 필요가 없다는 점이 정말 매력적이었습니다.

  이번 프로젝트를 진행하면서 Modular Architecture를 도입했을 때와 비슷한 고민에 빠졌습니다. 그때도 오버엔지니어링이라는 생각이 들며 "이게 맞나?"라는 의심이 계속되었지만, 어떻게든 밀고 나갔던 기억이 납니다. 프로젝트의 특성에 맞는 적절한 수준의 아키텍처를 도입하는 것이 중요하다는 점을 다시금 깨닫게 되었습니다. 😊

  이번에 클린 레이어드 아키텍처(Clean Layered Architecture)를 학습하고 이를 코드에 적용하면서 구조에 대해 깊이 고민할 수 있는 시간을 가졌습니다. 이러한 고민을 통해 생각하는 힘이 길러졌고, 저만의 소신이 생긴 것 같아 개인 프로젝트에서 클린 아키텍처를 도입한 것이 잘한 선택이라고 느꼈습니다. 학습하는 동안 정말 재미있었고, 구조를 적용하는 과정에서도 많은 배움이 있었습니다. 다만, 기존에 사용하던 ViewModel의 개념적 구조를 바꾸면서 신경을 많이 써야 했던 점은 다소 힘들었던 부분이었지만, 그 또한 좋은 경험이었습니다.

 


 

기회가 되면 개인프로젝트를 설명하는 블로그 글도 쓰도록 하겠습니다. 글 봐주셔서 감사합니다!

 

출처

 

Clean Coder Blog

The Clean Architecture 13 August 2012 Over the last several years we’ve seen a whole range of ideas regarding the architecture of systems. These include: Though these architectures all vary somewhat in their details, they are very similar. They all have

blog.cleancoder.com

 

 

GitHub - kudoleh/iOS-Clean-Architecture-MVVM: Template iOS app using Clean Architecture and MVVM. Includes DIContainer, FlowCoor

Template iOS app using Clean Architecture and MVVM. Includes DIContainer, FlowCoordinator, DTO, Response Caching and one of the views in SwiftUI - GitHub - kudoleh/iOS-Clean-Architecture-MVVM: Tem...

github.com