오랜시간 안드로이드 애플리케이션을 만들다 보면 좀 더 효율적이고 재사용이 되면서 각 단위로 테스트를 쉽게 할 수 있는 형태로 점진적으로 발전하게 됩니다. iOS의 경우 MVC기반으로 잘 구성되어 있지만 안드로이드는 그렇지 못했습니다. 그동안 여러 모델등을 통해서 이러한 패턴들이 발전해 왔고 MVC나 MVP, MVVM으로 불리며 안드로이드 패턴 아키텍처를 형성하고 있습니다. 안드로이드 팀에서도 나름대로 Android Architecture Components를 구성하여 좀더 좋은 디자인을 할 수 있도록 돕고 있습니다. 여기서는 많이 사용되어온 패턴들을 정리해 보도록 하겠습니다.
MVC(Model-View-Controller)은 유저 인터페이스(View)와 비지니스 룰이나 데이터(Model), 모델을 통해서 뷰에 연결하기 위한 중재자(Controller)로 구분되는 소프트웨어 프로젝트의 표준 모델입니다. 안드로이드에서는 외부의 모든 이벤트는 Controller(안드로이드에서 Activity)에서 처리하되 View에는 관여하지 않는 것이 원칙입니다. 하지만, 안드로이드에서는 Activity 특성상 View에 깊이 관여할 수 밖에 없습니다.
Model
Model은 앱에서 데이터나 상태, 비지니스 로직을 가르키고 있고 뷰나 컨트롤러와 묶여있지 않으므로 언제든 다른 문맥에서 재 사용이 가능합니다. 예를 들어 네트워크를 담당하거나 데이터베이스에 접근할 수 있습니다.
뷰는 모델의 표현형식으로 주로 UI(User Interface)를 표시하기 위한 목적으로 사용 되며 사용자가 애플리케이션의 UI를 건드리면 컨트롤러와 통신하게 됩니다.
사용자에 의해 특정 이벤트가 발생하면 컨트롤러는 그 이벤트와 상호작용하고 그것을 어떻게 처리할지를 결정합니다. 또한, 모델(Model)에서 데이터의 변화가 감지되면 그것을 적절하게 뷰(View)의 상태를 업데이터하거나 변경하게 됩니다. 보통, 안드로이드에서는 Activity나 Fragment가 컨트롤러 역할을 담당하게 됩니다.
MVC는 모델과 뷰를 분리한 모델이지만 컨트롤러에 역할에 몇가지 문제점이 있습니다. 컨트롤러는 안드로이드 API에 많이 종속되어 있어 유닛 테스트가 어렵습니다. 컨트롤러가 뷰에 강하게 결합되어 있어 뷰를 변경하면 컨트롤러도 변경되어야 합니다. 개발이 지속 되면서 상당한 량의 코드가 Activity에 집중되게 됩니다. 따라서 이것을 완화하기 위한 모델이 나오게 됩니다.
View와 Activity가 자연스럽게 결합하도록 합니다. 모델에는 변화가 없습니다. 직접적인 View의 접근은 Activity가 하도록 하고 이에 대한 제어는 Presenter가 하도록 하고 있습니다.
액티비티나 프래그먼트가 이제 뷰의 일부로 간주됩니다. 액티비티가 뷰 인터페이스를 구현해서 Presenter가 코드를 만들 인터페이스를 갖도록 하는것이 좋습니다. 이렇게 하면 특정 뷰와 결합되지 않고 가상 뷰를 구현해서 간단한 유닛 테스트를 실행할 수 있습니다.
본질적으로는 MVC의 컨트롤러와 같지만 뷰에 연결된것이 아니라 그냥 인터페이스라는 점이 다릅니다. 뷰와 프리젠터의 관계는 1:1입니다. 따라서 Activity가 N개 있다면 프리젠터도 N개가 있어야 합니다. 중복된 코드가 생길 수 밖에 없는 구조입니다. 그림으로 표현 하면 다음과 같을 것입니다.
MVP에서는 View는 1:1로 매칭하며 Presenter가 주요 기능을 담당하고 실제 view에서 발생하는 이벤트는 양방향 처리가 되는데 예를 들어 누군가 저장 버튼을 누르는 이벤트를 발생 시키면 Presenter에 'OnSave'에 전달하여 처리하도록 하고 다시 처리된 결과는 Presenter가 인터페이스를 통해 View에 전달하도록 합니다. 그런 다음 save가 완료된것을 볼 수 있습니다.
MVVM은 안드로이드의 데이터 바인딩을 사용합니다. 테스트와 모듈화가 쉽고 뷰와 모델을 연결하기 위해 사용해야 하는 연결 코드를 줄일 수 있다는 장점이 있습니다. 모델은 MVC의 모델 개념과 동일합니다.
뷰는 뷰모델에 의해 보여지는 Observable 변수와 액션에 유연하게 binding 됩니다.
ViewModel은 모델을 랩핑하고 뷰에 필요한 Observable데이터를 준비합니다. 뷰모델이 뷰에 종속되지 않습니다. 뷰가 모델에 이벤트를 전달할 수 있도록 hook을 준비합니다. View와 상호작용 하며 이벤트를 처리하기 때문에 코드가 많이 집중됩니다. 따라서 Activity는 초기화와 View의 이벤트가 아닌 외부 이벤트를 처리하는 것 외에는 코드가 줄어들게 됩니다.
MVVM은 두가지 디자인 패턴을 사용하는데 바로 Command와 Data Binding을 사용합니다. 이 패턴으로 인해 View와 ViewModel은 의존성이 사라지게 됩니다. View를 통해 입력이 들어오면 Command 패턴을 통해 ViewModel에 명령을 내리게 되고 Data Binding으로 인해 ViewModel의 값이 변화하면 바로 View의 정보가 바뀌게 됩니다.
어떤 패턴을 사용하던지 정답은 없습니다. 상황에 따라 적절한 패턴을 선택해야 할 것입니다. 또한 각 패턴을 안드로이드에 적용하기에 모호한 측면도 있습니다. 예를 들어 Activity는 View와 Controller의 역할을 다 할 수 가 있다는 점에서 분리하기 애매한 점이 있습니다. MVP와 MVC의 차이점도 의견이 저마다 다르기도 합니다. 여기(Stackoverflow)를 확인해 보세요. 간단한 프로젝트에 너무 복잡한 패턴을 적용하는 것이 오히려 방해가 될 수도 있습니다. 아무튼 애자일 소프트웨어 핸드북에 있는 로버트의 문장으로 글을 마쳐볼까 합니다. 다음에는 Adnroid Architecture Components에 대해서 살펴보겠습니다.
The only way to make the deadline - the only way to go fast - is to keep the code as clean as possible at all times.
- Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship
"어떤 것을 완전히 알려거든 그것을 다른 이에게 가르쳐라."
- Tryon Edwards -