소프트웨어 디자인 패턴 - 2. MVP 패턴 (Model-View-Presenter)
2025. 1. 19. 23:52ㆍ소프트웨어/디자인 패턴
MVP 패턴이란?
MVP(Model-View-Presenter) 패턴은 애플리케이션을 Model, View, Presenter로 나누어 설계하는 소프트웨어 디자인 패턴입니다. MVC(Model-View-Controller) 패턴과 유사하지만, Presenter와 View가 1:1 관계를 가지며 Presenter가 View와 Model 사이의 모든 로직을 처리한다는 점에서 차이가 있습니다. 이 패턴은 UI 코드와 비즈니스 로직을 명확히 분리하여 유지보수성과 테스트 용이성을 향상시킵니다.
구성 요소
1. Model
- 데이터를 관리하고 저장.
- 데이터베이스와 상호작용하거나 상태를 유지.
- MVC의 Model과 동일한 역할을 수행.
2. View
- 사용자 인터페이스(UI)를 담당.
- Presenter로부터 데이터를 받아 화면에 표시.
- 사용자 입력을 Presenter에 전달.
- 비즈니스 로직 없이 UI 관련 작업에만 집중.
3. Presenter
- View와 Model 사이의 중재자 역할.
- 비즈니스 로직을 수행하고, Model에서 데이터를 가져와 View에 전달.
- View와 1:1로 연결되어 직접 데이터를 업데이트.
MVP의 특징
- View와 Presenter의 1:1 관계:
- 각 View는 하나의 Presenter와만 연결됩니다.
- View는 단순히 UI 역할만 수행:
- View는 데이터 처리 로직 없이 사용자 입력 및 UI 표시만 담당합니다.
- 테스트 용이성 증가:
- Presenter가 View와 분리되어 단위 테스트가 쉽습니다.
장점
- 유지보수 용이:
- View와 로직이 분리되어 수정이 다른 구성 요소에 영향을 미치지 않습니다.
- 테스트 용이성:
- Presenter가 View와 분리되어 단위 테스트가 쉽습니다.
- 높은 재사용성:
- 동일한 Presenter가 다양한 View와 결합될 수 있어 코드 재사용성이 증가합니다.
- UI 독립성:
- View를 변경해도 Presenter와 Model에는 영향을 미치지 않아 독립적으로 UI를 개선할 수 있습니다.
단점
- 복잡성 증가:
- MVC보다 더 많은 클래스와 파일이 필요하므로 코드베이스가 커질 수 있습니다.
- View와 Presenter 간 강한 결합:
- 1:1 관계로 인해 Presenter와 View의 재사용성이 제한될 수 있습니다.
- Presenter 비대화 문제:
- 시간이 지남에 따라 Presenter에 로직이 누적되어 비대해질 위험이 있습니다.
- 초기 학습 곡선:
- MVP 패턴에 익숙하지 않은 개발자는 각 구성 요소의 역할을 이해하는 데 시간이 걸릴 수 있습니다.
적용 예시
1. 계산기 프로그램
- Model: 덧셈, 뺄셈, 곱셈, 나눗셈 등의 연산 로직.
- View: 숫자와 결과를 화면에 표시하고 버튼 입력 처리.
- Presenter: 사용자의 입력을 Model로 전달하고, 연산 결과를 View에 전달.
2. 로그인 시스템
- Model: 사용자 인증 데이터 관리.
- View: 사용자 입력 필드(아이디/비밀번호)와 결과 메시지 표시.
- Presenter: 입력 데이터를 검증하고, Model과 상호작용하여 결과를 View에 전달.
3. 상품 관리 프로그램
- Model: 상품 데이터 저장 및 조회 로직.
- View: 상품 목록을 표시하고, 사용자 입력을 처리.
- Presenter: 사용자의 요청에 따라 Model을 업데이트하고, 결과를 View로 전달.
C++ 예제 코드: 간단한 계산기 프로그램
#include <iostream>
#include <stdexcept>
#include <limits> // numeric_limits 사용을 위해 필요
using namespace std;
// Model: 계산 로직 관리
class CalculatorModel {
public:
double add(double a, double b) {
return a + b;
}
double subtract(double a, double b) {
return a - b;
}
double multiply(double a, double b) {
return a * b;
}
double divide(double a, double b) {
if (b == 0) {
throw invalid_argument("Division by zero is not allowed.");
}
return a / b;
}
};
// View: 사용자 인터페이스 관리
class CalculatorView {
public:
void displayResult(double result) {
cout << "Result: " << result << endl;
}
void displayError(const string& message) {
cout << "Error: " << message << endl;
}
double getUserInput(const string& prompt) {
cout << prompt;
double input;
while (!(cin >> input)) {
cin.clear(); // 입력 오류 복구
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "Invalid input. Please enter a number: ";
}
cin.ignore(numeric_limits<streamsize>::max(), '\n');
return input;
}
char getUserOperation() {
clearScreen();
cout << "Enter operation (+, -, *, /): ";
char op;
while (!(cin >> op) || (op != '+' && op != '-' && op != '*' && op != '/')) {
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "Invalid operation. Please enter +, -, *, or /: ";
}
cin.ignore(numeric_limits<streamsize>::max(), '\n');
return op;
}
private:
void clearScreen() {
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}
};
// Presenter: 로직 처리 및 View와 Model 연결
class CalculatorPresenter {
private:
CalculatorModel& model;
CalculatorView& view;
public:
CalculatorPresenter(CalculatorModel& model, CalculatorView& view)
: model(model), view(view) {}
void performCalculation() {
view.clearScreen();
try {
double a = view.getUserInput("Enter first number: ");
double b = view.getUserInput("Enter second number: ");
char op = view.getUserOperation();
double result;
switch (op) {
case '+':
result = model.add(a, b);
break;
case '-':
result = model.subtract(a, b);
break;
case '*':
result = model.multiply(a, b);
break;
case '/':
result = model.divide(a, b);
break;
default:
throw invalid_argument("Invalid operation.");
}
view.displayResult(result);
} catch (const exception& e) {
view.displayError(e.what());
}
}
};
// Main: 애플리케이션 실행
int main() {
CalculatorModel model;
CalculatorView view;
CalculatorPresenter presenter(model, view);
bool continueCalculation = true;
while (continueCalculation) {
presenter.performCalculation();
cout << "Do you want to perform another calculation? (y/n): ";
char choice;
while (!(cin >> choice) || (tolower(choice) != 'y' && tolower(choice) != 'n')) {
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "Invalid input. Please enter 'y' or 'n': ";
}
cin.ignore(numeric_limits<streamsize>::max(), '\n');
continueCalculation = (tolower(choice) == 'y');
}
return 0;
}
코드 설명
Model
- 계산 로직(add, subtract, multiply, divide)을 관리.
View
- 사용자 입력을 받고 결과를 출력.
- Presenter로부터 데이터를 받아 사용자에게 표시.
Presenter
- 사용자 입력에 따라 Model과 상호작용.
- Model에서 계산 결과를 받아 View에 전달.
- 예외 처리를 통해 에러 메시지를 View에 전달.
실행 결과 예시
Enter first number: 10
Enter second number: 5
Enter operation (+, -, *, /): +
Result: 15
Do you want to perform another calculation? (y/n): y
Enter first number: 10
Enter second number: 0
Enter operation (+, -, *, /): /
Error: Division by zero is not allowed.
Do you want to perform another calculation? (y/n): n
MVP 패턴 요약
- View는 UI 작업만 수행하고 로직을 Presenter에 위임.
- Presenter는 비즈니스 로직을 처리하며 Model과 View를 연결.
- Model은 데이터를 관리하고 저장.
MVP 패턴은 UI와 로직을 철저히 분리해야 하는 애플리케이션에서 특히 적합하며, 테스트와 유지보수의 편리함을 제공합니다.
'소프트웨어 > 디자인 패턴' 카테고리의 다른 글
소프트웨어 디자인 패턴 - 5. PAC 패턴 (Presentation-Abstraction-Control) (0) | 2025.01.20 |
---|---|
소프트웨어 디자인 패턴 - 4. MVU 패턴 (Model-View-Update) (0) | 2025.01.20 |
소프트웨어 디자인 패턴 - 3. MVVM 패턴 (Model-View-ViewModel) (0) | 2025.01.20 |
소프트웨어 디자인 패턴 - 1. MVC 패턴 (Model-View-Controller) (0) | 2025.01.19 |
템플릿 메소드 (template method) (0) | 2024.07.13 |