본문 바로가기
개발/iOS

[Swift] MVVM 디자인 패턴과 RxSwift

by 마동쿠 2017. 10. 4.

프로젝트를 진행하면서 프로젝트가 커지고 그에따라 코드를 정리해야겠다는 생각에 디자인패턴들을 찾아보던중 MVVM 디자인 패턴에 대해 간단하게 정리되어있는 글이 있어서 번역하여 공유합니다. 의역이 많으므로 원문을 보는것을 추천합니다 ^_^


원문 : http://lukagabric.com/mvvm-design-pattern-and-rxswift/





MVVM은 주요 응용프로그램 구성 요소를 3개(View, Model, ViewModel)의 상호 연결된 범주(Component)로 구분하는 소프트웨어 개발의 디자인 패턴입니다.   Model은 데이터를, View는 유저 인터페이스를, 그리고 ViewModel은 뷰와 모델 사이의 주요 연결을 나타냅니다.



모델 (Model)


Model은 응용 프로그램의 데이터 계층을 나타냅니다. 도메인 엔티티 / 모델과 비즈니스 로직으로 구성됩니다. Model은 그저 엔티티 구조체나 데이터베이스 뿐만 아니라 Alamofire 나 Facebook SDK 등과 같은 서비스 / 구성요소 입니다. 


Model은 데이터에 대한 모든 연산을 수행합니다. 이는 데이터를 얻거나 저장하거나 지우거나 업데이트하는 방법을 알고 있습니다. 이 작업들은 ViewModel에 의해 시작됩니다. Model이 데이터에대한 이 작업들을 마치면 ViewModel에게 결과를 알립니다.


Model은 ViewModel이 소유하고 있으며 View나 ViewModel에 대해 전혀 알지 못합니다.



뷰 (View)


View는 응용 프로그램의 시각적 표현이며 논리적인것을 포함하지 않습니다. 이것은 사용자 상호 반응 이벤트를 수신하고 데이터를 표시하는 유저 인터페이스로 구성됩니다. 유저 상호 반응 이벤트들은 ViewModel에 전달되어 처리해야할 책임이 있습니다. View는 ViewModel의 변경사항을 감지하고 ViewModel이 보여줄 준비가 된 데이터를 보여줍니다.


View와 Model사이엔 직접적인 연결이 없으며, 이 사이는 View가 소유한 ViewModel에 의해 연결됩니다.


유저 인터페이스들은 UIButton, UILabel, UICollectionView 등과 같이 UIKit에서 찾을 수 있는 구성요소들을 사용하는 xib나 storyboard들에 정의됩니다. ViewController는 유저 인터페이스가 정의되어 있는 xib나 storyboard에 직접 연결됩니다. 이것은 xib나 storyboard에 정의된 요소의 콘센트(outlet)가 포함되며 UI를 구성하는 추가 구현이 포합됩니다. 또한 뷰의 이벤트 라이프사이클을 관리합니다. MVVM 디자인 패턴에서 ViewController는 View의 일부로 간주됩니다.



뷰모델 (ViewModel)


ViewModel은 응용 프로그램의 논리(logic)을 나타냅니다. ViewModel은 View로 부터의 사용자 상호 작용(user interation)을 처리하고 Model을 업데이트합니다. 모델 업데이트 후 ViewModel에 결과를 알리고 View에 표시할 데이터를 준비합니다. 간단한 예제에서 ViewModel은 날짜를 포함하는 Model에게 일부 데이터를 요청할 수 있습니다. NSDate 값은 View에 노출되지 않는 대신 NSDateFormatter을 이용하여 문자열로 표한하기 위해 형식을 지정하고 준비합니다. View는 Model 데이터를 자체작으로 절대 조작하지 않으며, 단순히 ViewModel이 준비한 presentation을 보여줍니다.



MVC와 유사함


MVC 디자인 패턴에서, VIewController는 Model과 View 사이는 접착제(glue) 입니다. ViewController는 View를 소유하고, View 계층 구조를 관리하며, 사용자 상호 작용에 반응합니다. 또한 로딩, 표시, 사라짐 등과 같은 View 라이프 사이클 이벤트를 담당합니다. ViewController의 또 담당하는 것은 Model을 업데이트하며, Model 데이터를 표현하는 프레젠테이션 로직을 포함합니다. 이 모든 책임 때문에 ViewController는 매우 커지게 되고 이것이 MVC 디자인 패턴이 대규모 뷰 컨트롤러(Massive View Controller)라고도 불리는 이유입니다.


