Android 아키텍처 비교–MVP, MVVM, SVC–1

Bansook Nam
13 min readApr 19, 2019

1. Android에서 아키텍처 패턴은 왜 필요한가

앱을 만든다는 것은 사용자가 원하는 정보를 화면으로 잘 찾아보고, 잘 입력할 수 있게 하는 소프트웨어를 만드는 것이다. 앱을 만들 때 필요한 요소는 크게 2가지이다. 흔히 비즈니스 로직이라고 하는 “기획적인 요소”와 아이콘 이미지, 레이아웃, UI, UX를 포함한 “디자인적인 요소”가 그것이다.

앱을 만드는 순서는 4단계로 구성된다.

1) View를 배치하는 것

2) View에서 사용자 Action을 감지하는 것(터치, 클릭, 텍스트 입력, 스크롤, 스와이프 등)

3) Action에 따라 알맞은 동작을 수행하게 하는 것(데이터를 서버나 클라이언트에서 처리하는 등)

4) 동작의 결과를 다시 View에 반영하는 것

앱 하나의 페이지를 기준으로 각 항목은 복잡도에 따라서 1시간 안에 끝낼 수 있는가 하면, 1달이 걸릴 만큼 어려울 수도 있다. 앱의 복잡도에 따라서 아키텍처 패턴의 중요성이 달라진다.

단순한 앱이라면 아키텍처 패턴은 먼 나라 이야기로 들릴 수 있다. 그냥 XML 형식으로 View를 배치하고 Activity나 Fragment 안에서 모든 로직을 넣으면 그만이다. 코드가 총 200~300줄 정도라면 1년 차 개발자도 전체 흐름과 로직을 이해하는 데 문제가 없을 것이다.

그러나 시간이 흐르면, 사용자가 원하는 요구사항이 더 생기고, 원래의 스펙보다 복잡한 기획이 요구된다. 처음 200줄이었던 코드는 곧 1000줄을 넘어가는 시점이 온다. 프로젝트를 진행하다 보면 코드 작성자가 아닌 다른 개발자가 그 코드를 수정해야 하는 상황이 온다. 그 코드를 처음 보는 개발자는 1000줄이 넘는 코드를 해석하고 분석하는데 시간이 소요된다. 또 기존의 스펙이 변경됨에 따라 예상치 못한 단순 UI 버그부터 심도 있는 분석을 해야하는 시스템 버그들이 나타나기 시작한다. 문제가 예상되는 포인트가 어디인지 최대한 빨리 찾아낼 수 있으면 좋을 텐데, 1000줄이 되는 코드는 스크롤 20페이지가 되기 때문에(15인치 모니터 기준으로 한 화면에 50라인 정도) 수많은 메서드를 점프하고, 필드들의 존재를 확인하면서 위아래로 스크롤하다 보면 처음에 무엇을 찾으러 왔는지조차 잊어버리기 쉽다.

코드 분석이 어려워지는 지점에서 코드 분리의 필요성이 느껴지기 시작한다. 하나의 Activity, Fragment에서 구현하던 로직들과 필드를 각각 다른 클래스로 나누기 시작하면 그것이 바로 아키텍처의 시작이 된다. 좋은 아키텍처는 개발을 편리하게 하고, 코드 가독성을 좋게 하며, 유지보수를 쉽도록 한다.

이 글에서는 MVP, MVVM, SVC 3가지 아키텍처 패턴을 서로 비교한다. 각각의 특징과 장단점은 무엇인지 한번 알아보자.

2. 아키텍처 패턴의 종류

Android에서는 다양한 아키텍처 패턴들이 존재한다. UI개발에서 흔하게 말하는 MVC 패턴도 있고, MVI, 또 우버팀에서 만든 RIBS 아키텍처 등 정말 가지각색의 패턴들이 현존한다. 이 글에서는 Android에서 가장 잘 알려져 있는 MVP, MVVM와 네이버 GitHub 오픈소스인 SVC까지 총 3가지를 서로 비교해 보고 장단점을 분석한다.

