[Android] MVVM Part.1
- LiveData
- ViewModel
- Databinding
선수 지식 필요.
Architecture 패턴에는 일반적으로 사용하는 MVC, MVP, MVVM이 있다. 이러한 Architecture 적용으로 얻는 이점은 안정적인 서비스 개발을 할 수 있으며 유지 보수를 빠르게 할 수 있다는 것이다.
먼저, MVP Architecture부터 살펴보자.
1. MVP
- Presenter : View에서 요청한 정보를 Model로부터 가공해서 View로 전달하는 부분
Model과 View는 MVC와 동일하지만 사용자 입력을 View에서 받는다. 그리고 Model과 View는 각각 Presenter와 상호 작용을 하게 된다. 항상 Presenter를 거쳐 동작하는 셈이다.
그러므로 View와 Model은 서로를 알 필요가 전혀 없다. Presenter만 알면 된다. 그래서 MVC의 단점인 View와 Model의 의존성이 없어지게 된다.
정리하자면
- View로 사용자의 입력이 들어온다.
- View는 Presenter에 작업 요청을 한다.
- Presenter에서 필요한 데이터를 Model에 요청한다.
- Model은 Presenter에 필요한 데이터를 응답한다.
- Presenter는 View에 데이터를 응답한다.
- View는 Presenter로부터 받은 데이터로 화면에 보여주게 된다.
View에서 비즈니스 로직을 분리하는 부분은 성공적이다. Model 역시 분할되어 있어서 적용하기 쉽다. 하지만, View와 Presenter의 1:1 관계를 유지하는 덕분에 몇 가지 단점이 생긴다.
- 중복 코드 발생한다.
- View에 대한 의존성이 강해진다.
중복 코드?
간단한 예를 들어보겠다.
로그인과 로그아웃은 어떠한 화면에서도 호출될 수 있다. 기획상으로 가능하다면 말이다.
A/B/C/D 4개의 화면이 있고, 언제든 로그인만 호출할 수 있다. 대부분은 로그인 Acitivity로 이동시키고 이를 구현하면 된다.
반대로 로그아웃이다. 단순하게 로그아웃한다고 생각해보자. 로그아웃 역시 A/B/C/D 화면에서 언제든 접근할 수 있다.
MVP에서는?
로그인, 로그아웃을 처리하는 비즈니스 로직은 A/B/C/D 화면에 종속적인 Presenter 4개에서 모두 호출하게 된다. 모델에서 실제 로그인, 로그아웃 처리 로직이 있는 부분은 가져다가 사용하는 것이다.
여기서 공통 코드가 발생하게 된다.
공통 코드?
MVP에서 발생하는 공통 코드를 어떻게 해결할 수 있을까?
- Presenter도 상속을 받아서 공통 코드를 해결
- 별도의 공통 로직을 가지는 새로운 클래스를 생성
- Presenter 분리?
위와 같은 공통 코드를 관리할 수는 있다.
MVVM은 View Model 분리의 시작
위에서 적은 Presenter를 분리하면 MVVM이 된다. View에 대한 모델을 분리하였기 때문에 ViewModel이다.
즉,ViewModel은 View에 대한 모델 분리가 있어야 한다. View에서 사용하는 중복적인 Presenter의 코드가 발생하면 이를 ViewModel 하나로 분리해주는게 가장 이상적이다.
MVP의 기본 개념인 View와 Presenter의 1:1 관계 유지에서 벗어나기 위해서는 MVVM이 가장 좋은 해결책으로 보인다. 조금 더 이쁜 코드를 만들기 위해서는 최소한 View에 대한 Model은 정의해주어야 한다. 그렇게 해야 아래와 같은 처리가 가능해진다.
- View에 대한 종속성을 줄인다.
- 종속성을 줄이기 위해 ReactiveX, Databinding 등을 이용할 수 있다.
- View에 대한 Model 정의가 명확해야 한다.
- 이 ViewModel은 언제든 View에서 가져다 쓰기만 하면 되고, 불필요한 경우 해당 ViewModel만 버릴 수 있어야 한다.
- ViewModel에 대한 테스트가 가능해진다.
- View에 대한 테스트와 완전하게 분리 가능하여 비즈니스 로직이 아닌 각각의 ViewModel 테스트가 가능해진다.
MVVM의 ViewModel이란?
MVVM은 고민을 많이 해야 하는 구조이다.
- ViewModel 분리가 필요할까?
- 분리했을 때 얻는 이점은?
- 종속성은?
- ReactiveX, DataBinding 등을 이용해 해결할 수 있다.
- 테스트 코드는?
- 테스트 코드는 유용해야 한다.
View에 대한 모델을 모두 분리한다면? 다음과 같이 구성할 수 있을 것이다.
- LoginViewModel : 로그인만 하는 모델
- LogoutViewModel : 로그아웃만 하는 모델
- UserInfoViewModel : 사용자 정보만을 가져와 가공한다.
여기까지 MVP와 MVVM에 대해 차이점과 ViewModel에 대해 조금 알아봤다. 이제는 MVVM을 알아보도록 하자.
2. MVVM
Presenter 대신에 ViewModel이 존재한다.
- ViewModel : View를 표현하기 위해 만들어진 View를 위한 Model
MVVM에서는 두 가지 디자인 패턴을 사용한다. Command 패턴과 Data binding이다. 이 두 가지 디자인 패턴을 사용함으로써 View와 ViewModel은 의존성이 완전히 사라지게 된다.
View에서 입력이 들어오고 Command 패턴을 통해서 ViewModel에 명령을 내리게 되고 Data binding으로 인해 ViewModel의 값이 변화하면 바로 View의 정보가 바뀌게 된다.
정리하자면
- View에 입력이 들어오면 Command 패턴으로 ViewModel에 명령을 한다.
- ViewModel은 필요한 데이터를 Model에 요청한다.
- Model은 ViewModel에 필요한 데이터를 응답한다.
- ViewModel은 응답 받은 데이터를 가공해서 저장한다.
- View는 ViewModel과의 Data Binding으로 인해 자동으로 갱신된다.
왜 MVVM을 사용할까?
전통적인 UI 개발에서 개발자는 윈도우, 사용자 컨트롤, 페이지를 사용해서 View를 만들곤 했다. 그리고 모든 논리 로직이 들어간 코드(핸들링, 초기화 그리고 데이터 모델 등)를 뷰 클래스에 정의한다. 이러한 방법은 뷰 클래스의 크기를 늘리고 UI와 데이터 그리고 비즈니스 사이의 매우 강한 의존성
을 형성한다.
이와 같은 상황에서 여러 개발자들은 동시에 같은 뷰에서 작업하기 힘들어 질 것이다. 즉, 협업하는 상황에서 같은 화면을 함께 개발하지 못하고 서로 다른 화면을 개발해야 하는 비효율적인 상황이 발생할 것이다. 또한, 다른 개발자가 코드를 바꾸면 다른 부분을 망가뜨릴 위험도 존재한다.
이처럼 모든 코드들이 한 클래스에 있는 것은 유지보수 그리고 테스트를 하는데 있어서 좋지 않다.
- 뷰(UI)
- 모델(UI 상에 보여지고 있는 데이터)
- 글루 코드(핸들링과 바인딩 그리고 비즈니스 로직)
- 글루 코드 : 서로 다른 코드를 접착시키는 코드
- 비즈니스 로직 : 데이터의 처리가 이루어지는 부분
MVVM에서 글루 코드는 뷰 모델이다. 그래서 애플리케이션의 구조를 더욱 간단하고 유지가능할 수 있게 만들기 위해서는 관심사의 분리에 집중해야 한다.
만약, 뷰 모델에서의 프로퍼티 값이 변하게 되면 데이터 바인딩과 노티피케이션을 통해 자동으로 새로운 값을 알려준다. 만약 사용자가 저장하기 버튼을 클릭하는 액션을 뷰에서 취했을 때, 뷰모델은 요청된 명령을 실행한다. 이 과정에서 뷰모델은 모델의 데이터를 수정하고 뷰는 데이터를 수정하지 않는다. 뷰는 모델의 존재를 모른다. 반면 뷰 모델과 모델은 뷰를 인식하지 않는다. 모델은 뷰와 뷰 모델의 존재를 모른다.
MVVM에 대해
MVVM 패턴을 지켜 개발된 앱은 아래의 특징을 갖는다.
- 관심사의 분리 - 하나의 소프트웨어를 최대한 기능적으로 작은 단위로 나눈다
- 테스트가 쉬워지고 큰 프로젝트도 상대적으로 관리하기 좋다.
- SOLID 원칙을 지향한다.
- 앱이 구조적으로 약한 결합의 컴포넌트로 나눠진다.
대체적으로 위와 같은 장점에 대해서 이야기를 하지만 결국 가장 큰 목적은 유지보수가 쉽고 테스트가 용이한 코드를 만드는 것이다.
View는 ViewModel에게 클릭 이벤트, 필요한 데이터 요청 등을 명시적으로 하고 ViewModel이 notify할 때까지 기다리게 된다.
동일하게 ViewModel은 Model을 통해 데이터를 요청하고 기다리게 된다. 각각의 컴포넌트간 참조를 갖지 않고 단방향(View -> ViewModel -> Model)의 의존성만을 갖게 된다.
정리
- 뷰, 뷰모델, 모델 이 셋은 서로의 존재를 몰라야 한다.
-> 서로 간의 의존성을 최대한 제거함으로써 유지보수가 쉽고 테스트 가능한 코드가 된다. - 뷰에서 액션을 받으면 뷰모델에서 비즈니스 로직을 실행한다.
- 뷰 모델은 모델의 데이터를 수정한다.
- 뷰 모델은 데이터 바인딩과 노티피케이션을 통해 뷰를 수정한다.
- 뷰와 뷰 모델은 n:m의 관계이다.
- 오직 뷰모델만이 모델에 접근할 수 있고 뷰는 모델에 접근하지 못한다.