본문 바로가기
코딩취미/Python

MVC (Model-View-Controller)와 MVVM (Model-View-ViewModel)과 데이터 바인딩(Data Binding)

by 브링블링 2024. 7. 23.
반응형

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)

  1. Model (모델):
    • 애플리케이션의 데이터 구조를 정의하고 데이터를 관리합니다.
    • 데이터베이스나 API와 상호작용하여 데이터를 가져오고 저장하는 역할을 합니다.
  2. View (뷰):
    • 사용자에게 정보를 표시하는 부분입니다.
    • 모델에서 데이터를 받아 화면에 렌더링합니다.
  3. Controller (컨트롤러):
    • 사용자 입력을 처리하고 모델과 뷰를 연결합니다.
    • 모델에서 데이터를 가져와서 뷰에 전달하고, 뷰의 사용자 입력을 모델에 전달합니다.

* 주로 사용되는 분야

  • 웹 애플리케이션: 서버사이드 렌더링 웹 애플리케이션 (예: Django, Ruby on Rails)
  • 데스크탑 애플리케이션: GUI 애플리케이션 (예: Java Swing, .NET Windows Forms)
  • 모바일 애플리케이션: 기본적인 모바일 앱 구조

2. MVVM (Model-View-ViewModel)

  1. Model (모델):
    • 애플리케이션의 데이터 구조를 정의하고 데이터를 관리합니다.
    • 데이터베이스나 API와 상호작용하여 데이터를 가져오고 저장하는 역할을 합니다.
  2. View (뷰):
    • 사용자에게 정보를 표시하는 부분입니다.
    • 뷰모델과 데이터를 바인딩하여 화면에 렌더링합니다.
  3. 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를 관리하는 데 유용하며, 코드의 양을 줄이고 유지보수를 용이하게 합니다.

데이터 바인딩의 장점

  1. 자동 동기화: 데이터 모델과 UI 간의 동기화를 자동으로 처리하여 수동 업데이트의 필요성을 줄입니다.
  2. 코드 간소화: 데이터 업데이트 로직을 간소화하여 코드를 더 깔끔하고 관리하기 쉽게 만듭니다.
  3. 유지보수성 향상: 모델과 뷰를 분리하여 코드의 유지보수성을 높입니다.
  4. 양방향 바인딩: 뷰의 변경 사항이 모델에, 모델의 변경 사항이 뷰에 자동으로 반영됩니다.

데이터 바인딩의 예시

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_())

 

 

  1. 초기 설정:
    • DataModel 클래스는 데이터를 저장하고, 변경 사항이 있을 때 관찰자(Observer)들에게 알립니다.
    • ViewModel 클래스는 데이터 모델과 뷰 사이의 중개 역할을 하며, 데이터 변경을 처리합니다.
    • View 클래스는 사용자 인터페이스를 정의하고, 사용자 입력을 받아 처리합니다.
  2. 사용자 상호작용:
    • 사용자가 QLineEdit 위젯에 값을 입력하고 버튼을 클릭하면, View 클래스의 on_button_clicked 메서드가 호출됩니다.
    • 이 메서드는 ViewModel의 update_model 메서드를 호출하여 모델의 데이터를 업데이트합니다.
  3. 데이터 동기화:
    • 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을 연결합니다.

 

 

반응형