MVP 패턴

1990년대 Taligent사(Apple, IBM, HP의 협력 벤처)에서 시작한 패턴으로 알려져 있는 MVP는 dolphin smalltalk, Microsoft .Net 등 다양한 플랫폼에서도 적용된 패턴이다. MVP의 약자는 각각 Model, View, Presenter이고, 구조는 다음과 같다.

그림 1 MVP의 구조

MVP에서 주목할 점은 View가 수동적(Passive)이라는 점이다. View는 스스로 데이터를 반영하지 않고 명령을 받는다. 명령을 하는 주체인 Presenter가 View에게 “Data를 보여줘- view.showData(data)” 하고 명령을 하는 형태인 것이다.
View는 클릭이나, 기타 사용자 Action이 발생 시 Presenter를 직접 호출한다. presenter.do()와 같은 형태로 비즈니스 로직이 바로 수행된다. Presenter는 Action과 Lifecycle에 따라서 작업을 수행한다. Model의 데이터를 변형하기도 하고, 필요한 작업을 수행한 뒤에, 결과를 다시 View에게 전달해서 사용자에게 결과 데이터를 보여준다.

Presenter는 Mediator(중재인)으로서 Model과 View 사이에서 필요한 작업들을 수행한다. Mediator의 다른 설명으로는 Supervising Controller(감독 제어기)라고 하기도 하는데, 중간에서 감시·감독한다는 의미로 해석할 수 있다.

MVVM 패턴

2005년 John Gossman이 제안한 패턴이다. MVVM 개념의 기초는 2004년 마틴 파울러가 제안한 PM(Presentation Model)를 기반으로 만들어졌다. PM은 View에 렌더링에 필요한 데이터를 가지고 있는 객체이고, View는 PM의 데이터를 기반으로 렌더링을 한다. PM에서 중요한 포인트는 PM이 들고 있는 데이터와 View는 항상 동기화(synchronization)가 되어야 한다는 것이다. John Gossman은 PM의 개념을 WPF와 Silverlight 플랫폼에 특화시켰고, PM을 ViewModel이라는 이름으로 명명하면서 MVVM이 탄생했다. 더 자세한 내용은 “Model-View-ViewModel 디자인 패턴을 사용한 WPF 응용 프로그램”을 참고한다.

John Gossman이 이 패턴에서 이루려고 한 목적이 2005년 블로그글 서두에 나온다. 전체 작업을 UI 로직 작업과 비즈니스 로직 작업으로 나눌 수 있다. 이렇게 나누어진 작업을 디자인에 특화된 UI 개발자는 UI쪽을 담당하고, 비즈니스 로직에 특화된 개발자는 나머지 부분을 담당할 수 있도록 하기 위해서이다. 이렇게 나누어서 협업을 하는 것은 특히 드림위버, 플래시와 같이 UI개발에 특화된 WYSIWYG 소프트웨어를 개발할때 도움이 된다.
MVVM은 Model, View, ViewModel의 약자이고, 구조는 다음과 같다.

그림 2 MVVM의 구조

