# 기존 UI 시스템을 MVVM으로 마이그레이션 가이드 Unity 프로젝트에서 기존 UI 시스템(BaseUi, BasePopupUi, PopupUi)을 MVVM 패턴으로 점진적으로 전환하는 방법을 설명합니다. ## 호환성 보장 ### 1. 기존 UI 시스템은 그대로 유지됩니다 - `BaseUi`, `BasePopupUi`, `PopupUi` 클래스들은 변경되지 않음 - 기존 UI들은 계속해서 정상 동작 - `UiManager`와 `PopupUiState`도 기존 방식 그대로 지원 ### 2. 새로운 Integrated 클래스들 사용 - `IntegratedBaseUi` : BaseUi + MVVM 기능 통합 - `IntegratedBasePopupUi` : BasePopupUi + MVVM 기능 통합 - `IntegratedPopupUi` : PopupUi + MVVM 기능 통합 ## 마이그레이션 전략 ### 단계 1: 점진적 전환 계획 수립 1. **우선순위 결정** ``` 높음: 복잡한 상태 관리가 필요한 UI (InventoryView, ShopView 등) 중간: 중간 규모의 팝업 UI 낮음: 간단한 확인/알림 다이얼로그 ``` 2. **전환 범위 설정** - 한 번에 하나의 UI씩 전환 - 관련된 ViewModel과 Service 함께 구현 - 철저한 테스트 후 다음 UI 진행 ### 단계 2: ViewModel 및 Service 구현 #### 기존 View 분석 ```csharp // 기존 InventoryView public class InventoryView : MonoBehaviour { private InventoryCategoryType _currentCategory; private List _items; public void UpdateCategoryView(InventoryCategoryType category) { _currentCategory = category; // 복잡한 UI 업데이트 로직 } } ``` #### ViewModel로 분리 ```csharp // 새로운 InventoryViewModel public class InventoryViewModel : SimpleViewModel { private InventoryCategoryType _currentCategory; private List _visibleItems; public InventoryCategoryType CurrentCategory { get => _currentCategory; set => SetField(ref _currentCategory, value); } public void SetCategory(InventoryCategoryType category) { CurrentCategory = category; UpdateVisibleItems(); // 비즈니스 로직 } } ``` #### Service 계층 분리 ```csharp // 비즈니스 로직을 Service로 분리 public class InventoryService : IUiService { public IEnumerable FilterItems(InventoryCategoryType category) { // 복잡한 필터링 로직 } } ``` ### 단계 3: View를 MVVM으로 전환 #### 기존 View 코드 ```csharp public class InventoryView : MonoBehaviour, IEventHandler { [SerializeField] private Transform _slotParent; [SerializeField] private Text _categoryLabel; // 많은 상태 변수들과 복잡한 로직들... } ``` #### MVVM View로 전환 ```csharp public class MvvmInventoryView : MvvmBasePopupUi { [SerializeField] private Transform _slotParent; [SerializeField] private Text _categoryLabel; protected override void SetupBindings() { BindText(_categoryLabel, nameof(InventoryViewModel.CategoryDisplayText)); // 간단한 바인딩 설정만 } public void OnCategoryButtonClicked(int categoryIndex) { ViewModel.SetCategory((InventoryCategoryType)categoryIndex); } } ``` ### 단계 4: 마이그레이션 체크리스트 #### ViewModel 체크리스트 - [ ] SimpleViewModel 상속 - [ ] 모든 상태를 속성으로 변환 - [ ] SetField 사용한 PropertyChanged 알림 - [ ] 계산된 속성 구현 - [ ] 비즈니스 로직을 Service로 위임 #### View 체크리스트 - [ ] 적절한 MVVM Base 클래스 상속 - [ ] SetupBindings() 구현 - [ ] UI 이벤트를 ViewModel 메서드 호출로 변경 - [ ] 직접적인 UI 업데이트 코드 제거 - [ ] HandleCustomPropertyChanged에서 복잡한 UI 처리 #### Service 체크리스트 - [ ] IService 인터페이스 구현 - [ ] 복잡한 비즈니스 로직 포함 - [ ] 데이터 접근 로직 캡슐화 - [ ] 테스트 가능한 구조 ## 구체적인 마이그레이션 예시 ### 예시 1: 간단한 팝업 UI #### Before (기존) ```csharp public class SimpleDialogUi : BasePopupUi { [SerializeField] private Text _messageText; [SerializeField] private Button _confirmButton; public void ShowMessage(string message) { _messageText.text = message; _confirmButton.onClick.AddListener(Close); } } ``` #### After (MVVM) ```csharp // ViewModel public class DialogViewModel : SimpleViewModel { private string _message; public string Message { get => _message; set => SetField(ref _message, value); } } // View public class MvvmSimpleDialogUi : MvvmBasePopupUi { [SerializeField] private Text _messageText; [SerializeField] private Button _confirmButton; protected override void SetupBindings() { BindText(_messageText, nameof(DialogViewModel.Message)); } protected override void Awake() { base.Awake(); _confirmButton.onClick.AddListener(() => Close()); } } ``` ### 예시 2: 복잡한 목록 UI #### Before (기존 InventoryView) ```csharp public class InventoryView : MonoBehaviour { private InventoryCategoryType _currentCategory; private Dictionary _slotLookup = new(); public void UpdateCategoryView(InventoryCategoryType category) { _currentCategory = category; // 복잡한 필터링 로직 foreach (var slot in _slotLookup.Values) { bool shouldShow = MatchesCategory(slot.Model, category); slot.SetActive(shouldShow); } // 정렬 로직 // UI 업데이트 로직 } private bool MatchesCategory(ItemViewModel model, InventoryCategoryType category) { // 복잡한 카테고리 매칭 로직 } } ``` #### After (MVVM) ```csharp // ViewModel (상태 관리) public class InventoryViewModel : SimpleViewModel { private InventoryCategoryType _currentCategory; private List _visibleItems; public InventoryCategoryType CurrentCategory { get => _currentCategory; set => SetField(ref _currentCategory, value); } public List VisibleItems { get => _visibleItems; private set => SetField(ref _visibleItems, value); } public string CategoryDisplayText => GetCategoryDisplayText(); public void SetCategory(InventoryCategoryType category) { CurrentCategory = category; UpdateVisibleItems(); OnPropertyChanged(nameof(CategoryDisplayText)); } private void UpdateVisibleItems() { var filtered = _inventoryService.FilterItems(CurrentCategory, CurrentSortType); VisibleItems = filtered.ToList(); } } // Service (비즈니스 로직) public class InventoryService : IUiService { public IEnumerable FilterItems(InventoryCategoryType category, InventorySortType sortType) { // 복잡한 필터링과 정렬 로직을 Service로 이동 } } // View (UI 바인딩만) public class MvvmInventoryView : MvvmBasePopupUi { [SerializeField] private Text _categoryLabel; [SerializeField] private Transform _slotParent; protected override void SetupBindings() { BindText(_categoryLabel, nameof(InventoryViewModel.CategoryDisplayText)); } protected override void HandleCustomPropertyChanged(string propertyName) { if (propertyName == nameof(InventoryViewModel.VisibleItems)) { UpdateItemSlots(); // 복잡한 UI 업데이트는 여전히 필요 } } } ``` ## 마이그레이션 주의사항 ### 1. 기존 코드와의 의존성 - 기존 UI를 참조하는 다른 코드들 확인 - 이벤트 시스템과의 연동 유지 - ScriptableObject 설정 파일들과의 호환성 ### 2. 성능 고려사항 - PropertyChanged 이벤트의 과도한 발생 방지 - UI 업데이트 배칭 활용 - 메모리 누수 방지 (이벤트 핸들러 정리) ### 3. 테스트 전략 - ViewModel 단위 테스트 작성 - 기존 UI와 새 UI 동시 테스트 - 사용자 시나리오 기반 통합 테스트 ## 마이그레이션 우선순위 추천 ### 1차 마이그레이션 (높은 우선순위) - **InventoryView**: 복잡한 상태 관리, 필터링, 정렬 - **ShopView**: 상품 목록, 카테고리, 구매 로직 - **MenuView**: 메뉴 관리, 레시피 선택 ### 2차 마이그레이션 (중간 우선순위) - **SettingsView**: 다양한 설정 옵션들 - **StatisticsView**: 데이터 표시와 계산 ### 3차 마이그레이션 (낮은 우선순위) - **SimpleDialogUi**: 간단한 확인 다이얼로그들 - **NotificationUi**: 알림 팝업들 이 가이드를 따라 점진적으로 UI를 MVVM으로 전환하면, 기존 시스템의 안정성을 유지하면서도 새로운 아키텍처의 이점을 얻을 수 있습니다.