MVC (Model-View-Controller)와 MVVM (Model-View-ViewModel)과 데이터 바인딩(Data Binding)
MVC와 MVVM은 각각의 패턴이 적합한 분야에서 사용되어 코드의 구조를 명확히 하고 유지보수성을 높이는 데 기여합니다. MVC는 전통적인 서버사이드 웹 애플리케이션 및 데스크탑 애플리케이션에 널리 사용되며, MVVM은 데이터 바인딩을 통해 복잡한 UI를 더 쉽게 관리할 수 있어 현대적인 프론트엔드 개발에 자주 사용됩니다.
* MVC와 MVVM의 차이점
특징 | MVC | MVVM |
구성 요소 | Model, View, Controller | Model, View, ViewModel |
주된 역할 | Controller가 중개 역할 | ViewModel이 중개 역할 |
데이터 바인딩 | 데이터 바인딩이 일반적으로 사용되지 않음 | 데이터 바인딩을 사용하여 뷰와 뷰모델 간의 양방향 데이터 흐름 지원 |
컴포넌트의 결합도 | 컨트롤러와 뷰의 결합도가 비교적 높음 | 뷰와 뷰모델의 결합도가 낮아 독립적으로 테스트 가능 |
코드의 가독성 | 상대적으로 낮음 | 상대적으로 높음 |
단위 테스트 용이성 | 상대적으로 낮음 | 상대적으로 높음 |
주로 사용되는 분야 | 서버사이드 웹 애플리케이션, 기본 모바일 및 데스크탑 애플리케이션 | 클라이언트 사이드 웹 애플리케이션, 현대적 모바일 및 데스크탑 애플리케이션 |
1. MVC (Model-View-Controller)
- Model (모델):
- 애플리케이션의 데이터 구조를 정의하고 데이터를 관리합니다.
- 데이터베이스나 API와 상호작용하여 데이터를 가져오고 저장하는 역할을 합니다.
- View (뷰):
- 사용자에게 정보를 표시하는 부분입니다.
- 모델에서 데이터를 받아 화면에 렌더링합니다.
- Controller (컨트롤러):
- 사용자 입력을 처리하고 모델과 뷰를 연결합니다.
- 모델에서 데이터를 가져와서 뷰에 전달하고, 뷰의 사용자 입력을 모델에 전달합니다.
* 주로 사용되는 분야
- 웹 애플리케이션: 서버사이드 렌더링 웹 애플리케이션 (예: Django, Ruby on Rails)
- 데스크탑 애플리케이션: GUI 애플리케이션 (예: Java Swing, .NET Windows Forms)
- 모바일 애플리케이션: 기본적인 모바일 앱 구조
2. MVVM (Model-View-ViewModel)
- Model (모델):
- 애플리케이션의 데이터 구조를 정의하고 데이터를 관리합니다.
- 데이터베이스나 API와 상호작용하여 데이터를 가져오고 저장하는 역할을 합니다.
- View (뷰):
- 사용자에게 정보를 표시하는 부분입니다.
- 뷰모델과 데이터를 바인딩하여 화면에 렌더링합니다.
- ViewModel (뷰모델):
- 뷰와 모델 사이의 중개자 역할을 합니다.
- 모델에서 데이터를 가져와서 뷰가 필요로 하는 형식으로 변환하고, 뷰의 데이터를 모델에 전달합니다.
- 뷰의 상태와 동작을 관리하며, 데이터 바인딩을 통해 뷰와 상호작용합니다.
* 주로 사용되는 분야
- 데스크탑 애플리케이션: WPF (Windows Presentation Foundation), UWP (Universal Windows Platform)
- 모바일 애플리케이션: Xamarin, Android (Jetpack Compose), iOS (SwiftUI)
- 웹 애플리케이션: 클라이언트 사이드 프레임워크 (예: Angular, Vue.js)
패턴 | 주요 사용 분야 | 예시 프레임워크/기술 |
MVC | 서버사이드 웹 애플리케이션, 데스크탑 애플리케이션, 기본 모바일 앱 | Django, Ruby on Rails, Java Swing, .NET Windows Forms |
MVVM | 클라이언트 사이드 웹 애플리케이션, 현대적 모바일 및 데스크탑 애플리케이션 | Angular, Vue.js, WPF, UWP, Xamarin, Android (Jetpack Compose), iOS (SwiftUI) |
3. 데이터 바인딩 (Data-Binding)
데이터 바인딩(Data Binding)은 애플리케이션의 UI 요소(View)와 데이터 소스(Model) 사이의 동기화를 자동으로 유지하는 기술입니다. 데이터 바인딩을 사용하면 데이터 모델의 변경 사항이 UI에 자동으로 반영되고, UI의 변경 사항이 데이터 모델에 자동으로 업데이트될 수 있습니다. 이는 특히 복잡한 UI를 관리하는 데 유용하며, 코드의 양을 줄이고 유지보수를 용이하게 합니다.
데이터 바인딩의 장점
- 자동 동기화: 데이터 모델과 UI 간의 동기화를 자동으로 처리하여 수동 업데이트의 필요성을 줄입니다.
- 코드 간소화: 데이터 업데이트 로직을 간소화하여 코드를 더 깔끔하고 관리하기 쉽게 만듭니다.
- 유지보수성 향상: 모델과 뷰를 분리하여 코드의 유지보수성을 높입니다.
- 양방향 바인딩: 뷰의 변경 사항이 모델에, 모델의 변경 사항이 뷰에 자동으로 반영됩니다.
데이터 바인딩의 예시
1. Python : PyQt는 기본적으로 데이터 바인딩을 지원하지 않지만, 이를 유사하게 구현하는 과정을 정리합니다.
- 기본 데이터 모델 만들기(M) : 간단한 데이터 모델 클래스를 정의합니다.
class DataModel:
def __init__(self, initial_value=""):
self._data = initial_value
self._observers = []
def get_data(self):
return self._data
def set_data(self, value):
self._data = value
self._notify_observers()
def add_observer(self, observer):
self._observers.append(observer)
def _notify_observers(self):
for observer in self._observers:
observer()
- 뷰 모델 만들기(VM) : 뷰 모델은 데이터 모델과 뷰 사이의 중개 역할을 합니다.
from PyQt5.QtCore import QObject, pyqtSlot
class ViewModel(QObject):
def __init__(self, model):
super().__init__()
self.model = model
self.model.add_observer(self.on_model_changed)
@pyqtSlot(str)
def update_model(self, value):
self.model.set_data(value)
def on_model_changed(self):
print(f"Model data changed: {self.model.get_data()}")
- 뷰 만들기(V): 뷰는 사용자 인터페이스를 정의합니다. 여기서는 간단한 입력 창과 버튼을 사용합니다.
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLineEdit, QPushButton
class View(QWidget):
def __init__(self, view_model):
super().__init__()
self.view_model = view_model
self.initUI()
def initUI(self):
self.layout = QVBoxLayout()
self.line_edit = QLineEdit(self)
self.layout.addWidget(self.line_edit)
self.button = QPushButton("Update Model", self)
self.layout.addWidget(self.button)
self.setLayout(self.layout)
self.button.clicked.connect(self.on_button_clicked)
def on_button_clicked(self):
new_value = self.line_edit.text()
self.view_model.update_model(new_value)
- 메인 애플리케이션 만들기 : 메인 애플리케이션을 정의하고 실행합니다.
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
model = DataModel()
view_model = ViewModel(model)
view = View(view_model)
view.setWindowTitle('Data Binding Example')
view.setGeometry(300, 300, 400, 200)
view.show()
sys.exit(app.exec_())
- 초기 설정:
- DataModel 클래스는 데이터를 저장하고, 변경 사항이 있을 때 관찰자(Observer)들에게 알립니다.
- ViewModel 클래스는 데이터 모델과 뷰 사이의 중개 역할을 하며, 데이터 변경을 처리합니다.
- View 클래스는 사용자 인터페이스를 정의하고, 사용자 입력을 받아 처리합니다.
- 사용자 상호작용:
- 사용자가 QLineEdit 위젯에 값을 입력하고 버튼을 클릭하면, View 클래스의 on_button_clicked 메서드가 호출됩니다.
- 이 메서드는 ViewModel의 update_model 메서드를 호출하여 모델의 데이터를 업데이트합니다.
- 데이터 동기화:
- ViewModel의 update_model 메서드는 DataModel의 set_data 메서드를 호출하여 데이터를 설정합니다.
- DataModel의 set_data 메서드는 데이터 변경 사항을 모든 관찰자(뷰 모델)에게 알립니다.
- 관찰자인 ViewModel의 on_model_changed 메서드는 데이터 변경 사항을 처리하여, 뷰를 업데이트하거나 다른 동작을 수행할 수 있습니다.
2. Angular : Angular는 강력한 데이터 바인딩 기능을 제공합니다. Angular의 데이터 바인딩은 주로 {{}} (Interpolation), [] (Property Binding), () (Event Binding), [()] (Two-way Binding)을 사용합니다.
- app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Angular Data Binding Example';
inputValue: string = '';
updateValue(event: any): void {
this.inputValue = event.target.value;
}
}
- app.component.html
<div>
<h1>{{ title }}</h1>
<input type="text" [(ngModel)]="inputValue" (input)="updateValue($event)">
<p>You entered: {{ inputValue }}</p>
</div>
- Interpolation: {{ title }}는 title 변수를 뷰에 표시합니다.
- Two-way Binding: [(ngModel)]="inputValue"는 inputValue와 입력 필드 간의 양방향 바인딩을 설정합니다.
- Event Binding: (input)="updateValue($event)"는 입력 필드에서 입력 이벤트가 발생할 때 updateValue 메서드를 호출합니다.
3. Vue.js : Vue.js는 직관적인 데이터 바인딩을 제공합니다. Vue의 데이터 바인딩은 주로 {{}} (Interpolation)과 v-model (Two-way Binding)을 사용합니다.
- index.html
<!DOCTYPE html>
<html>
<head>
<title>Vue.js Data Binding Example</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
</head>
<body>
<div id="app">
<h1>{{ title }}</h1>
<input type="text" v-model="inputValue">
<p>You entered: {{ inputValue }}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
title: 'Vue.js Data Binding Example',
inputValue: ''
}
});
</script>
</body>
</html>
- Interpolation: {{ title }}는 title 변수를 뷰에 표시합니다.
- Two-way Binding: v-model="inputValue"는 inputValue와 입력 필드 간의 양방향 바인딩을 설정합니다.
4. WPF (Windows Presentation Foundation) : WPF는 .NET을 기반으로 하는 강력한 데스크탑 애플리케이션 프레임워크로, XAML을 사용하여 UI를 정의하고, 데이터 바인딩을 쉽게 설정할 수 있습니다.
- MainWindow.xaml
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WPF Data Binding Example" Height="200" Width="400">
<Grid>
<StackPanel>
<TextBlock Text="{Binding Title}" FontSize="24" Margin="10"/>
<TextBox Text="{Binding InputValue, UpdateSourceTrigger=PropertyChanged}" Margin="10"/>
<TextBlock Text="{Binding InputValue}" FontSize="18" Margin="10"/>
</StackPanel>
</Grid>
</Window>
- app.component.html
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApp
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string inputValue;
public string Title { get; set; } = "WPF Data Binding Example";
public string InputValue
{
get { return inputValue; }
set
{
inputValue = value;
OnPropertyChanged();
}
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
- TextBlock: Text="{Binding Title}"는 Title 속성을 바인딩하여 뷰에 표시합니다.
- TextBox: Text="{Binding InputValue, UpdateSourceTrigger=PropertyChanged}"는 InputValue 속성과 양방향 바인딩을 설정합니다.
- INotifyPropertyChanged: 속성 변경을 알리기 위해 INotifyPropertyChanged 인터페이스를 구현합니다.
5. Android (Data Binding Library) : Android의 데이터 바인딩 라이브러리를 사용하면 XML 레이아웃 파일에서 직접 데이터 바인딩을 설정할 수 있습니다.
- build.gradle
android {
...
viewBinding {
enabled = true
}
}
- activity_main.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.example.app.MainViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.title}" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={viewModel.inputValue}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.inputValue}" />
</LinearLayout>
</layout>
- MainViewModel.java
package com.example.app;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
public class MainViewModel extends ViewModel {
private MutableLiveData<String> inputValue = new MutableLiveData<>("");
public LiveData<String> getInputValue() {
return inputValue;
}
public void setInputValue(String value) {
inputValue.setValue(value);
}
public String getTitle() {
return "Android Data Binding Example";
}
}
- MainActivity.java
package com.example.app;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import com.example.app.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private MainViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(MainViewModel.class);
binding = ActivityMainBinding.inflate(getLayoutInflater());
binding.setViewModel(viewModel);
binding.setLifecycleOwner(this);
setContentView(binding.getRoot());
}
}
- XML Layout: <layout> 태그 내에서 데이터 바인딩을 설정하고, variable을 정의하여 ViewModel을 바인딩합니다.
- ViewModel: LiveData를 사용하여 데이터 변화를 감지하고, 뷰에 반영합니다.
- MainActivity: ActivityMainBinding을 사용하여 뷰와 ViewModel을 연결합니다.
'코딩취미 > Python' 카테고리의 다른 글
QTableWidget 내부에 QLineEdit 와 QComboBox 추가해서 이벤트 처리하기 (0) | 2024.08.09 |
---|---|
레이아웃(Layout) 및 위젯(Widget)간의 간격설정 방법 : setSpacing, setContentsMargins, QSpacerItem (0) | 2024.07.30 |
파이썬 MVC 패턴 프로젝트 구성 및 예시 (0) | 2024.07.22 |
에디터박스(QEdit)에서 최소값, 최대값 유효성 체크와 값 입력받기 (0) | 2024.07.19 |
음력일과 D-Day 계산하는 코드 만들기 + 날짜/시간 확인 (0) | 2024.07.15 |