MVVM은 MVP와는 반대로 View가 능동적(Active)이다. View는 스스로 ViewModel 객체의 어떤 데이터가 필요한지 직접 관찰한다. 관찰하기 위해서는 ViewModel에 있는 데이터가 관찰 가능한 형태여야 한다. Google에서 제공한 LiveData, ObservableField 객체를 쓰거나 Rx Java에서 제공해주는 Observable 객체도 사용 가능하다. 관찰 필드에 변화가 생기면 즉시 View는 알림을 받게 되고, 알맞은 View 렌더링 로직을 수행하게 된다.
View는 클릭이나 기타 사용자 Action이 발생하면 ViewModel을 직접 호출한다. Google에서 제안한 예제를 보면 2가지 형태로 호출하는데 ViewModel 내부의 메서드를 직접 호출하는 방법과, ViewModel 내부에 있는 CommandObservable 객체를 통해 호출하는 방법이다. 전자의 방법은 MVP와 동일하다. 이 방법을 쓰게되면 ViewModel이 특정 비즈니스 로직과 관련이 깊어질 수 있기 때문에 별도의 인터페이스나 클래스 객체를 만들어서 비즈니스 로직을 위임하는 것이 로직을 분리하는 측면에서 바람직하다. 후자의 방법은 ViewModel에서는 View에서 발생한 Action을 전달만 할 뿐 비즈니스 로직 처리는 해당 CommandObservable을 관찰하는 쪽에서 담당하기 때문에 비즈니스 로직을 좀 더 강제로 분리할 수 있다. 그러나 후자의 방법을 쓰면서 CommandObservable을 관찰하는 객체가 ViewModel 자신이라면 그것은 불필요한 관찰을 하는 것이라고 생각한다. CommandObservable 객체를 차라리 사용하지 않는게 코드 복잡도를 줄이는 길이다. CommandObservable을 쓴다면 ViewModel이 아닌 다른 비즈니스 로직 처리 객체가 관찰을 하도록 설계하는 것이 바람직하다.

MVVM으로 앱을 개발한다면 Databinding 라이브러리를 쓰는 것을 꼭 고려하는게 좋다. View의 디펜던시를 Activity, Fragment 의존에서 xml 의존으로 내릴 수 있다. 그러면 다음의 구조로 표현할 수 있다.

그림 3 MVVM — Databinding의 구조

구조는 “그림2”의 형태와 동일하지만 View는 실제 XML로만 존재를 한다. View에 코드로 접근할 일이 있다면 BindingAdapter와 InverseBindingAdapter를 통해서 접근하고 상태값을 통해 View를 갱신한다. ViewModel은 View에 표현할 데이터를 들고 있으면서 MVP의 Presenter와 같이 비즈니스 로직과 View 사이에서 Mediator 역할을 한다.

John Gossman이 이루려고 했던 UI 개발 영역과 비즈니스 영역 개발을 나누어서 협업을 하려면 ViewModel 내부에 observable 필드들을 정의하고, View의 Action을 받을 interface 또는 CommandObservable 객체를 미리 정의하면 된다. ViewModel에서 미리 정의된 값을 xml에서 observing하고 필요한 View의 Action을 호출하도록 만들면 분리된 영역 내에서 서로 부딪히지 않고 작업이 가능하다. UI 담당 개발자는 xml과 BindingAdapter, InverseBindingAdapter만 개발하면 되고, 비즈니스 로직 담당자는 나머지 부분을 개발하면 된다.

그렇다면 MVP에서는 영역 분리가 불가능한가? 실제로 완전히 불가능하다고 할 수는 없지만, 충돌 영역이 조금 생긴다. View는 Presenter를 호출해야하고 Presenter는 View를 호출해야 되기 때문에 작업 영역이 겹치고 여기서 충돌이 발생할 수 있다. 이 부분을 줄이기 위해서 Presenter와 View 인터페이스를 각각 미리 만들면 그 충돌을 줄일 수 있지만 View를 작성하는 입장에서는 사용자 Action을 전달하기 위해 Presenter를 사용해야하는데, Presenter에 public 함수가 40개가 되고, View Action과 관련된 함수가 3개밖에 없다면 40개중에 3개를 찾기 위해서 눈을 부릅떠야하는 불편함이 있다.

MVP의 구조적인 단점들이 있기 때문에 이러한 단점을 해결하기 위해 SVC 패턴을 만들었다.

SVC 패턴

SVC의 전체적인 구조는 MVP와 비슷하지만, UI 영역과 비즈니스 영역 사이의 연관성을 끊기 위해서 View는 Presenter를 직접적으로 알지 못하도록 설계되었다. SVC의 약자는 각각 Screen, Views, ControlTower(관제탑)이고 구조는 다음과 같다.

