효율적인 코드는 개발자가 작성한 코드가 직관적이고 명확하며 단순해야 한다고 생각한다. 개인이 혼자 개발하는 프로젝트가 아닌 이상 내가 작성한 코드를 다른 사람이 한눈에 알아보고 작업을 이어나가는 것이 중요할 것이다. 따라서 코드의 재사용성 또한 중요할 것이고 이에 따른 유지보수가 적거나 쉬워야 한다. 마찬가지로 어떤 시스템을 구성하는 서브 시스템, 모듈, 컴포넌트와 같이 구성요소들 간의 결합도들을 최소화하는 것이 중요할 것이다. 이를 위해 다양한 아키텍쳐 패턴들을 사용하게 되는데, 이러한 패턴들을 적용하여 개발을 하다 보면 가독성 좋은 코드, Unit Test, 원활한 유지보수를 보장한다.
이번 포스팅은 아키텍쳐 패턴 중 MVP (Model View Presenter)에 대한 포스팅이다.
1. MVP 배경 및 구성 요소
기존의 MVC 패턴은 Activity나 Fragment에서 View와 Control을 모두 처리할 수 있었다. 사실 MVC도 함수 분리만 잘하고 코드만 잘 이해할 수 있으면 쉽게 로직의 파악이 가능하긴 하다. 하지만 결과적으로는 코드의 양이 증가하게 되고 View와 Model 간의 높은 결합도가 가장 큰 문제였다. 대부분의 코드는 View에서 Model을 직접 호출하여 사용하기 때문이다. 또한, UI에서 모든 것을 하기 때문에 UI위주의 테스트 코드만 작성이 가능하다. 따라서 Unit Test 측면에서는 분명한 한계가 있다.
MVP 패턴은 Model, View, Presenter의 앞 글자를 따서 지어졌다.
Model
MVC 패턴의 Model과 동일하다. Data에 접근하여 관리하는 역할을 수행한다. 즉, Data와 관련된 처리를 담당하며 네트워크, 로컬 데이터를 포함한다.
View
사용자의 실질적인 Event가 발생하고, 이 Event를 처리하기 위해 Presenter로 전달하는 역할을 한다. MVC는 Controller로 Event를 인식하는 것과 차이점이 있다. MVP에서 View는 순수하게 View의 형태만 가지고 있다. 또한 View에 대한 모든 처리는 View에서 이루어지나, 데이터가 필요한 경우 Presenter에 요청 후 데이터를 전달받아 View에서 처리한다.
Presenter
View에서 전달받은 Event를 처리하고, 이를 다시 View에 전달하는 역할을 한다. 따라서, View와 Presenter는 1:1 관계를 갖게 된다. Model로부터 Data를 받아 가공하여 View에 전달하기 때문에 View와 Model을 간접적으로 이어주는 역할을 한다.
2. MVP 패턴 구조
안드로이드 개발에 있어서 MVP 패턴은 이전의 MVC 패턴과 달리 Model과 View 간의 결합도를 낮추는데 초점을 둔다. 위의 그림처럼 Model과 View가 완전히 분리되어 있다.
정확한 이해를 돕기 위해 간단한 동작 방식을 아래에 적어 두었다.
3. 안드로이드 MVP 패키지 구조
다양한 패키지 구조가 있겠지만 검색하다 보니 가장 직관적이고 파일 탐색이 편한 패키지 구조를 찾게 되었다.
https://thdev.tech/androiddev/2017/02/20/Android-MVP-Package-Structure/
source 디렉토리에는 데이터가 local인지 remote인지를 구분한다. local인 경우 디바이스 내부 저장소와 관련된 로직이 모일 것이고, remote인 경우 원격 서버와의 통신을 통한 데이터와 관련된 로직이 모이게 될 것이다.
view 디렉토리에는 화면 단위에서 동일한 Activity, Fragment, Presenter, Adapter 등을 기능 별로 모아두게 된다.
위 패키지 구조는 Android Studio에서 아래와 같이 확인 가능하다.
4. Presenter 생성 방법 1. Google Architecture
https://github.com/android/architecture-samples/tree/todo-mvp-kotlin
위의 예시에서는 Contract라는 Interface를 생성한다. Contract는 View와 Presenter에 대한 Interface이다.
Contract 구현
interface SampleContract {
interface View {
// View와 관련된 Methods
}
interface Presenter {
// Presenter와 관련된 Methods
fun bindView(view: View)
}
}
하나의 Interface에 View와 Presenter에서 사용될 Method들을 만들어놓고 각각의 View와 Presenter에서 정의하는 방식이다.
Presenter 구현
SampleContract의 Presenter Interface를 상속받아 아래와 같이 사용할 수 있다.
class SamplePresenter: SampleContract.Presenter {
private var sampleView: SampleContract.View? = null
override fun bindView(view: SampleContract.View) {
sampleView = view
}
}
View 구현 - Activity
SampleContract의 View Interface를 상속받아 아래와 같이 사용할 수 있다.
class SampleActivity: AppCompatActivity(), SampleContract.View {
private lateinit var samplePresenter: SampleContract.Presenter
override fun onCreate(..)
// SampleContract.View에 선언한 Method 구현
}
5. Presenter 생성 방법 2. PresenterImpl 구현
Google Architecture의 Contract를 사용할 땐 View와 Presenter에 대한 Interface 두 개를 만들어 사용했다. 이 방식은 Presenter에 View에 대한 Interface만 작성하고 PresenterImpl에서 Presenter를 상속받아 사용한다.
interface SamplePresenter {
// Presenter 관련 Method 구현
fun loadItem()
interface View {
// View 관련 Method 구현
}
}
class SamplePresenterImpl(private val view: SamplePresenter.View): SamplePresenter {
override fun loadItem()
}
6. Model 구현
Google Architecture의 Model을 위의 그림으로 나타내고 있다.
Repository는 Remote와 Local을 구분하며 Cache를 포함한다. Remote data source는 네트워크를 통해 서버에서 데이터를 받아오는 역할을 한다. Retrofit2나 OkHttp 라이브러리를 활용하는 곳이다. Local data source는 디바이스 내부 저장소에 저장되어있는 데이터를 받아오는 역할을 한다. Jetpack Room을 활용할 수 있다.
중요한 점은 View와 Presenter가 1:1 관계를 갖는다고 해서 Model과 Presenter가 1:1 관계를 갖는 것은 아니다. 만약, View에서 표현해야 할 Model이 2개 이상인 경우 Presenter는 여러 Model을 가질 수 있다. 또한, 각각 다른 View에서 동일한 Model을 표현해야 할 경우 Presenter는 기존의 Model을 여럿 가질 수 있다.
7. MVP의 한계
MVP는 기존의 MVC 패턴이 가지고 있던 View와 Model의 강한 결합을 Presenter를 활용하여 제거할 수 있었다. 하지만 이와 동시에 View는 Presenter와 결합이 생겨버렸다. 또한, 한 화면에서 추가적인 기능을 구현할 시 Presenter에 비즈니스 로직이 모이게 될 수밖에 없다.
References
https://thdev.tech/androiddev/2016/10/12/Android-MVP-Intro/
https://youngest-programming.tistory.com/111
https://academy.realm.io/kr/posts/eric-maxwell-mvc-mvp-and-mvvm-on-android/
https://github.com/android/architecture-samples/tree/todo-mvp-kotlin
'SW Engineering > Architecture Patterns' 카테고리의 다른 글
(Architecture) MVVM (0) | 2021.12.02 |
---|