대규모 뷰 컨트롤러(Massive View Controller)에는 몇가지 이슈들이 있습니다. ViewController가 처리해야 하는 모든 책임들로 코드로는 추론하기가 훨씬 더 어렵습니다. 그것은 분명 단일 책임 원칙을 어기는 것입니다. ViewController에 포함된 어플리케이션 로직을 테스트하는것은 매우 어렵습니다. ViewController는 View와 매우 밀접하게 관련이 있고 어플리케이션 논리(logic)을 포함하는 이벤트 라이프사이클은 격리하거나 테스트하기 매우 어렵습니다. 뷰의 라이프사이클 이벤트를 조롱(mocking)*(오타인듯..?)하는데 많은 노력을 기울여야 합니다.


MVVM 디자인 패턴은 MVC 디자인 패턴과 굉장히 유사합니다. 이전에 말했듯이 MVC 디자인 패턴에서 ViewController 는 View와 어플리케이션 로직을 모두 포함합니다. ViewController에서 어플리케이션 로직을 분리하고 컴포지션 패턴을 이용하여 클래스를 자체 클래스로 분해하면, View만 포함된 ViewController와 ViewController가 가지고 있던 어플리케이션 로직이 포함된 또 다른 클래스가 남게됩니다. 그 클래스가 ViewModel이며, 이것은 ViewController와 1:1 대응합니다. 그래서 MVVM 디자인 패턴은 기본적으로 ViewController에서 컴포지션 패턴을 이용하여 어플리케이션 로직을 분리하고 ViewModel에 넣은 MVC디자인 패턴입니다.



ViewModel 에서 View로 데이터 바인딩


데이터를 View로 바인딩 하는데 사용할 수 있는 많은 메커니즘이 있습니다.(혹은, 데이터를 바인딩하여 볼 수있는 많은 메커니즘이 있습니다.) 예를들어, Swift는 속성 관찰자를 지원합니다. View는 ViewModel에 업데이트 클로저를 제공하여 관찰된 속성의 변경사항을 View에 알리는데 사용할 수 있습니다. 변경사항을 감지하는 것 이외에도, 어플리케이션은 일반적으로 비동기작업(네트워크 요청과 같은)이나 스트림(이벤트나 알림같은)을 처리해야합니다. 이것은 FRP 프레임워크를 위한 완벽한 일입니다. 현재 이용가능한 많은 FRP 프레임워크가 있지만, 가장 인기있는 두가지는 RxSwift와 ReactiveCocoa입니다. 내가 RxSwift vs ReactiveCocoa게시물에 쓴 것처럼, 당신이 잘못 될 수 없겠지만 RxSwift를 선호합니다.



RxSwift


명령형 패러다임은 단계별로 실행되는 명시적 명령을 기반으로 합니다. 프로그램이 어떻게 작동하는지 정확하게 설명합니다. 값은 상태로 지정되고 저장됩니다. 실행은 단계별로 수행하므로 나중에 값이 변한다고 하더라도 변경사항이 전파되진 않습니다.

RxSwift를 사용하면 FRP(Functional Reaction Programming) 패러다임을 사용하여 코드를 작성할 수 있습니다. 구성, 변환 및 관찰할 수 있는 이벤트 또는 데이터스트림을 쉽게 작성하여 관측 값을 기반으로 일부 조치를 수행할 수 있습니다.


앞에서 말했듯, MVVM 디자인 패턴에서 View는 ViewModel에서 준비한 모델 데이터를 표시합니다. RxSwift는 이러한 값을 관찰하고 View에 바인딩하는 간단하고깔끔한 메커니즘을 제공합니다.



Observable


Observable은 RxSwift의 주요 빌딩 블록입니다. 비동기적으로 요소를 수신할 수 있는 시퀸스입니다. 이러한 시퀸스는 0개 이상의 요소를 가질 수 있습니다. 발생할 수 있는 이벤트 유형은 다음과 같습니다. - Next(다음), Error(오류) 그리고 Completed(완료)


Observable의 subscribe 메서드를 사용하여 이러한 이벤트를 관찰할 수 있습니다. 그렇게하면 이벤트의 각 경우를 처리할 수 있습니다. 다음에는 관찰된 요소 값을 사용할 수 있습니다. Observable 시퀸스가 성공적으로 완료되면 완료시에 호출됩니다. 관찰가능한 순서를 완료하지 못하면 오류가 발생합니다. 완료되거나 오류 이벤트가 관찰되면 관찰 가능한 시퀸스는 다른 요소를 생성할 수 없습니다.


Hot and cold observable (의역이 많습니다. 원문을 볼것을 권장합니다.)


