# 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 생성 ```csharp 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 생성 ```csharp namespace DDD { public class MyView : IntegratedBaseUi { [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. 컨버터 사용 ```csharp [SerializeField, BindTo(nameof(MyViewModel.IsVisible), typeof(InvertBoolConverter))] private GameObject _hiddenPanel; [SerializeField, BindTo(nameof(MyViewModel.Count), typeof(ItemCountConverter))] private Text _formattedCountText; ``` ## 기존 InventoryView 변환 예시 ### 기존 코드 (InventoryView) ```csharp public class InventoryView : MonoBehaviour { private InventoryCategoryType _currentCategory = InventoryCategoryType.Food; public void UpdateCategoryView(InventoryCategoryType category) { _currentCategory = category; // 복잡한 UI 업데이트 로직... } } ``` ### MVVM 변환 후 **InventoryViewModel.cs** ```csharp 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** ```csharp namespace DDD.MVVM { public class InventoryView : AutoBindView { [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`: 소수를 백분율로 변환 ### 커스텀 컨버터 생성 ```csharp 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 분리 1. View의 상태 변수들을 ViewModel로 이동 2. 비즈니스 로직을 Service로 분리 3. UI 업데이트 로직을 바인딩으로 대체 #### 2단계: 자동 바인딩 적용 1. `AutoBindView` 상속 2. UI 요소에 `BindTo` Attribute 추가 3. 수동 UI 업데이트 코드 제거 #### 3단계: 최적화 및 리팩토링 1. 컨버터 활용으로 로직 단순화 2. 계산된 속성으로 중복 제거 3. 이벤트 시스템과 통합 이 MVVM 시스템을 통해 Unity UI 개발의 생산성과 유지보수성을 크게 향상시킬 수 있습니다.