# TRD - 플레이어 인벤토리 시스템
## 📋 기본 정보
- **프로젝트명**: Unity MVVM 게임 프로젝트
- **작성자**: 박개발 (기술 리드)
- **작성일**: 2024-01-02
- **버전**: v1.0
- **상태**: Approved
- **관련 PRD**: [PRD_Player_Inventory_System.md](PRD_Player_Inventory_System.md)
## 🏗️ 아키텍처 개요
### 전체 시스템 구조
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ View Layer │ │ ViewModel Layer │ │ Model Layer │
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│ InventoryView │◄──►│InventoryViewModel│◄──►│InventoryModel │
│ ItemSlotView │ │ ItemSlotViewModel│ │ ItemData │
│ TooltipView │ │ FilterViewModel │ │ ItemDatabase │
└─────────────────┘ └─────────────────┘ └─────────────────┘
▲ ▲ ▲
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Input System │ │ R3 Observable │ │ ScriptableObject│
│ Unity Events │ │ MessagePipe │ │ JSON Serializer │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
### 디자인 패턴
- **MVVM 패턴**: View-ViewModel-Model 분리로 관심사 분리
- **Observer 패턴**: R3 Observable을 통한 데이터 바인딩
- **Command 패턴**: 사용자 액션을 커맨드 객체로 캡슐화
- **Object Pool 패턴**: UI 요소의 재사용으로 GC 최소화
### 모듈 구조
```
Assets/
├── Features/
│ └── Inventory/
│ ├── Models/
│ │ ├── InventoryModel.cs
│ │ ├── ItemData.cs
│ │ └── ItemDatabase.cs
│ ├── ViewModels/
│ │ ├── InventoryViewModel.cs
│ │ ├── ItemSlotViewModel.cs
│ │ └── FilterViewModel.cs
│ ├── Views/
│ │ ├── InventoryView.cs
│ │ ├── ItemSlotView.cs
│ │ └── TooltipView.cs
│ └── Controllers/
│ ├── InventoryController.cs
│ └── DragDropController.cs
├── Prefabs/
│ └── Inventory/
│ ├── InventoryUI.prefab
│ ├── ItemSlot.prefab
│ └── Tooltip.prefab
└── Resources/
└── Inventory/
├── ItemDatabase.asset
└── ItemIcons/
```
## 🔧 Unity 기술 스택
### Unity 환경
- **Unity 버전**: Unity 2022.3.20f1 LTS
- **Render Pipeline**: URP (Universal Render Pipeline)
- **Target API**: .NET Standard 2.1
### 필수 패키지 및 라이브러리
| 패키지명 | 버전 | 목적 | 필수 여부 |
|----------|------|------|-----------|
| R3 | 1.2.4 | 반응형 프로그래밍, UI 데이터 바인딩 | 필수 |
| UniTask | 1.7.15 | 비동기 처리, 애니메이션 최적화 | 필수 |
| VContainer | 1.15.4 | 의존성 주입, 모듈 결합도 감소 | 필수 |
| MessagePipe | 1.8.1 | 메시징 시스템, 모듈 간 통신 | 필수 |
| Google Protobuf | 3.21.12 | 네트워크 통신, 인벤토리 데이터 직렬화 | 필수 |
| Addressables | 1.21.19 | 에셋 관리, 아이템 아이콘 로딩 | 선택 |
### 코딩 표준
- **네이밍 규칙**:
- 클래스/메서드: `InventoryViewModel`, `AddItem()`
- 변수/매개변수: `itemCount`, `slotIndex`
- private 필드: `_currentItems`, `_isInitialized`
- 상수: `MAX_INVENTORY_SIZE`, `DEFAULT_SLOT_COUNT`
- **아키텍처 패턴**: MVVM + DI + 메시징
## 🎯 기능별 기술 요구사항
### Feature 1: 기본 인벤토리 표시
- **담당 모듈**: Inventory.Views, Inventory.ViewModels
- **주요 클래스**:
- `InventoryModel`: 인벤토리 데이터 관리, 아이템 추가/제거 로직
- `InventoryViewModel`: UI 바인딩용 데이터 변환, 사용자 액션 처리
- `InventoryView`: Unity UI 표시, 사용자 입력 수신
- **의존성**: ItemDatabase (ScriptableObject), R3 Observable
- **성능 요구사항**:
- 메모리 사용량: 15MB 이하
- UI 렌더링: 60 FPS 유지
- 슬롯 생성 시간: 0.05초 이내
- **구현 우선순위**: High
**코드 구조 예시:**
```csharp
// Model
[CreateAssetMenu(fileName = "InventoryModel", menuName = "Game/Inventory")]
public class InventoryModel : ScriptableObject
{
[SerializeField] private List<ItemData> _items = new();
[SerializeField] private int _maxSlots = 48;
public ReactiveProperty<IReadOnlyList<ItemData>> Items { get; private set; }
public ReactiveProperty<int> UsedSlots { get; private set; }
}
// ViewModel
public class InventoryViewModel : IDisposable
{
[Inject] private InventoryModel _model;
[Inject] private IMessagePublisher<ItemAddedMessage> _publisher;
public ReadOnlyReactiveProperty<string> SlotCountText { get; private set; }
public ReactiveCommand<int> SelectSlotCommand { get; private set; }
}
// View
public class InventoryView : MonoBehaviour
{
[SerializeField] private GridLayoutGroup _itemGrid;
[SerializeField] private Text _slotCountText;
[Inject] private InventoryViewModel _viewModel;
private void Start()
{
// R3 Observable을 통한 데이터 바인딩
_viewModel.SlotCountText
.Subscribe(text => _slotCountText.text = text)
.AddTo(this);
}
}
```
### Feature 2: 드래그 앤 드롭 시스템
- **담당 모듈**: Inventory.Controllers
- **주요 클래스**:
- `DragDropController`: 드래그 상태 관리, 드롭 검증
- `ItemSlotView`: 개별 슬롯의 드래그 이벤트 처리
- `DragDropMessage`: MessagePipe를 통한 드래그 상태 전파
- **의존성**: Unity EventSystem, Physics2D Raycasting
- **성능 요구사항**:
- 드래그 응답 시간: 0.016초 이내 (60 FPS)
- 메모리 할당: 드래그 중 0 allocation
- **구현 우선순위**: High
**구현 방식:**
```csharp
public class DragDropController : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
[Inject] private IMessagePublisher<ItemDragMessage> _dragPublisher;
private Canvas _canvas;
private RectTransform _draggedItem;
private Vector2 _originalPosition;
public void OnBeginDrag(PointerEventData eventData)
{
// 드래그 시작 시 반투명 아이템 생성
CreateDragIcon(eventData);
_dragPublisher.Publish(new ItemDragMessage(true, _draggedItem));
}
public void OnDrag(PointerEventData eventData)
{
// 드래그된 아이템을 마우스/터치 위치로 이동
RectTransformUtility.ScreenPointToLocalPointInRectangle(
_canvas.transform as RectTransform,
eventData.position,
_canvas.worldCamera,
out Vector2 position);
_draggedItem.position = position;
}
}
```
### Feature 3: 아이템 필터링 시스템
- **담당 모듈**: Inventory.ViewModels, Inventory.Views
- **주요 클래스**:
- `FilterViewModel`: 필터 조건 관리, 필터링된 아이템 목록 제공
- `ItemFilterView`: 필터 UI (탭, 드롭다운)
- **의존성**: LINQ, R3 Observable
- **성능 요구사항**:
- 필터링 처리 시간: 1000개 아이템 기준 0.01초 이내
- **구현 우선순위**: Medium
## 🗂️ 데이터 구조
### 모델 설계
```csharp
[Serializable]
public class ItemData : IEquatable<ItemData>
{
[SerializeField] private string _id;
[SerializeField] private string _name;
[SerializeField] private ItemType _type;
[SerializeField] private ItemRarity _rarity;
[SerializeField] private int _stackSize;
[SerializeField] private Sprite _icon;
[SerializeField] private string _description;
// 프로퍼티들...
public string Id => _id;
public string Name => _name;
public ItemType Type => _type;
public ItemRarity Rarity => _rarity;
public int StackSize => _stackSize;
public Sprite Icon => _icon;
public string Description => _description;
public bool Equals(ItemData other) => _id == other?._id;
public override int GetHashCode() => _id?.GetHashCode() ?? 0;
}
public enum ItemType
{
Weapon = 0,
Armor = 1,
Consumable = 2,
Material = 3,
Misc = 4
}
public enum ItemRarity
{
Common = 0,
Uncommon = 1,
Rare = 2,
Epic = 3,
Legendary = 4
}
[Serializable]
public class InventorySlot
{
[SerializeField] private ItemData _item;
[SerializeField] private int _count;
public ItemData Item => _item;
public int Count => _count;
public bool IsEmpty => _item == null || _count <= 0;
public bool CanStack(ItemData item) =>
_item?.Id == item?.Id && _count < _item.StackSize;
}
```
### 데이터 저장 방식
- **로컬 데이터**: JSON 직렬화하여 PlayerPrefs 또는 Application.persistentDataPath에 저장
- **세이브 파일 위치**: `Application.persistentDataPath/SaveData/inventory.json`
- **클라우드 연동**: Firebase Firestore를 통한 클라우드 세이브 (선택적)
**저장 데이터 구조:**
*JSON 형식 (로컬 저장용):*
```json
{
"inventory": {
"slots": [
{
"itemId": "sword_001",
"count": 1,
"slotIndex": 0
},
{
"itemId": "potion_health",
"count": 5,
"slotIndex": 1
}
],
"maxSlots": 48,
"lastModified": "2024-01-02T10:30:00Z"
}
}
```
*Protobuf 스키마 (네트워크 통신용):*
```protobuf
syntax = "proto3";
message InventoryData {
repeated InventorySlot slots = 1;
int32 max_slots = 2;
int64 last_modified = 3;
}
message InventorySlot {
string item_id = 1;
int32 count = 2;
int32 slot_index = 3;
}
```
### ScriptableObject 구조
- **아이템 데이터베이스**: `ItemDatabase.asset` - 모든 게임 아이템 정의
- **인벤토리 설정**: `InventorySettings.asset` - 기본 슬롯 수, UI 설정 등
```csharp
[CreateAssetMenu(fileName = "ItemDatabase", menuName = "Game/ItemDatabase")]
public class ItemDatabase : ScriptableObject
{
[SerializeField] private List<ItemData> _allItems = new();
private Dictionary<string, ItemData> _itemLookup;
public void Initialize()
{
_itemLookup = _allItems.ToDictionary(item => item.Id);
}
public ItemData GetItem(string itemId) =>
_itemLookup.TryGetValue(itemId, out ItemData item) ? item : null;
}
```
## 🎨 UI/UX 기술 구현
### UGUI 구조
- **Canvas 계층**:
- Background Canvas (Sort Order: 0) - 게임 배경
- Game UI Canvas (Sort Order: 100) - HUD, 상태바
- Inventory Canvas (Sort Order: 200) - 인벤토리 창
- Tooltip Canvas (Sort Order: 300) - 툴팁 오버레이
### UI 바인딩 시스템
```csharp
// R3 Observable을 활용한 데이터 바인딩
public class InventoryView : MonoBehaviour
{
[SerializeField] private Transform _itemSlotParent;
[SerializeField] private ItemSlotView _slotPrefab;
[Inject] private InventoryViewModel _viewModel;
private readonly List<ItemSlotView> _slotViews = new();
private void Start()
{
// 인벤토리 아이템 변경 시 UI 업데이트
_viewModel.Items
.Subscribe(items => UpdateSlotViews(items))
.AddTo(this);
// 필터 변경 시 표시되는 아이템 업데이트
_viewModel.FilteredItems
.Subscribe(filteredItems => ApplyFilter(filteredItems))
.AddTo(this);
}
private void UpdateSlotViews(IReadOnlyList<InventorySlot> slots)
{
for (int i = 0; i < slots.Count; i++)
{
if (i >= _slotViews.Count)
{
// 슬롯 뷰 오브젝트 풀링
var slotView = _slotPool.Rent();
_slotViews.Add(slotView);
}
_slotViews[i].UpdateSlot(slots[i]);
}
}
}
```
### 로컬라이제이션
```csharp
// 다국어 지원을 위한 LocalizationManager 연동
[Inject] private ILocalizationService _localization;
private void UpdateItemTooltip(ItemData item)
{
var localizedName = _localization.GetText(
quot;item.{item.Id}.name");
var localizedDesc = _localization.GetText(quot;item.{item.Id}.description");
_tooltipView.SetContent(localizedName, localizedDesc);
}
```
## 🚀 성능 최적화
### 메모리 관리
- **오브젝트 풀링**: UI 슬롯, 드래그 아이콘, 툴팁 등 재사용 가능한 UI 요소
- **GC 최소화**:
- StringBuilder 사용으로 string 연산 최적화
- LINQ 대신 for문 사용 (필터링 로직)
- 이벤트 구독 해제를 통한 메모리 누수 방지
```csharp
public class ItemSlotPool : MonoBehaviour
{
[SerializeField] private ItemSlotView _slotPrefab;
[SerializeField] private int _initialSize = 50;
private readonly Queue<ItemSlotView> _pool = new();
private void Start()
{
// 미리 슬롯 오브젝트들을 생성하여 풀에 저장
for (int i = 0; i < _initialSize; i++)
{
var slot = Instantiate(_slotPrefab);
slot.gameObject.SetActive(false);
_pool.Enqueue(slot);
}
}
public ItemSlotView Rent()
{
if (_pool.Count > 0)
{
var slot = _pool.Dequeue();
slot.gameObject.SetActive(true);
return slot;
}
return Instantiate(_slotPrefab);
}
public void Return(ItemSlotView slot)
{
slot.gameObject.SetActive(false);
_pool.Enqueue(slot);
}
}
```
### 렌더링 최적화
- **UI 배칭**: 동일한 아틀라스 텍스처 사용으로 Draw Call 최소화
- **캔버스 분리**: 자주 변경되는 UI와 정적 UI 분리
- **마스킹 최적화**: Rect Mask 2D 활용으로 오버드로우 방지
### 프로파일링 목표
- **Target FPS**: 60 FPS (모바일 환경)
- **메모리 사용량**: 최대 50MB (인벤토리 시스템 전체)
- **로딩 시간**: 인벤토리 열기 0.1초 이내
- **GC 발생**: 1분당 최대 1회
## 🔐 보안 및 데이터 보호
### 데이터 보안
- **세이브 파일 검증**: CRC32 체크섬을 통한 무결성 검사
- **치트 방지**:
- 중요 데이터 서버 검증
- 클라이언트 데이터 암호화
- 비정상적 아이템 획득 패턴 감지
- **개인정보 처리**: GDPR 준수, 로컬 데이터만 사용
```csharp
public class SecureInventorySerializer
{
private const string ENCRYPTION_KEY = "InventoryKey2024";
public string SerializeInventory(InventoryModel inventory)
{
var json = JsonUtility.ToJson(inventory);
var encrypted = EncryptString(json, ENCRYPTION_KEY);
var checksum = CalculateChecksum(encrypted);
return quot;{checksum}:{encrypted}";
}
public InventoryModel DeserializeInventory(string data)
{
var parts = data.Split(':');
if (parts.Length != 2) throw new InvalidDataException();
var checksum = parts[0];
var encrypted = parts[1];
if (!VerifyChecksum(encrypted, checksum))
throw new InvalidDataException("Data corruption detected");
var json = DecryptString(encrypted, ENCRYPTION_KEY);
return JsonUtility.FromJson<InventoryModel>(json);
}
}
```
## 🧪 테스트 전략
### 자동화 테스트
- **Unit Test**: 인벤토리 로직 테스트 (NUnit)
- **Integration Test**: MVVM 바인딩 테스트 (Unity Test Framework)
- **Performance Test**: UI 성능 테스트 (Unity Performance Testing)
```csharp
[TestFixture]
public class InventoryModelTests
{
private InventoryModel _inventory;
private ItemDatabase _database;
[SetUp]
public void Setup()
{
_inventory = ScriptableObject.CreateInstance<InventoryModel>();
_database = ScriptableObject.CreateInstance<ItemDatabase>();
// 테스트 데이터 설정...
}
[Test]
public void AddItem_ShouldIncreaseItemCount()
{
// Arrange
var testItem = _database.GetItem("test_item");
// Act
var result = _inventory.AddItem(testItem, 1);
// Assert
Assert.IsTrue(result);
Assert.AreEqual(1, _inventory.GetItemCount(testItem.Id));
}
[Test]
public void AddItem_WhenInventoryFull_ShouldReturnFalse()
{
// Arrange
FillInventoryToMax();
var newItem = _database.GetItem("new_item");
// Act
var result = _inventory.AddItem(newItem, 1);
// Assert
Assert.IsFalse(result);
}
}
```
### 수동 테스트
- **기능 테스트**: 사용자 시나리오별 테스트 체크리스트
- **호환성 테스트**: 다양한 해상도 및 기기에서 테스트
- **사용성 테스트**: 실제 사용자 대상 UI/UX 테스트
## 🌍 플랫폼별 고려사항
### Mobile (Android/iOS)
- **터치 입력**: 멀티터치 지원, 제스처 인식
- **메모리 제한**: 모바일 환경의 메모리 제약 고려
- **배터리 최적화**: GPU 사용량 최소화
- **해상도 대응**: Safe Area, Notch 대응
```csharp
public class MobilePlatformAdapter : MonoBehaviour
{
[SerializeField] private RectTransform _inventoryPanel;
private void Start()
{
AdjustForSafeArea();
OptimizeForMobile();
}
private void AdjustForSafeArea()
{
var safeArea = Screen.safeArea;
var rectTransform = _inventoryPanel;
var anchorMin = safeArea.position;
var anchorMax = anchorMin + safeArea.size;
anchorMin.x /= Screen.width;
anchorMin.y /= Screen.height;
anchorMax.x /= Screen.width;
anchorMax.y /= Screen.height;
rectTransform.anchorMin = anchorMin;
rectTransform.anchorMax = anchorMax;
}
}
```
## 📦 빌드 및 배포
### 빌드 설정
- **Player Settings**:
- Scripting Backend: IL2CPP
- Target API Level: Android 31, iOS 15.0
- Compression Format: LZ4 (빠른 압축 해제)
- **Addressable Settings**: 아이템 아이콘 에셋 번들링
- **Build Settings**:
- Development Build: false (릴리스 빌드)
- Script Debugging: false
- Compression Method: LZ4HC
### 배포 프로세스
- **버전 관리**: 시맨틱 버저닝 (major.minor.patch)
- **CI/CD**: GitHub Actions를 통한 자동 빌드
- **QA 프로세스**:
- 자동 테스트 → 수동 기능 테스트 → 성능 테스트 → 배포
## 🔄 개발 워크플로우
### Git 브랜치 전략
- **main**: 릴리즈 가능한 안정 버전
- **develop**: 개발 통합 브랜치
- **feature/inventory-basic-ui**: 기본 UI 구현
- **feature/inventory-drag-drop**: 드래그 앤 드롭 구현
- **feature/inventory-filtering**: 필터링 시스템 구현
### 코드 리뷰 규칙
- **리뷰 필수 항목**:
- [ ] Unity 코딩 표준 준수 (네이밍, 들여쓰기)
- [ ] MVVM 패턴 적용 확인
- [ ] 메모리 누수 위험성 검토 (이벤트 구독 해제 등)
- [ ] 성능 영향도 평가 (Update문 내 GetComponent 등)
- [ ] 단위 테스트 작성 여부
- **승인 조건**: 최소 2명의 시니어 개발자 승인
- **자동 체크**: SonarQube 정적 분석, 단위 테스트 통과
## 📅 개발 일정 및 마일스톤
### 기술적 마일스톤
- **Tech Milestone 1**: MVVM 아키텍처 구축 (2024-01-10)
- VContainer DI 컨테이너 설정
- 기본 Model, ViewModel, View 클래스 구현
- R3 Observable 바인딩 시스템 구축
- **Tech Milestone 2**: 핵심 기능 구현 (2024-01-20)
- 인벤토리 기본 UI 및 데이터 표시
- 드래그 앤 드롭 시스템 구현
- 아이템 툴팁 시스템 구현
- **Tech Milestone 3**: 고급 기능 및 최적화 (2024-02-05)
- 필터링 및 정렬 시스템
- 오브젝트 풀링 및 성능 최적화
- 메모리 프로파일링 및 최적화
- **Tech Milestone 4**: 플랫폼 최적화 및 배포 (2024-02-15)
- 모바일 플랫폼 최적화
- 멀티 해상도 대응
- 빌드 최적화 및 배포 준비
### 리스크 분석
| 리스크 | 확률 | 영향도 | 대응 방안 |
|--------|------|--------|-----------|
| MVVM 패턴 학습 곡선 | Medium | High | 팀 교육 세션, 프로토타입 우선 구현 |
| 모바일 성능 최적화 | High | High | 정기 성능 테스트, 프로파일링 도구 활용 |
| UI 복잡성 증가 | Medium | Medium | 단계별 구현, UI 컴포넌트 모듈화 |
| 멀티 플랫폼 호환성 | Low | Medium | 플랫폼별 빌드 테스트 자동화 |
## 🔄 승인 및 변경 이력
### 기술 검토자
- [x] **Technical Lead**: 박개발 - 2024-01-02
- [x] **Senior Developer**: 이시니어 - 2024-01-03
- [x] **Mobile Specialist**: 김모바일 - 2024-01-03
### 변경 이력
| 버전 | 날짜 | 변경 사항 | 작성자 |
|------|------|-----------|--------|
| v1.0 | 2024-01-02 | 초기 작성 | 박개발 |
---
## 📚 참고 문서
- [Unity MVVM 패턴 가이드](https://docs.unity3d.com/Manual/UIE-MVVM-pattern.html)
- [R3 Observable 공식 문서](https://github.com/Cysharp/R3)
- [VContainer 의존성 주입 가이드](https://github.com/hadashiA/VContainer)
- [Unity 성능 최적화 베스트 프랙티스](https://docs.unity3d.com/Manual/MobileOptimization.html)
- [MessagePipe 메시징 시스템](https://github.com/Cysharp/MessagePipe)