1. [Android] MVVM Part.1
  2. [Android] MVVM Part.2

MVVM에 대한 이해가 부족해 보충하고자 조금 더 공부하며 정리를 하고자 한다.

안드로이드 개발을 처음 시작한다면 하나의 액티비티에 모든 코드를 넣어서 개발을 할 것이다. 이렇게 되면 시간이 지날수록 액티비티는 무거워지고 많은 일을 담당하게 될 것이다. 그로 인해 수정이나 유지보수하기가 힘들어진다. 이처럼 비구조적이고 유지보수가 어려운 코드를 스파게티 코드라고 하는데, 나의 첫 프로젝트도 기가 막힌 스파게티 코드였다.

스파게티 코드를 줄이고 이쁜 코드를 만들기 위해 디자인 패턴 공부의 필요성을 느끼게 되었다. 수업 시간에 들은 디자인 패턴을 잘 숙지해둘 걸 하는 후회가 밀려온다. 기억이 잘 나지 않기 때문이다…ㅜ

아무튼 디자인 패턴은 차근차근 공부하기로 하고 MVVM에 대한 글을 쓰면서 내가 알고 있는 개념도 정리하려고 두 번째 글을 쓰게 되었다. 첫 번째 글은 MVP와 MVVM의 차이에 대한 설명과 어떤 점이 좋은지에 대해 간략하게 설명하고 있다.

MVVM 패턴

액티비티에 기능을 붙이다보면 액티비티가 무거워지거나 혹은 종속성이 너무 강해 테스트가 힘들고 유지보수가 어려워진다. 이러한 고민을 해결하기 위해 Architecture 패턴이 등장했고 그 중 하나가 이번에 살펴볼 MVVM이다. MVVM은 View - ViewModel - Model을 이용해 각각의 역할을 분리하여 가독성과 재사용성을 높인 Architecture 패턴이다.

MVC와 MVV 차이점

기존 MVC(Model - View - Controller) 구조에서는 액티비티가 컨트롤러의 역할을 했으며, 뷰와 연결되어 사용자와 상호작용도 하고 모델과 연결되어 데이터도 처리했다. 즉, 뷰와 모델 사이에서 중재자 역할을 했다.

MVVM에서는 뷰에서 뷰모델로, 뷰모델에서 모델로 작업을 처리하며, 뷰에서 모델을 직접 참조하지 않는다. 대신 뷰에서 뷰모델을 관찰하여 데이터의 변경 사항을 감지한다.

예를 들어보자. 유저가 목록에 새로운 아이템을 추가했다.

MVC에서는 컨트롤러가 유저의 클릭 액션을 확인하고, 모델에 데이터를 갱신하도록 요청한다. 그리고 뷰에도 화면을 업데이트하라고 요청을 해야 한다. 이 경우 액티비티가 해야 할 일이 너무 많다. 까닥하면 DB만 갱신되고 화면은 갱신되지 않는 경우도 있다.

MVVM에서도 뷰가 유저의 클릭 액션을 확인하지만, 뷰에서 곧바로 DB에 접근하지 않는다. 말 그대로 뷰이기 때문에 UI를 갱신하는 역할에 충실하다. 대신 뷰모델을 참조하고, 뷰모델에서는 다시 모델에서 정리된 데이터를 참조한다. 또, 뷰는 뷰모델은 관찰(Observe) 한다. DB에 새로운 아이템을 추가한 후에 화면을 업데이트 하라고 직접 명령하지 않아도 된다. 뷰에서는 이미 뷰모델을 관찰하고 있기 때문에 데이터의 변화를 알아차리고 자동으로 화면을 갱신한다.

MVVM의 장점

  1. 뷰가 데이터를 실시간으로 관찰한다.

LiveData 즉, Observer 패턴을 이용하기 때문에 데이터베이스를 관찰하고 자동으로 UI를 갱신한다. 직접 뷰를 바꿔주는 번거로움도 없으며 데이터와 불일치할 확률이 줄어든다.

  1. 생명주기로부터 안전하다. 메모리 누수 방지!

AAC의 뷰모델을 통해 데이터를 참조하기 때문에 액티비티/프래그먼트의 생명주기를 따르지 않는다. 즉, 액티비티/프래그먼트의 생명주기 동안 계속 살아있고, onDestroy()가 호출될 때 사라지게 된다.

화면 회전과 같이 액티비티가 파괴된 후에 재구성되어도 뷰모델이 데이터를 홀드하고 있기 때문에 영향을 받지 않는다. 또한, 뷰가 활성화되어있을 경우에만 작동하기 때문에 불필요한 메모리 사용을 줄일 수 있다.

  1. 역할 분리!

