(나를 포함한) 안드로이드 개발을 처음 시작하는 사람들은 대부분 액티비티에 거의 모든 코드를 직접 넣는다. 하지만 시간이 갈수록 액티비티는 점점 무거워지고, 수정이나 유지 보수 하기가 힘들어진다. 흔히 비 구조적이고 유지보수가 어려운 코드를 스파게티 코드하고 하는데, 나의 첫 프로젝트는 스파게티에 라면 사리 다섯 개 정도 추가한 모습과 같았다.

때문에 디자인 패턴 공부의 필요성을 마음 깊이 느끼게 됐다. 이제 인싸 안드로이드 개발자라면 다들 MVVM에 DataBinding, Rx 등등은 숙지하고 있는 것 같다. 낯선 생김새 때문에 쉽사리 입문하기 힘들었지만, 일단 차근차근 하나씩 글을 써가며 연습해보기로 했다. 이 중에서도 MVVM 패턴과, 그것을 쉽게 만들도록 도와주는 안드로이드 아키텍쳐 컴포넌트 에 관한 예제로 시작하려고 한다.

***

글이 너무 길어져 개념 설명과 예제 포스트를 나누어 작성했다!

  1. MVVM + AAC 시작하기(현재글) - MVC와의 차이점, MVVM의 장단점, AAC 설명
  2. MVVM 연습 예제1 - MVVM패턴, AAC(ViewModel, LiveData, Room), RecyclerView
  3. MVVM 연습 예제2 - 예제1 + DataBinding (조금 더 사용해보고 추가하겠습니다)

MVVM 패턴?

위에서 언급한 대로, 액티비티에 기능을 붙이다보면 액티비티가 무거워지거나 혹은 종속성이 너무 강해 테스트가 힘들고 유지보수가 어려워진다. 이런 고민 때문에 MVVM 패턴이 등장했다. MVVM은 View - ViewModel - Model을 이용해 각각의 역할을 분리하여 가독성과 재사용성을 높인 디자인 패턴이다.

MVC와 MVVM 차이점

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

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

그림으로 그려보자면 뭔가 이런 느낌…?


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

MVC에서는 컨트롤러가 유저의 클릭 액션을 확인하고, 모델에 데이터를 갱신하도록 요청하고, 뷰에도 화면을 업데이트 하라고 요청을 해야한다. 코딩하다 무언가 빠뜨리면, 때로는 DB만 갱신되고 화면은 갱신되지 않는 경우도 나타난다. 액티비티가 해야 할 역할이 많아 바쁘다.

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


MVVM 장점을 요약하자면

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

2. 생명주기로부터 안전! 메모리 릭 방지! 뷰모델을 통해 데이터를 참조하기 때문에 액티비티/프래그먼트의 생명주기를 따르지 않는다. 화면전환과 같이 액티비티가 파괴된 후 재구성 되어도 뷰모델이 데이터를 홀드하고 있기 때문에 영향을 받지 않는다. 또한 뷰가 활성화되어있을 경우에만 작동하기 때문에 불필요한 메모리 사용을 줄일 수 있다.

3. 역할 분리! 모듈화! UI, 비즈니스 로직, 데이터베이스가 기능별로 모듈화 되어있어서 역할 별로 정리가 ★☆ 깔-끔 ☆★ 유닛 테스트가 한결 용이해질 것이다. (물론 MVVM도 잘 짜야겠지만 말이다.)

모듈화가 잘 되어있다면 다음과 같은 상황에서 편하게 코딩이 가능할 것 같다.

  • 내장 DB를 통째로 바꾸고 싶다고 할 때, 뷰나 다른 코드에 깊게 종속돼있지 않기 때문에 DB만 쓱 교체해주면 된다.
  • 뷰모델과 뷰가 1:n 연결이 가능하기 때문에, 뷰모델에 하나의 메소드를 구현해 놓으면 A 액티비티든 B 액티비티든 여러 뷰에서 호출해 재사용하기 편리하다.