그림 4 SVC의 구조

MVP와 핵심적으로 다른 요소는 Views 객체에서 발생하는 이벤트가 ViewsAction 인터페이스를 통해서 전달되고, 이 전달을 통해 ControlTower 객체 함수가 호출 된다는 것이다. 그래서 호출하는 화살표도 점선으로 표시가 되어있다.

SVC에서는 Android의 특성을 고려하여 만든 Screen이라는 interface 객체가 추가되었다. 앱은 여러 가지의 화면 페이지로 구성된다. 한 페이지 전체를 덮는 Activity나, 그 위에 살짝 얹어지는 Dialog가 있고, 화면 영역의 일부를 담당하는 Fragment가 존재한다. 각 Screen 객체는 UI로 표현되는 Views와 ControlTower로 이루어져 있다.

Views 객체의 이름은 Android에서 말하는 View 객체들이 많이 모여있는 집합이라는 뜻에서 복수형으로 ‘s’가 붙었다. Views는 하나의 xml에서 인플레이트된 View들의 집합이고, xml안에 있는 View들을 콘트롤하고 각 View에서 발생하는 이벤트를 viewsAction 객체에게 알려준다(viewsAction.onClickSave()같은 형태로).

ControlTower 객체는 Screen에서 발생하는 모든 이벤트를 제어하는 관제탑이다. Screen의 Lifecycle 이벤트를 감지하고, Views에서 발생하는 이벤트를 viewsAction 인터페이스를 통해 감지한다. Screen에서 일어나는 모든 이벤트를 받고 이에 맞는 로직을 수행한다. 처리 결과를 Views에게 알려주는 방식은 MVP와 동일하다.

MVP와의 차이점 2가지이다. Activity, Fragment를 View가 아닌 Screen이라는 개념으로 뒀다는 것과, View에서 일어나는 사용자 이벤트는 직접적인 비즈니스 로직 호출이 아닌 ViewsAction 인터페이스를 통해 전달된다는 점이다.

SVC는 ViewModel과 함께 사용하는 것도 스펙에 따라서 고려해 보는 것도 좋다. ViewModel과 함께 SVC를 설계한다면 크게 2가지 type으로 설계할 수 있다. 다음은 첫 번째 type의 도식이다.

그림 5 SVC와 ViewModel type1 구조

기본적인 구조는 SVC의 구조를 가져간다. 그러나 ControlTower가 Views에게 렌더링 명령을 내리지 않는다. MVVM에서와 같이 Views에서 ViewModel에 필요한 Observable 데이터를 관찰을 해서 능동적으로 데이터를 View에 반영한다. MVVM과 다른 점은 ViewModel이 Views에 필요한 데이터 보관 객체로서의 역할만 한다는 점이다. Mediator의 역할은 ControlTower가 하기 때문에 필요한 데이터는 Model에서 가져오고, View에 갱신이 필요하다면 ViewModel의 데이터를 변경한다.

다음은 두 번째 타입의 도식이다.

그림 6 SVC와 ViewModel type2 구조

세로로 그려져 있는 도식을 가로로 놓고 보면 MVVM과 거의 흡사한 형태가 된 다. 여기서 ControlTower의 역할은 Views에서 발생하는 ViewsAction을 받아 UI로직과 비즈니스 로직이 섞이지 않게 중개만 해주는 역할을 담당한다. 앱에서 정보를 주로 제공해주는 화면이 많을때 두 번째 타입이 유용하다. Model을 통해 ViewModel에 필요한 데이터 갱신이 잦고, 각 ViewModel과 정보를 제공하는 DB repository가 서로 유형별로 비슷해서 짝지을 수 있는 경우라면 “그림 6”의 형태로 설계하는게 바람직하다.

본격적인 비교는 다음 글에서 이어집니다.
Android 아키텍처 비교–MVP, MVVM, SVC–2

--

--