7.3 KiB
7.3 KiB
Unity MVVM 시스템 가이드
Unity에서 MVVM(Model-View-ViewModel) 패턴을 구현한 시스템입니다. Attribute 기반 데이터 바인딩과 nameof
를 활용한 타입 안전한 구조를 제공합니다.
폴더 구조
GameUi/New/
├── ViewModels/ # ViewModel 클래스들
│ ├── Base/ # 기본 ViewModel 클래스
│ │ └── SimpleViewModel.cs
│ └── InventoryViewModel.cs # 예시 ViewModel
├── Views/ # View 클래스들
│ └── Base/
│ └── AutoBindView.cs # 자동 바인딩 View 기본 클래스
├── Services/ # 서비스 계층 클래스들
│ ├── IService.cs # 서비스 인터페이스
│ └── InventoryService.cs # 예시 서비스
├── Utils/ # 유틸리티 클래스들
│ ├── BindToAttribute.cs # 바인딩 Attribute
│ └── BindingContext.cs # 바인딩 컨텍스트
└── Converters/ # 값 변환기들
├── IValueConverter.cs # 컨버터 인터페이스
└── CommonConverters.cs # 공통 컨버터들
핵심 클래스
1. SimpleViewModel
- ViewModel의 기본 클래스
INotifyPropertyChanged
구현- 속성 변경 알림 자동 처리
- 배치 업데이트 지원
2. IntegratedBaseUi
- UI의 기본 클래스 (BaseUi + MVVM 기능 통합)
- Attribute 기반 자동 바인딩
- ViewModel과 UI 요소 자동 연결
3. BindToAttribute
- UI 요소를 ViewModel 속성에 바인딩
nameof()
사용으로 타입 안전성 보장- 컨버터 지원
사용법
1. ViewModel 생성
namespace DDD.MVVM
{
public class MyViewModel : SimpleViewModel
{
private string _title = "기본 제목";
private int _count = 0;
private bool _isVisible = true;
public string Title
{
get => _title;
set => SetField(ref _title, value);
}
public int Count
{
get => _count;
set => SetField(ref _count, value);
}
public bool IsVisible
{
get => _isVisible;
set => SetField(ref _isVisible, value);
}
public string CountText => $"개수: {Count}";
public void IncrementCount()
{
Count++;
OnPropertyChanged(nameof(CountText));
}
}
}
2. View 생성
namespace DDD
{
public class MyView : IntegratedBaseUi<MyViewModel>
{
[SerializeField, BindTo(nameof(MyViewModel.Title))]
private Text _titleText;
[SerializeField, BindTo(nameof(MyViewModel.CountText))]
private Text _countText;
[SerializeField, BindTo(nameof(MyViewModel.IsVisible))]
private GameObject _panel;
// UI 이벤트 핸들러
public void OnIncrementButtonClicked()
{
ViewModel.IncrementCount();
}
public void OnToggleVisibilityClicked()
{
ViewModel.IsVisible = !ViewModel.IsVisible;
}
}
}
3. 컨버터 사용
[SerializeField, BindTo(nameof(MyViewModel.IsVisible), typeof(InvertBoolConverter))]
private GameObject _hiddenPanel;
[SerializeField, BindTo(nameof(MyViewModel.Count), typeof(ItemCountConverter))]
private Text _formattedCountText;
기존 InventoryView 변환 예시
기존 코드 (InventoryView)
public class InventoryView : MonoBehaviour
{
private InventoryCategoryType _currentCategory = InventoryCategoryType.Food;
public void UpdateCategoryView(InventoryCategoryType category)
{
_currentCategory = category;
// 복잡한 UI 업데이트 로직...
}
}
MVVM 변환 후
InventoryViewModel.cs
namespace DDD.MVVM
{
public class InventoryViewModel : SimpleViewModel
{
private InventoryCategoryType _currentCategory = InventoryCategoryType.Food;
public InventoryCategoryType CurrentCategory
{
get => _currentCategory;
set => SetField(ref _currentCategory, value);
}
public string CategoryDisplayText => CurrentCategory switch
{
InventoryCategoryType.Food => "음식",
InventoryCategoryType.Drink => "음료",
_ => "전체"
};
public void SetCategory(InventoryCategoryType category)
{
CurrentCategory = category;
OnPropertyChanged(nameof(CategoryDisplayText));
}
}
}
InventoryView.cs
namespace DDD.MVVM
{
public class InventoryView : AutoBindView<InventoryViewModel>
{
[SerializeField, BindTo(nameof(InventoryViewModel.CategoryDisplayText))]
private Text _categoryLabel;
public void OnCategoryButtonClicked(int categoryIndex)
{
ViewModel.SetCategory((InventoryCategoryType)categoryIndex);
}
}
}
장점
1. 타입 안전성
nameof()
사용으로 컴파일 타임 검증- 속성명 변경 시 자동 업데이트
- IDE IntelliSense 지원
2. Unity 통합성
- Inspector에서 바인딩 확인
- MonoBehaviour 패턴 유지
- 기존 Unity 워크플로우와 호환
3. 유지보수성
- View와 비즈니스 로직 분리
- 테스트 가능한 ViewModel
- 재사용 가능한 컴포넌트
4. 개발 생산성
- 자동 바인딩으로 보일러플레이트 코드 감소
- 데이터 변경 시 UI 자동 업데이트
- 일관된 개발 패턴
컨버터 목록
기본 컨버터들
InvertBoolConverter
: 불린 값 반전ItemCountConverter
: 숫자를 "아이템 수: N" 형식으로 변환InventoryCategoryConverter
: 카테고리 열거형을 한국어로 변환CurrencyConverter
: 숫자를 통화 형식으로 변환PercentageConverter
: 소수를 백분율로 변환
커스텀 컨버터 생성
public class CustomConverter : IValueConverter
{
public object Convert(object value)
{
// 변환 로직 구현
return convertedValue;
}
public object ConvertBack(object value)
{
// 역변환 로직 구현 (선택사항)
return originalValue;
}
}
베스트 프랙티스
1. ViewModel 설계
- 단일 책임 원칙 준수
- UI 관련 Unity API 직접 사용 금지
- 계산된 속성 적극 활용
- 이벤트를 통한 느슨한 결합
2. 바인딩 설정
nameof()
사용 필수- 적절한 컨버터 활용
- 복잡한 로직은 ViewModel에서 처리
3. 성능 고려사항
- 배치 업데이트 활용
- 불필요한 PropertyChanged 이벤트 방지
- 컬렉션 변경 시 효율적인 업데이트
마이그레이션 가이드
단계별 적용 방법
1단계: 기존 View에서 ViewModel 분리
- View의 상태 변수들을 ViewModel로 이동
- 비즈니스 로직을 Service로 분리
- UI 업데이트 로직을 바인딩으로 대체
2단계: 자동 바인딩 적용
AutoBindView<TViewModel>
상속- UI 요소에
BindTo
Attribute 추가 - 수동 UI 업데이트 코드 제거
3단계: 최적화 및 리팩토링
- 컨버터 활용으로 로직 단순화
- 계산된 속성으로 중복 제거
- 이벤트 시스템과 통합
이 MVVM 시스템을 통해 Unity UI 개발의 생산성과 유지보수성을 크게 향상시킬 수 있습니다.