티스토리 뷰

반응형

 

이전 글: 프로젝트를 시작하며

 

구조 설계하기

앞선 글에서 언급한 것처럼, 프로젝트를 MVVM 구조로 설계를 진행했어요.  

 

MVVM은 무엇이 다른가?

설계에 앞서 구조에 대해 MVVM의 이론에 대해 학습을 진행했어요(이 글에서는 VIP나 MVVM에 대해 자세히 다루지는 않고 느낀 점을 위주로 작성할 계획이에요. 추후 MVVM에 대해 글을 작성하려고 해요).

 

MVVM에 관한 글들을 읽어보고, 기존에 제가 알던 VIP(Clean Swift)와 비교하며 이해를 하려고 했어요.

 

결국 차이는 "어떤 객체에게 어떤 권한과 책임을 부여하는가"에서 발생한다고 생각하고, MVC나 VIP와 크게 다르지 않다고 판단했어요. 다만, 읽어보는 글마다 조금씩 설명된 MVVM의 구조가 달랐기 때문에 이해하기가 어려웠어요.

 

용어 또한 같은 역할을 하는 객체도 글마다 다르게 명명하는 경우가 많아서 더 헷갈렸어요.

 

그래서 코드를 통해 패턴을 이해하기 위해 GitHub에서 여러 MVVM 코드를 읽어봤어요.

 

 

MVVM으로 설계하기

출처: 위키백과

 

MVVM의 기본 흐름은 아래와 같이 설명할 수 있어요

 

Entity ->  Repository -> Model -> Service -> ViewModel -> View(ViewController)

 

또 아래와 같이 정의하기도 해요

 

Entity -> Usecase -> Data API -> Interactor(Repository) -> View Model -> View(ViewController)

 

위의 요소를 모두 쓴 프로젝트도, 그중 일부만 사용한 프로젝트도 있었어요.

 

결국 중요한 것은 용어가 아니라 ViewController는 View의 역할에 집중할 수 있게 ViewModel에서 비즈니스 로직을 처리하게 하는 개념이라고 생각해요.

View가 ViewModel을 참조하고, 역방향으로는 참조하지 않으며 ViewModel이 Model을 관찰하는 구조로 처리하는 것이 여러 프로젝트의 공통점이었어요.

 

제가 만들어야 하는 화면은 아래와 같아요.

 

1. 검색하는 화면
2. 검색한 결과물을 자세히 보여주는 화면

 

우선 검색하는 화면을 기준으로 생각해볼게요.

 

1. ViewController

우선, ViewController는 Title을 설정하는 부분과 SearchBar UI를 가져야 해요. 또한 Cell들을 보여줘야 하는데 이 부분은 ViewController에 직접 구현하는 게 아니라 SearchBar, Cell 객체들을 따로 생성해 구현할 예정이에요. 또한 이 Cell을 담을 수 있는 TableView를 ViewController가 소유해야 해요.

 

그리고 비즈니스 로직을 처리할 ViewModel 역시 가져야 해요.

 

당연히 TableView가 있기 때문에 TableViewDataSource, TableViewDelegate도 채택해야 해요.

 

🤔 고민한 점

VIP로 구현할 때 DI를 위해 프로토콜로 구현된 부분들에 객체를 주입하는 것을 모두 ViewController에서 처리했었어요. 

func configure() {
    self.interactor = FooInteractor()
    self.interactor.presenter = BarPresenter()
    ...
}

저 역시 이 프로젝트에 DI를 위해 프로토콜을 사용하고, 그렇기 때문에 객체 생성을 ViewController에서 진행하도록 설계했어요. 그런데 다른 예시들은 보통 DI를 적용하지 않고 있어서 이 부분이 MVVM에 맞지 않는 것인지, 또 이것을 ViewController에서 처리하도록 하는 게 맞는 건지 고민이 되었어요.

 

2. ViewModel

ViewModel에서 데이터를 갖고 있게 설계했어요. 모델 객체를 따로 둘까 했는데 굳이 그럴 필요는 없다고 판단했어요. 속했던 팀에서 VIP를 사용했었을 때에도 Interactor에서 InteractorLogic과 함께 DataSource 프로토콜을 채택하게 해서 정보를 소유하게 한 경우가 있었어요. 또한 다른 프로젝트들에서도 그렇게 사용하는 경우가 있어 모델 배열을 갖게 하려고 해요.

 

그리고 그 배열은 private이어야 좋다고 판단되어서 외부에서 접근 가능하게 만드는 메서드가 필요하고, 가장 중요하게는 repository를 통해 데이터를 fetch 하는 기능이 필요해요. 

 

그래서 DI를 위해 프로토콜로 정의된 Repository 변수를 가져야 해요.

 

 

3. Repository / Model

Entity와 Model을 구분하고, Service와 Repository를 구분할까 고민했지만 이 프로젝트는 비교적 간단하기 때문에 굳이 그럴 필요가 없다고 판단했어요. Entity의 정보를 앱에서 활용하기 위해 많은 로직이 필요한 경우는 그 구별이 필요하다고 생각하지만 이 앱에서는 거의 주어진 내용을 그대로 가져오기 때문에 Repository에서 처리하는 것으로 설계했어요.

 

Repository에서는 네트워킹을 담당해줄 네트워크 매니저 객체를 통해 원하는 데이터를 가져오게 해요.

 

Model은 API에서 그대로 받아올 Decodable 구조인 Response 객체와 앱에서 사용하기 좋은 형태의 데이터인 가공된 Model로 구현돼요. 이 가공 역시 단순하기 때문에 Repository에서 진행해요.

 

옵셔널 바인딩 등 추가적인 로직으로 Repository를 무겁게 하지 않기 위해 모델을 옵셔널 타입으로 설계했어요. 더 자세한 것은 Repository PR에서 설명하도록 할게요.

 

 

4. Network Manager

네트워킹을 담당할 객체예요. 고민하는 부분은 테스팅을 위해서는 Mock 객체를 구현해야 하고, 의존성을 주입하는 구조로 구현해야 하는데 보다 나은 테스트를 위해서 Moya를 적용할지 고민이에요. 현재로서는 우선 Alamofire를 사용해본 적이 없기 때문에 Alamofire로 진행해볼 계획이에요.

 

추가: 또한 Alamofire를 사용하기 위해 Request를 생성하는 Router 객체가 필요해요.

 

 

정리

현재 설계 단계는 전체적인 흐름 정도이고 보다 정확한 설계는 개별 객체를 구현할 때 진행할 계획이에요. 

반응형
댓글