단, 예상되는 단점이라면 MVVM 패턴에선 기존에 비해 추가로 만들어주어야 하는 클래스도 많고, 이들을 서로 코딩하여 연결해주어야 한다. 이 과정이 복잡해지면 기존의 프로젝트에 적용하기는 시간적, 인적 자원이 많이 필요할 것 같다.


안드로이드 아키텍쳐 컴포넌트

이런 트렌드에 발맞춰, 구글에서는 안드로이드 아키텍쳐 컴포넌트(Android Architecture Components, AAC)를 제공한다. 안드로이드 아키텍쳐 컴포넌트는 앱 구조를 더 튼튼하고, 테스트에 용이하고, 유지 보수성이 뛰어나게 만들어 주는 라이브러리 모음이다. 아키텍쳐 컴포턴트에서는 조금 더 모듈화된 코딩을 돕기 위해 Databinding, LiveData, ViewModel 등의 유용한 라이브러리를 제공하며, 이러한 라이브러리의 모음은 MVVM 패턴의 구조의 설계에 최적화되어 있다.

참조 - Android Developers - AAC


이미지 출처 - GDD India ‘17 Youtube


MVVM의 구성 요소로 역할을 나눠보자면 초록색 부분은 UI를 담당하는 뷰, 파란 부분은 뷰모델, 그리고 리포지토리와 룸 데이터베이스는 모델 역할을 담당한다.

각각의 기능은 다음과 같다.

View - 뷰

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

ViewModel - 뷰모델

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

LiveData - 라이브데이터

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

Repository - 리포지토리

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

Room - 룸

SQLite 데이터베이스를 편하게 사용하게 해주는 라이브러리이다. SQLite의 코드를 직접 작성하는 경우, 직접 테이블을 Create 하거나 쿼리문을 일일이 변수를 통해 작성해주어야 했지만, Room을 쓰면 조금 더 직관적이고 편리하게 DB를 사용할 수 있다. 이전에 작성한 Android Room 포스트에 조금 더 자세히 정리해두었다.


결론

  • MVVM 패턴에서는 ViewModel이나 Repository 같이 낯선 새로운 클래스를 몇개 더 만들어야 해서 어려워 보일 수 있다. 아니, 어렵다기보다는 구조가 MVC에 비해 상대적으로 복잡해진다. 수십 개의 기능을 가진 기존의 프로젝트를 MVVM으로 리팩토링 하려면 난이도가 장난 아니겠다. 보수적인 기업에서는 더욱 적용하기 힘들어 보인다.

  • 앱에 기능이 더 붙으면 구조 짤 때 머리가 터지겠구나, 싶다. 안드로이드 개발자들 모인 곳에 가끔 ‘이 기능은 MVVM 중 어디에 붙여야 하나요?’ 하는 질문이 올라오는데, 왜인지 알 것 같다.
  • 하지만 코드 재사용성은 확실히 높을 것 같다. 나도 아직 실제로 적용해보진 않아서 조금 더 써 봐야 여기서 말하는 효과를 체감할 수 있을 것 같다.

의문

  • MVVM을 적용하기로 했다면 한 프로젝트 내에서는 모든 코드를 MVVM으로 쓰는지, 아니면 일부 간단한 액티비티의 경우에는 그냥 MVC와 병행해서 쓰는지 궁금해졌다.


엮인 글 - MVVM 연습 예제1 - MVVM패턴, AAC(ViewModel, LiveData, Room), RecyclerView


References

  • https://developer.android.com/topic/libraries/architecture
  • https://www.youtube.com/watch?v=BofWWZE1wts
  • https://www.youtube.com/watch?v=ARpn-1FPNE4&t=4s
  • https://academy.realm.io/kr/posts/eric-maxwell-mvc-mvp-and-mvvm-on-android/
  • https://medium.com/@ankit.sinhal/mvc-mvp-and-mvvm-design-pattern-6e169567bbad