UI, 비즈니스 로직, 모델이 기능별로 모듈화 되어 있어서 역할 별로 분리가 되어있다. 이로 인해서 테스트 코드를 작성하는게 한결 수월해진다. 이처럼 모듈화가 잘 되어 있다면 다음과 같은 상황에서 수정이 쉽다.

  • 내장 DB를 통째로 바꾸고 싶다고 할 때, 뷰나 다른 코드에 깊게 종속되어 있지 않기 때문에 DB만 교체하면 된다.
  • 뷰모델과 뷰는 1:N 관계를 갖기 때문에 뷰모델에 하나의 메소드를 구현하고 A 액티비티나 B 액티비티, 여러 뷰에서 호출해 사용하기 때문에 재사용이 가능하다.

AAC

구글은 Android Architecture Component 이하 AAC를 제공한다. AAC는 앱 구조를 더 튼튼하고 테스트에 용이하고, 유지보수성이 뛰어나게 만들도록 도와주는 라이브러리의 모음이다. 조금 더 모듈화된 코딩을 돕기 위해 Databinding, LiveData, ViewModel 등의 유용한 라이브러리를 제공하며, 이러한 라이브러리를 사용해 MVVM 패턴을 조금 더 쉽게 구성할 수 있다.

위의 이미지에서 MVVM의 구성 요소로 역할을 나눠보자.

  • View : 초록색(UI)
  • ViewModel: 파란색
  • Model : 노란색(Repository), Room Database

View

  • UI를 담당하는 액티비티, 프래그먼트를 의미한다.
  • 화면에 무엇을 그릴지 결정하고 사용자와 상호작용한다.
  • 보통 데이터의 변화를 감지하기 위한 옵저버를 가지고 있다.

ViewModel

  • UI를 위한 데이터를 가지고 있으며, 구성이 변경되어도 살아남는다.(ex. 화면 회전, 언어 변경 등)
  • AsyncTask는 액티비티나 프래그먼트의 생명 주기에서 자유로울 수 없지만, 뷰모델은 뷰와 분리되어 있기 때문에 액티비티가 Destory 되었다가 다시 Create 되어도 종료되지 않고 데이터를 여전히 유지하고 있다.

LiveData

  • 관찰(Observable)이 가능한 데이터 홀더 클래스이다.
  • 뷰에서 뷰모델의 Live Data를 관찰하게 되면 데이터가 변경될 때 내부적으로 자동으로 알려주게 된다.
  • 또한, Live Data는 액티비티나 프래그먼트의 생명 주기를 인지한다. 즉, 액티비티가 활성화되어 있을 때에만 UI 변경 등의 기능을 동작하게 되고, Destory된 상태에서는 동작하지 않기 때문에 Memory Leak의 발생을 줄여준다.

Repository

  • 뷰 모델과 상호작용하기 위해 잘 정리된 데이터 API를 들고 있는 클래스이다.
  • 앱에 필요한 데이터, 즉 내장 데이터베이스나 외부 웹 서버 등에서 데이터를 가져온다.
  • 따라서 뷰모델은 DB나 서버에 직접 접근하지 않고, Repository에 접근하는 것으로 앱의 데이터를 관리한다.

결론

MVVM에 대해 처음에는 이해하기가 너무 어려웠다. 왜냐하면 간단한 기능을 가진 앱을 만들더라도 많은 클래스가 필요하고, 프로젝트의 패키지 구조가 복잡해지기 때문이다.

또한, 인터넷 상에 존재하는 대부분의 MVVM 예제는 DI가 적용된 것들이 많다. MVVM도 처음에 어려운데 DI까지 함께 한다면 정말 머리가 깨질 수도 있다.

그래서 나는 Koin을 사용한 MVVM 예제를 찾았고 이를 많이 참고했다. Dagger2에 비해서 접근하기가 훨씬 쉬우며, 사용법도 간단해서 금방 배울 수 있다고 생각한다.

아래는 내가 공부하면서 참고했던 Sample 프로젝트의 출처이다.

그래도 위의 프로젝트를 참고해서 구조를 익히고 따라해서 만들고 직접 간단하게라도 적용해본다면 MVVM에 대한 이해가 될 것이라고 생각한다. 나도 위의 3 프로젝트를 직접 따라해보면서 코딩해봤고, 혼자서 분석도 해봤다.

그래서 지금 진행 중인 프로젝트에 MVVM을 적용하고 있다. 아직 어려운 부분이 좀 많지만 그때 그때 구글링을 통해서 해결하고 있다. 이런 해결 과정도 정리해놓으면 좋을 것 같다. 아무튼, 모두 화이팅!(나도!)

참고