MVC, MVP, MVVM 사용 목적 및 특징
- UI와 로직의 분리가 목적인 패턴들
- 불필요한 종속 관계를 줄일 수 있음
- SoC(Separation of Concerns, 관심사 분리) 측면
- Architecture Pattern(아키텍쳐 패턴)으로도 분류
- 잠재적 스파게티 코드 방지
- 신입 프로그래머 혹은 지망생들에게 당장 쓰게 될 패턴
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
MVC(Model View Controller)란?
- 소프트웨어의 논리적인 부분을 데이터와 프레젠테이션에서 분리, 불필요한 종속성과 스파게티 코드를 줄임
1. 모델
- 값을 보관하는 데이터 컨테이너
- 게임플레이 로직을 수행하거나 계산을 실행하지 않음
2. 뷰
- 데이터를 사용하는 표시
- 화면에 데이터의 그래픽 표현하는 인터페이스
3. 컨트롤러
- 입력 및 로직 처리
- 게임 데이터를 처리하고 런타임에 값이 어떻게 변하는지 계산
- 입력을 처리하고 결과를 모델로 다시 전송
한계
- 모델 데이터의 변경 사항을 수신하기 위한 뷰별 코드가 필요
예시코드
// Model
public class PlayerModel
{
public float Health { get; private set; }
public float MaxHealth { get; private set; }
public PlayerModel(float maxHealth)
{
MaxHealth = maxHealth;
Health = maxHealth;
}
public void SetHealth(float value)
{
Health = Mathf.Clamp(value, 0, MaxHealth);
}
}
// View
public class PlayerHealthView : MonoBehaviour
{
[SerializeField] private Image healthBar;
public void UpdateHealthBar(float currentHealth, float maxHealth)
{
healthBar.fillAmount = currentHealth / maxHealth;
}
}
// Controller
public class PlayerController : MonoBehaviour
{
private PlayerModel model;
private PlayerHealthView view;
private void Start()
{
model = new PlayerModel(100f);
view = GetComponent<PlayerHealthView>();
}
public void TakeDamage(float damage)
{
model.SetHealth(model.Health - damage);
view.UpdateHealthBar(model.Health, model.MaxHealth);
}
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
MVP(Model View Presenter)
1. 모델
- 데이터와 데이터를 관리하는 규칙
- 게임의 현재 상태, 게임 속성, 데이터에 대한 Logic
- Model은 View에 대한 정보가 없음
2. Presenter
- Model과 View 사이에서 중재
- View에서 사용자 입력 이벤트 처리
- 게임 조건이 변경되면 Model을 업데이트하고, View를 업데이트
3. View
- 사용자에게 데이터를 표시하는 애플리케이션의 UI
- 사용자 상호작용을 Presenter 전송
- MVP패턴의 View는 Model과 직접 상호작용 X
- UI 로직에 대한 별도의 스크립트가 있을 수 있지만, 게임의 비즈니스 로직을 처리하지 X
장점
1. 확장성
- 특정 작업을 처리하는 부분들이 나뉘어 있으면 코드베이스를 관리 및 확장 용이
2. 모듈성
- 한 구성의 변경 사항이 전체 시스템에 영향 X
- 각 구성 요소는 독립적으로 개발, 테스트 및 수정 가능
- 코드 디버깅이 용이
3. 재사용성 및 유지 관리성
- 코드의 일부는 애플리케이션의 다른 부분에서 재사용 가능
- 스파게티 코드가 될 확률이 적음
예시코드
// Model
public class PlayerStats
{
public float Health { get; set; }
public float MaxHealth { get; }
public PlayerStats(float maxHealth)
{
MaxHealth = maxHealth;
Health = maxHealth;
}
}
// View Interface
public interface IPlayerHealthView
{
void SetHealthBarFill(float fillAmount);
void ShowDamageEffect();
}
// Concrete View
public class PlayerHealthView : MonoBehaviour, IPlayerHealthView
{
[SerializeField] private Image healthBar;
public void SetHealthBarFill(float fillAmount)
{
healthBar.fillAmount = fillAmount;
}
public void ShowDamageEffect()
{
// 데미지 효과 표시 로직
}
}
// Presenter
public class PlayerHealthPresenter
{
private readonly PlayerStats model;
private readonly IPlayerHealthView view;
public PlayerHealthPresenter(PlayerStats model, IPlayerHealthView view)
{
this.model = model;
this.view = view;
}
public void OnDamageTaken(float damage)
{
model.Health = Mathf.Max(0, model.Health - damage);
view.SetHealthBarFill(model.Health / model.MaxHealth);
view.ShowDamageEffect();
}
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
MVVM(Model View ViewModel)
- 기존 MVP에서 View와 Controller/Presenter 의존성을 약화
1. Model
- 비즈니스 로직에서 사용되는 데이터를 포함한 데이터 구조
- 어떤 객체라도 가능: 스크립터블오브젝트, MonoBegaviour
2. View
- UI Toolkit에서는 USS 스타일 시트와 UXML 파일로 구성
- 일반적으로 UI Element로 구성
- 사용자에게 데이터를 표시하고 상호작용하는 UI
3. ViewModel
- View와 Model 사이의 중재자 역할
- 사용자가 View와 상호작용할때 Model을 업데이트 하는 역할
- ViewModel과 View는 데이터 바인딩 기능을 통해 연결
특징
1. 데이터 바인딩(핵심)
- 뷰와 뷰 모델간의 데이터 바인딩을 통해 뷰모델 속성 변경이 자동으로 뷰에 반영
2. 커멘드
- 뷰 모델에서 UI상호작용을 처리하는 커맨드 패턴을 사용
3. 관심사 분리(SoC)
- 뷰모델은 뷰에 대한 참조를 가지지 않으므로 테스트가 용이
// Model
public class PlayerData
{
public float Health { get; set; }
public float MaxHealth { get; }
public PlayerData(float maxHealth)
{
MaxHealth = maxHealth;
Health = maxHealth;
}
}
// ViewModel
public class PlayerHealthViewModel : INotifyPropertyChanged
{
private readonly PlayerData model;
private float healthPercentage;
public event PropertyChangedEventHandler PropertyChanged;
public float HealthPercentage
{
get => healthPercentage;
private set
{
healthPercentage = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(HealthPercentage)));
}
}
public PlayerHealthViewModel(PlayerData model)
{
this.model = model;
UpdateHealthPercentage();
}
public void TakeDamage(float damage)
{
model.Health = Mathf.Max(0, model.Health - damage);
UpdateHealthPercentage();
}
private void UpdateHealthPercentage()
{
HealthPercentage = model.Health / model.MaxHealth;
}
}
// View (UXML)
/*
<ui:UXML xmlns:ui="UnityEngine.UIElements">
<ui:ProgressBar name="healthBar" />
</ui:UXML>
*/
// View Code
public class PlayerHealthView : MonoBehaviour
{
private PlayerHealthViewModel viewModel;
private VisualElement root;
private ProgressBar healthBar;
private void OnEnable()
{
var playerData = new PlayerData(100f);
viewModel = new PlayerHealthViewModel(playerData);
root = GetComponent<UIDocument>().rootVisualElement;
healthBar = root.Q<ProgressBar>("healthBar");
// 데이터 바인딩 설정
viewModel.PropertyChanged += (sender, args) =>
{
if (args.PropertyName == nameof(PlayerHealthViewModel.HealthPercentage))
{
healthBar.value = viewModel.HealthPercentage;
}
};
}
}
Data Binding
- UI가 아닌 객체 속성과 UI 요소 간의 동기화 보장
- Unity6의 UI툴킷은 이제 런타임 데이터 바인딩을 지원
'유니티 > 패턴, 코드 및 이론 정리' 카테고리의 다른 글
[간단 정리] 가비지 컬랙션 (0) | 2025.01.22 |
---|---|
[간단 정리] 다국어 처리 (0) | 2025.01.22 |
[간단 정리] JSON (0) | 2025.01.22 |
[코드] 유한 상태 머신 (0) | 2025.01.11 |