2025-09-22 00:45
MVP 패턴 완벽 정복 가이드 A to Z: 더 나은 안드로이드 개발을 위하여
소프트웨어 아키텍처는 견고하고 유지보수하기 쉬운 애플리케이션을 만드는 핵심적인 설계도입니다. 특히 UI를 가진 애플리케이션에서는 데이터, 로직, 그리고 화면 표시를 어떻게 분리하고 구성하느냐가 전체 시스템의 품질을 좌우합니다. 수많은 아키텍처 패턴 중, 안드로이드 초창기부터 현재까지 많은 개발자에게 사랑받고, 또 다른 패턴의 기반이 된 MVP(Model-View-Presenter) 패턴에 대해 깊이 있게 탐구해 보겠습니다.
이 핸드북은 MVP 패턴이 왜 등장했는지부터 시작하여 그 구조와 작동 방식, 장단점, 그리고 다른 패턴과의 비교까지 상세하게 다룹니다.
1. MVP 패턴은 왜 만들어졌을까? 거인의 어깨 위에서
MVP 패턴의 탄생 배경을 이해하려면 그 이전에 널리 사용되던 MVC(Model-View-Controller) 패턴의 한계를 먼저 알아야 합니다.
MVC의 도전 과제: 거대 컨트롤러(Massive View-Controller)
전통적인 MVC 패턴에서 View(UI)는 사용자의 입력을 받아 Controller에게 전달하고, Controller는 Model(비즈니스 로직 및 데이터)을 업데이트합니다. 그러면 Model은 자신의 데이터가 변경되었음을 View에게 알리고, View는 변경된 데이터를 다시 Model로부터 가져와 화면을 갱신합니다.
라이선스 제공자: Google
이 구조는 논리적으로 명확해 보이지만, 특히 안드로이드와 같은 플랫폼에서는 몇 가지 문제점을 드러냈습니다.
-
강한 결합도: View와 Model이 직접적으로 연결(의존)됩니다. View가 Model의 구조를 알아야 데이터를 가져와 표시할 수 있기 때문에, Model이 변경되면 View도 함께 수정되어야 하는 경우가 많았습니다.
-
테스트의 어려움: 안드로이드에서
Activity나Fragment가 View와 Controller의 역할을 동시에 수행하는 경우가 많았습니다. 이들은 안드로이드 프레임워크에 대한 강한 의존성을 가지므로, 로직을 테스트하기 위해선 복잡한 안드로이드 환경을 구성해야만 했습니다. 순수한 비즈니스 로직의 단위 테스트(Unit Test)가 매우 어려워지는 결과를 낳았습니다. -
거대해지는 컨트롤러:
Activity가 모든 것을 처리하다 보니, UI 코드, 데이터 처리, 비즈니스 로직, 화면 전환 등 수많은 책임이 한곳에 집중되었습니다. 이는 수천 줄에 달하는 “거대 컨트롤러” 또는 “갓 액티비티(God Activity)“를 탄생시켰고, 코드를 이해하고 유지보수하기 어렵게 만들었습니다.
이러한 문제들을 해결하기 위해 등장한 것이 바로 MVP 패턴입니다. MVP의 핵심 목표는 **관심사의 분리(Separation of Concerns)**를 더욱 강화하여, 특히 View와 Model을 완전히 분리하는 것이었습니다.
2. MVP 패턴의 구조: 세 가지 핵심 역할
MVP 패턴은 애플리케이션을 세 가지 주요 구성 요소로 나눕니다.
1. 모델 (Model)
-
역할: 애플리케이션의 데이터와 비즈니스 로직을 담당합니다.
-
특징:
-
데이터의 상태를 관리하고, 데이터를 가져오거나(네트워크, 데이터베이스) 조작하는 규칙을 포함합니다.
-
순수한 비즈니스 로직을 담고 있으며, View나 Presenter에 대해 아무것도 알지 못합니다. (독립적)
-
예를 들어,
User객체나, 데이터베이스와 통신하는UserRepository클래스 등이 Model에 해당합니다.
-
2. 뷰 (View)
-
역할: 사용자에게 데이터를 보여주고(Display), 사용자의 **입력(Action)**을 받아 Presenter에게 전달합니다.
-
특징:
-
안드로이드에서는
Activity,Fragment, 또는View클래스가 이 역할을 수행합니다. -
최대한 수동적(Passive)이고 멍청하게(Dumb) 만드는 것이 핵심입니다. View는 “어떻게” 동작할지에 대한 로직을 포함하지 않고, 단지 Presenter가 지시하는 대로 화면을 갱신할 뿐입니다.
-
일반적으로
interface로 추상화되어 Presenter가 구체적인 View 구현체(Activity등)에 직접 의존하지 않도록 합니다.
-
3. 프레젠터 (Presenter)
-
역할: View와 Model 사이의 중재자 역할을 합니다.
-
특징:
-
View로부터 사용자 입력을 받으면, 이를 해석하여 Model에게 필요한 작업을 요청합니다.
-
Model로부터 데이터를 받아온 후, View가 화면에 표시하기 좋은 형태로 가공하여 View에게 전달합니다.
-
Presenter는 Model과 View의
interface에 의존합니다. 즉, 안드로이드 프레임워크와 완전히 독립적일 수 있습니다. 이 덕분에 Presenter의 로직은 JVM 위에서 간단하게 단위 테스트가 가능해집니다. -
View와 Presenter는 보통 1:1 관계를 가집니다.
-
3. MVP 패턴의 작동 방식: 상호작용의 흐름
사용자가 버튼을 클릭하여 데이터를 불러오는 간단한 시나리오를 통해 MVP의 상호작용을 살펴보겠습니다.
-
사용자 입력 (User Action)
- 사용자가 View(예:
MainActivity)에 있는 “데이터 불러오기” 버튼을 클릭합니다.
- 사용자가 View(예:
-
View → Presenter 호출
-
MainActivity는 이 클릭 이벤트를 직접 처리하지 않습니다. 대신, 자신이 참조하고 있는 Presenter의 메서드(예:presenter.loadData())를 호출합니다. -
View는 Presenter에게 “사용자가 데이터 로드를 원해!”라고 알리는 역할만 수행합니다.
-
-
Presenter → Model 요청
- Presenter는
loadData()메서드가 호출되면, Model(예:UserRepository)에게 데이터를 요청합니다. (예:userRepository.getUser("userId"))
- Presenter는
-
Model → Presenter 데이터 반환
- Model은 비즈니스 로직을 수행하여(DB 조회, API 통신 등) 데이터를 가져온 후, 그 결과를 Presenter에게 반환합니다.
-
Presenter → View 갱신 지시
-
Presenter는 Model로부터 받은 데이터를 View가 표시하기 쉬운 형태로 가공합니다. (예:
User객체를String이름으로 변환) -
가공된 데이터를 가지고 View의
interface에 정의된 메서드(예:view.showUserName("홍길동"),view.showLoading(false))를 호출하여 화면 갱신을 지시합니다.
-
-
View 화면 갱신
MainActivity는showUserName("홍길동")메서드가 호출되면,TextView의 텍스트를 “홍길동”으로 설정합니다. View는 왜, 어떻게 이 데이터가 왔는지 전혀 신경 쓰지 않고, 그저 화면에 표시하는 임무만 수행합니다.
이처럼 모든 로직과 흐름 제어는 Presenter가 담당하고, View는 철저히 수동적인 디스플레이 역할만 수행함으로써 각 컴포넌트의 책임이 명확하게 분리됩니다.
4. MVP 패턴의 장점과 단점
장점
| 장점 | 설명 |
|---|---|
| 뛰어난 테스트 용이성 | Presenter는 안드로이드 프레임워크에 대한 의존성이 거의 없으므로, 간단한 JVM 단위 테스트를 통해 비즈니스 로직을 빠르고 안정적으로 검증할 수 있습니다. |
| 명확한 관심사 분리 (SoC) | UI(View), 비즈니스 로직(Model), UI 로직(Presenter)의 역할이 뚜렷하게 구분되어 코드의 가독성과 이해도가 높아집니다. |
| 유지보수 및 확장 용이 | 각 컴포넌트의 결합도가 낮아져 하나의 변경이 다른 부분에 미치는 영향을 최소화합니다. 새로운 기능을 추가하거나 기존 로직을 수정하기가 훨씬 수월해집니다. |
| 팀 협업에 유리 | 디자이너나 UI 개발자는 View에 집중하고, 백엔드나 로직 담당 개발자는 Model과 Presenter에 집중하는 등 역할 분담을 통한 병렬 작업이 가능합니다. |
단점
| 단점 | 설명 |
|---|---|
| 보일러플레이트 코드 증가 | 모든 View에 대해 Presenter와 인터페이스를 만들어야 하므로, 화면이 많아질수록 코드의 양이 기하급수적으로 늘어날 수 있습니다. |
| View와 Presenter의 강한 1:1 의존성 | Presenter가 View의 인터페이스를 직접 참조하므로, View와 Presenter는 강한 1:1 관계를 맺습니다. 이는 유연성을 떨어뜨리는 요인이 될 수 있습니다. |
| 복잡도 증가 | 아주 간단한 기능을 구현할 때도 여러 클래스와 인터페이스를 만들어야 하므로, 작은 프로젝트에서는 오버헤드로 느껴질 수 있습니다. |
5. 다른 아키텍처 패턴과의 비교: MVP vs MVC vs MVVM
MVP는 다른 UI 아키텍처 패턴들과 어떤 차이점이 있을까요?
| 구분 | MVC (Model-View-Controller) | MVP (Model-View-Presenter) | MVVM (Model-View-ViewModel) |
|---|---|---|---|
| 핵심 중재자 | Controller | Presenter | ViewModel |
| View-Model 관계 | 직접 의존 (View가 Model을 관찰) | 완전 분리 (Presenter가 중재) | 완전 분리 (ViewModel이 중재) |
| View의 역할 | 데이터 표시, 사용자 입력 처리 | 수동적, Presenter의 지시만 따름 | 데이터 바인딩을 통해 ViewModel의 상태를 반영 |
| 중재자와 View의 관계 | 1:N (하나의 Controller가 여러 View 관리 가능) | 1:1 (하나의 Presenter가 하나의 View 관리) | 1:N (하나의 ViewModel이 여러 View에 데이터 제공 가능) |
| 테스트 용이성 | 상대적으로 어려움 (Controller가 플랫폼에 의존) | 높음 (Presenter가 플랫폼과 독립적) | 매우 높음 (ViewModel이 플랫폼과 독립적, 데이터 바인딩) |
| 주요 특징 | 고전적인 패턴 | View의 수동성, 명확한 책임 분리 | 데이터 바인딩, 생명주기 인식 |
-
MVP vs MVC: 가장 큰 차이는 View와 Model의 분리 여부입니다. MVP에서는 Presenter가 중간에서 모든 것을 처리하여 둘을 완벽히 분리하지만, MVC에서는 View가 Model을 직접 참조하여 결합도가 높습니다.
-
MVP vs MVVM: MVVM(Model-View-ViewModel)은 MVP에서 한 단계 더 발전한 패턴입니다. Presenter 대신 ViewModel이 그 역할을 하며, **데이터 바인딩(Data Binding)**을 통해 ViewModel의 데이터가 변경되면 View가 자동으로 갱신됩니다. 이로 인해 Presenter가
view.showUserName()처럼 직접 View를 조작하던 코드가 사라져, View와 ViewModel의 결합도가 MVP보다 더욱 낮아집니다.
6. 결론: MVP 패턴은 언제 사용해야 할까?
MVP 패턴은 안드로이드 아키텍처의 역사에서 매우 중요한 역할을 했습니다. 특히 MVC의 문제점이었던 테스트의 어려움과 강한 결합도를 해결하며, 현대적인 아키텍처 패턴으로 나아가는 징검다리가 되었습니다.
이런 경우 MVP 패턴을 고려해볼 수 있습니다:
-
단위 테스트가 매우 중요한 프로젝트일 경우: Presenter를 통한 로직 테스트는 MVP의 가장 강력한 무기입니다.
-
UI 로직과 비즈니스 로직을 명확하게 분리하고 싶을 때: 복잡한 화면과 상호작용을 가진 애플리케이션에서 코드의 구조를 깔끔하게 유지하고 싶을 때 적합합니다.
-
팀원들이 아키텍처 패턴에 익숙하지 않을 때: MVVM의 데이터 바인딩이나 MVI의 반응형 스트림보다 개념적으로 이해하기 쉬워, 패턴 도입의 첫걸음으로 좋은 선택이 될 수 있습니다.
물론, 최근 안드로이드 생태계에서는 Google이 공식적으로 권장하는 MVVM과 Jetpack 라이브러리(ViewModel, LiveData, Data Binding)의 조합이 대세로 자리 잡았습니다. 하지만 MVP 패턴의 핵심 사상인 ‘관심사 분리’와 ‘테스트 용이성’은 모든 현대 아키텍처의 근간을 이루고 있습니다. 따라서 MVP를 깊이 있게 이해하는 것은 더 나은 개발자로 성장하기 위한 훌륭한 자양분이 될 것입니다.