본문 바로가기
아키텍처

아키텍처 공부 kudoleh/iOS-Clean-Architecture-MVVM

by vapor3965 2024. 3. 28.

목차

     

     

     

    클린아키텍처 책을 읽고난후 관련 아키텍처를 공부하려고 한다. 

    공유의 시대이므로 깃헙에 많은 아키텍처 샘플 코드가 많았다. 

    그중 iOS-Clean-Architecture-MVVM  라는 샘플코드를 보면서 공부해보려고 한다. 

     

    깃헙에 Clean-Architecture ios 라고 치면 상단에 나오는 레포지토리다. 

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

     

    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

     

    이 레포가 좋은게, 분석해놓은 글을 미디엄에 올려놓아서 공부하기 안성맞춤이였다. 

    - https://tech.olx.com/clean-architecture-and-mvvm-on-ios-c9d167d9f5b3

     

    Clean Architecture and MVVM on iOS

    When we develop software it is important to not only use design patterns, but also architectural patterns. There are many different…

    tech.olx.com

     

     

     

    분석

    도메인,유스케이스 layer

    • 써드파티, 프레임워크 의존성이 없어야한다. 심지어 UIKit도.
    • 도메인,유스케이스 구조만 보고도 알수있는 장점. 
    • 유스케이스 생성방법으로는...
      • 유스케이스 & Interactor가 있고,
      • 또다른 방법으로는, UseCase라는 프로토콜이 있고, start라는 메소드를 포함한다. Interactor를 통해서 유스케이스를 생성한다 ? 유스케이스는 유스케이스를 사용할수있다.

     

     

    Presentation layer

    ViewModel

    여기도 UIKit 관련 프레임워크 import하지않는다.
    왜냐, ViewModel만으로도 UIKit으로 구현할건지, SwiftUI로 구현할건지 상관없기때문.
    뷰모델은 뷰의 정보를 전혀참조하지않고, View,ViewController에서는 ViewModel을 의존하고, 옵저빙하여 갱신한다.

    View, ViewController

    뷰에서는 비즈니스로직을 포함하지않는다. 당연히 유스케이스도 접근하지않는다.

    • 그러한 로직들은 뷰모델만 알고있도록 한다.

    뷰모델만 참조하도록 하고, 뷰의 액션들은 뷰모델에 전달하고, 여기서 화면전환이 필요하다면, 뷰모델에서 coordinator로 전달한다. 

     

    Coordinator

    Coordinator를 통해서 뷰컨,뷰전환등을 돕는다.
    뷰컨에서 이루어지는 전환로직등을 Coordinator가 담당함으로써 뷰컨트롤러의 의존성을 줄일수있다.

    ViewModel에서 Movie 데이터 가져오는 MovieUseCase를 참조하는데,

    리스트화면에서, 상세영화화면으로 진입하려면 ViewModel의 데이터가 필요한데, 화면전환은 Coordinator에 맡기기위해서, closure를 액션으로 받도록 함.. (대박이넹..)
    뷰는 진짜 단순히 행위에 대한 역할만 하네, 그 이상 그이하도아닌....

    • 장점은, 뷰컨트롤러의 교체가 쉽다.

     

    Data Layer

    DTO

    Data Transfer Object 의 약자로, 각 계층의 일시적인 데이터모델로써, 도메인으로 전달하기 위한 모델로 사용한다.

    • 클린아키텍처의 룰을 잘 적용했다. db의 인터페이스를 도메인까지 끌어올리지말라는 규칙
      db, API네트웍 모델을 도메인까지 전달하기Repository

    API 네트워크, DB를 아우름.

    • 그래서 API도 레포지토리, 레포지토리로 부르는 이유가 그거군..

    DI Layer 

    의존성 주입을 관리하는 객체다.
    뷰모델, 뷰컨트롤러, 유스케이스의 의존성들을 주입하는 객체.

    Coordinator는 뷰전환역할을 돕는데, 전환을하려면 뷰,뷰컨트롤러,뷰모델이 필요하다.
    이것들은 Protocol타입이며, 의존성 주입이 필요하다.
    이역할을 DIContainer에게 맡기고, 전달된 객체를 Coordinator에서 관리할뿐이다.

    🙋 의존성 주입의 기준은 어떤건가 .. ? 자잘한 뷰는 아닌것같고.

     

     

     

     

     

    느낀점

    클린아키텍처를 읽고서 궁금했던부분들이 어느정도 해소되는 느낌이다.

    마지막쯤에는 각 컴포넌트들을 따로 프레임워크화한 프로젝트도 있다. (대단..)

     

    해당 분석글에도 이 아키텍처는 좋은방법이 될수있지만, 항상 맞다는건 아니라고 한다. 자신의 프로젝트에 알맞는 아키텍처를 고르는게 중요하다고 한다. 

     

    핵심 컴포넌트들을 의존성 다이어그램으로 표현해봤다.

    (열린화살표는 의존성(참조), 닫힌 화살표는 인터페이스를 구현했다는 뜻)

     

     

    유스케이스는 컴포넌트인가 ?

    맞다고 할수있겠다. 유스케이스는 애플리케이션과는 독립적인 단위다. 즉, 어느 어느플랫폼이든 적용되는 규칙이며 컴포넌트라고 볼수있다. 유스케이스는 프레임워크와는 관련이없는 독립적이여야한다.

    Repository 인터페이스 의문?

    해당 예제에서는 Repository도 유스케이스에 포함시켰다.
    유스케이스가 "영화목록 가져오기" 라고되어있으니, 어떤식으로든 영화목록을 가져아야할텐데, 세부사항은 네트워킹일수도있고, db일수도 있겠다.
    다만 Repository라는 인터페이스를 유스케이스에 포함시키는게 맞는지 의문이었는데,
    인터페이스니, 어느 애플리케이션이든 사용할수는있겠다.
    Repository가 다양한 구현체로 사용될수있으니!

    엔티티의 데이터는 독립적이다.

    유스케이스와 마찬가지로, 엔티티의 데이터도 애플리케이션과는 독립적인 단위다.
    엔티티의 데이터가 앱 공용으로 사용되는 핵심데이터이므로, db에도 저장되는 데이터라면 db에서도 그대로 사용하거나, 뷰에서도 그대로 사용될수도 있을텐데, 이를 분리한다.
    DTO라는 Data Transfer Object 개념으로, 계층마다 사용할수있는 데이터로 변환하여 사용한다.

    뷰모델은 뷰와 독립적이다.

    뷰를 위한 모델, 뷰컨트롤러의 역할을 줄이기위한 역할로써, 더 나아가, 뷰모델만으로도 뷰를 교체가능하게끔 할수있다. 그러기위해서는 뷰모델에는 뷰와 관련된 내용이 전혀없어야한다. (import UIView)
    SwiftUI로 구현할지, UIKit으로 구현할지는 세부사항이므로, 뷰모델만있으면 상관없다.

    Coordinator

    보통 뷰전환이 트리거가 필요한데, 보통 뷰의 액션으로 발생이되어지는데,
    이러한 흐름을 Coordinator에 맡기는 코드를 봤을때, 신선하고 좋았다.
    뷰모델까지 더해지니, 정말 뷰는 그리는 용도이외는 없어진게 보였다.
    Coordinator의 뷰전환액션을 ViewModel의 액션 클로져로 받는것도 신선했다.

    ViewModel 프로토콜

    input, output 프로토콜로 각각 따로 만듬으로써, 이들을 채택하는 ViewModel프로토콜 만든것도 좋았다.
    프로토콜만 보고서 어떤 인풋이 들어오는지, 또, 뷰에게는 어떤 아웃풋이 필요한지를 한눈에 볼수있어서 의도파악이 좋아보인다.

    DI Container (의존성주입 관리객체)

    클린아키텍쳐 읽으면서 의존성주입은 한곳에서 관리를 하는건가 ? 하는 의문이 들었는데, 이 예시에서는 DIContainer라는것을 만들어서 이것이 관리하도록 했다.
    세부사항에서 액션이 발생하여 새로운것을 보여줘야할때, View -> ViewModel -> Coordinator -> DIContainer 로 전달하는 식으로하여 의존성을 결국 DIContainer에서 받게했다.

    🧐 살짝 우려되는것은 앱의 비중이 커질때, DI Container의 코드가 굉장히 많아질것 같은데, 괜찮은가.. ?

    아래처럼 다 관리하는데... 흠, 오히려 한곳에서 관리하니까 유지보수 측면에서 괜찮으려나 ? 

    🧐 외부 업데이트는 어떻게 관리해야할까..?

    View, ViewModel, UseCase, Coordinator로 관리되고있는데, View는 ViewModel의 데이터에 의존적인데, 외부환경으로 ViewModel의 Data가 변경되어야한다면 View는 어떻게 갱신해야할까 ?
    고민해봤을때.. Coordinator는 현재 보여지고있는 화면(뷰)은 알고있으니, 변화를 Coordinator에 전달하고, Coordinator는 보여지고있는 뷰의 뷰모델에 update메소드를 호출하도록 하면 어떨까 싶다.

     

     

     

     

    공부할것.. 

    Flow Coordinator

    VIPER

    RIBs 

    댓글