Observable들은 옵서버가 구독하지 않아도 요소를 생성하면 뜨겁다는것으로 간주됩니다. 옵서버가 없는 경우에도 화재가 발생하는 알림센터를 생각해보십시오. 그런 종류의 Observable 시퀸스를 구독하면 이전 알림을 놓친것 일 수도 있습니다.


Observable들은 옵서버가 구독한 후에만 요소를 생성하면 차가운 것으로 간주됩니다. 리소스는 옵서버 구독당 할당되는 경우가 많습니다. (예: 하나의 네트워크 요층을 구독할 때마다 해고됩니다.) 하지만 공유할 수 있습니다 (해당 Observable의 모든 구독자에 대해 하나의 네트워크 요청만 실행)



Driver


Driver는 RxCocoa의 일부인 값 유형 구성요소입니다. Observable 시퀸스를 래핑하고 실제로 UI에서보다 쉽게 값을 바인딩 할 수 있는 편의를 제공합니다. Observable들은 오류가 발생할 수 있으며, 발생하면 쉽게 표시할 수 있는 편리함이 필요합니다. 모든 관측 가능한 객체는 asDriver 메소드를 사요하여 Driver로 변환할 수 있습니다. 여기서 기본 관찰 가능 오류가 발생하면 반환할 대상을 제공합니다.

네트워크 요청과 같은 비동기 작업을 수행하고 UI결과 수를 표시하려는 경우를 생각해 봅시다. 반환된 항목의 수를 문자열로 맵핑해야합니다. 문자열은 "X item(s)"과 같은 형식일 수 있습니다. Driver는 또한 우리가 UI를 주도하고 있기 때문에 메인 스케줄러에서 구독이 발생하도록 합니다.

이제 데이터를 label에 쉽게 바인딩할 수 있습니다.



DisposeBag


시퀸스가 종료되고, dispose를 호출하지 않으면 영구 리소스 누수가 발생하지 않지만, 시퀸스가 완료되거나 오류가 발생할 때까지 해당 리소스가 사용됩니다. 시퀸스가 완료되지 않거나 오류가 발생하지 않으면 영구적으로 할당됩니다. 따라서 DisposeBag을 멤버변수로 사용하고 모든 disposables를 멤버변수에 추가하는 것이 편리합니다. 이렇게하면 개체가 할당 해제되면 모든 리소스가 삭제되고 정리됩니다.



Variable


Variable(변수)는 관찰할 수 있는 상태를 나타냅니다. 그것들은 stateful과 functional 패러다임의 다리역할을 합니다. 변수는 항상 생성자에 제공되는 초기값을 포함하며 구독할때 마다 즉시 현제값이 해당 구독으로 전송됩니다. (이전값은 전송되지 않고 최신, 현재값만 전송됩니다.) 또한 노출값 속성을 통해 변수값을 가져오거나 설정할 수 있습니다. 기본 관측 가능 객체는 asObservable() 메소드를 사용하여 엑세스 할 수 있습니다. 변수는 결코 오류를 일으킬 수 없습니다.


MVVM 디자인 패턴과 RxSwift를 사용하여 작성됭 예제 애플리케이션을 만들어 보았습니다. 날씨 데이터를 가져와 표시하는 간단한 응용 프로그램입니다. 로딩(네트워크 요청이 수행되는 동안), 표시된 데이터 (요청이 성공적으로 완료되면 날씨 데이터가 표시됨) 및 오류 (네트워크 요청이 실패한 경우)의 세가지 상태가 있습니다. 오류가 발생하면 두가지 변형이 있습니다. 하나는 오류를 표시하는 것이고, 다른 하나는 이전에 가져온 데이터를 사용할 수 있는 경우 표시하는 것입니다. 그렇지 않으면 오류가 표시됩니다. 두 변화는 반응 패러다임과 명령 패러다임을 사용하여 구현됩니다. 따라서 단순한 오류를 반드시 지켜야 하고, 반응적이며, 기존의 데이터 명령형 및 사후 대응 방식으로 대체해야 합니다.


이 예제에서는 ViewModel만 변경되었습니다. 복잡성을 약간 증가시키면 (예전에 오류에 대한 데이터를 보여줌으로써) 변화를 모델링 하기 위해 반응 패러다임을 더 많이 변경해야 하는 것을 볼 수 있습니다. 선언적 프로그래밍이고 체인을 쉽게 따라갈 수 있기 때문에 표현력이 더 커질것입니다. 명령형 패러다임을 사용하여 동일하게 변경하는것은 추가 상태를 추가하는것을 의미합니다.


If you liked this blog post I'd appreciate if you followed me on Twitter



댓글