Files
XRLib/CLAUDE.md

353 lines
12 KiB
Markdown
Raw Normal View History

2026-01-08 20:15:57 +09:00
# Unity 개발 지침 (UI Toolkit · MVVM · 성능 · Nullable)
본 지침은 Unity UI Toolkit 기반 프로젝트의 아키텍처, 성능, 코드 품질을 일관되게 유지하기 위한 규칙입니다.
프로젝트 스타일: `#nullable enable`, UniTask, DOTween, 한국어 주석
---
## 1) 핵심 원칙
### UI 프레임워크
- **UI Toolkit(UIElements) 필수 사용**. uGUI(Canvas 기반)는 레거시로 취급합니다.
- UXML(구조)과 USS(스타일)를 분리하고, C# 코드에서 인라인 스타일 지정을 지양합니다.
### 아키텍처 (MVVM/MVC)
```
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ View │◄────│ ViewModel │◄────│ Model │
│ (UXML/USS) │ │ (Presenter) │ │ (Service) │
└─────────────┘ └──────────────┘ └─────────────┘
│ │
└── 이벤트 전달 ──────┘
```
| 레이어 | 책임 | 금지 사항 |
|--------|------|-----------|
| **View** | 표시/레이아웃, 이벤트 라우팅 | 비즈니스 로직, 상태 보유 |
| **ViewModel/Presenter** | 상태 관리, 데이터 변환, 바인딩 속성 | Unity API 직접 호출 (테스트 용이) |
| **Model/Service** | 도메인 로직, 데이터 접근 | UI 참조 |
- **MVVM**: UI 상태/양방향 동기화가 많을 때
- **MVC**: 입력 → 도메인 액션 → UI 반영 흐름이 단순할 때
### 필수 규약
- 파일 선두에 `#nullable enable`, 모든 참조형에 `?` 명시
- 비동기는 `UniTask` + `CancellationToken` 사용 (`Task`/코루틴 지양)
- 느슨한 결합: 인터페이스/이벤트로 연결
---
## 2) 폴더 구조
```
Assets/
├── Scripts/
│ ├── {프로젝트명}/ # 프로젝트별 코드
│ │ ├── Config/ # 설정, 상수
│ │ ├── Manager/ # 매니저 클래스
│ │ ├── Command/ # Command 패턴 (Undo/Redo)
│ │ └── ...
│ └── UVC/ # 공통 라이브러리 ⭐
│ ├── Core/ # DI, Injector, Singleton
│ ├── Data/ # DataMapper, MQTT/HTTP 통신
│ ├── Pool/ # 오브젝트 풀링
│ ├── UI/ # uGUI 컴포넌트 (Modal, Tab)
│ └── UIToolkit/ # UI Toolkit 컴포넌트 ⭐
├── Resources/
│ ├── {프로젝트명}/ # 프로젝트별 리소스
│ │ ├── Materials/
│ │ ├── Models/
│ │ └── Prefabs/
│ └── UIToolkit/ # 공통 UI 리소스 ⭐
│ ├── Common/ # 공통 스타일 (USS)
│ ├── List/ # 리스트 컴포넌트 (UXML)
│ ├── Modal/ # 모달 컴포넌트 (UXML)
│ ├── Property/ # 속성 편집기 (UXML)
│ └── Window/ # 윈도우 컴포넌트 (UXML)
├── Plugins/ # 서드파티 (Best.HTTP, DOTween 등)
├── Sample/ # 샘플 씬
└── Scenes/ # 앱 씬
```
**현재 프로젝트:**
| 폴더 | 설명 |
|------|------|
| `Scripts/Factory` | 스마트 팩토리 3D 시각화 (MQTT 실시간) |
| `Scripts/Simulator` | Factory 시뮬레이션 버전 |
| `Scripts/Studio` | 3D 씬 에디터 (Undo/Redo, Gizmo) |
| `Scripts/SHI` | 조선소 공정 모달 (TreeList, Chart) |
| `Scripts/NHN` | 무한 스크롤 컴포넌트 (uGUI 레거시) |
> **참고**: 각 폴더에 `CLAUDE.md` 파일이 있어 모듈별 상세 가이드를 제공합니다.
---
## 3) 성능 최적화
### VisualElement 쿼리
```csharp
// ❌ 나쁜 예: 매 프레임 쿼리
void Update() {
rootVisualElement.Q<Label>("title").text = _title;
}
// ✅ 좋은 예: 캐싱
private Label? _titleLabel;
void OnEnable() {
_titleLabel = rootVisualElement.Q<Label>("title");
}
```
### 체크리스트
- [ ] `Q<T>()`, `Query<T>()` 결과는 필드에 캐싱
- [ ] 변경된 데이터만 업데이트 (전체 리빌드 지양)
- [ ] 대량 목록은 `ListView`/`TreeView` 가상화 활용
- [ ] USS 선택자 복잡도 최소화 (`>` 중첩, 와일드카드 지양)
- [ ] 동적 생성/파괴 대신 풀링 또는 `display: none` 토글
- [ ] 지연/반복 작업은 `schedule.Execute()` 사용
- [ ] Update에서 GC 할당 금지 (LINQ/문자열 연결/클로저 지양)
- [ ] DOTween: 핸들 보관, 수명 종료 시 `Kill()`
---
## 4) 메모리 관리
### 이벤트 구독/해제
```csharp
private EventCallback<ClickEvent>? _onClick;
void OnEnable() {
_onClick = OnButtonClick;
_button?.RegisterCallback(_onClick);
}
void OnDisable() {
_button?.UnregisterCallback(_onClick);
}
```
### 체크리스트
- [ ] `RegisterCallback<T>``UnregisterCallback<T>` 대칭 확인
- [ ] `CancellationTokenSource``OnDestroy`에서 `Cancel/Dispose`
- [ ] VisualTreeAsset/USS 동일 리소스 반복 로드 방지 (캐싱)
- [ ] 클로저/람다 캡처로 인한 누수 점검
- [ ] 오브젝트 풀: `IPoolable.OnRent/OnReturn` 훅, 반환 시 `DOTween.Kill()`
---
## 5) 비동기 (UniTask)
```csharp
// 공개 API는 UniTask 반환, CancellationToken 필수
public async UniTask<Data?> LoadDataAsync(CancellationToken ct)
{
var result = await _repository.FetchAsync().AttachExternalCancellation(ct);
return result;
}
// Fire-and-forget은 예외 로깅 후 .Forget()
LoadDataAsync(_cts.Token).Forget(ex => Debug.LogError(ex));
```
### 규칙
- `async void` 지양, `UniTask`/`UniTask<T>` 반환
- 토큰 결합: `CreateLinkedTokenSource(parent, local)`
- 타임아웃: `cts.CancelAfter(TimeSpan.FromSeconds(5))`
- CPU 바운드: `UniTask.Run``UniTask.SwitchToMainThread`
---
## 6) 리소스 로드 (Addressables/Resources)
```csharp
[Serializable]
public struct AssetRef<T> where T : UnityEngine.Object
{
[SerializeField] private string _path;
public async UniTask<T?> LoadAsync(CancellationToken ct = default)
{
#if USE_ADDRESSABLES
var handle = Addressables.LoadAssetAsync<T>(_path);
await handle.Task.AsUniTask().AttachExternalCancellation(ct);
return handle.Status == AsyncOperationStatus.Succeeded ? handle.Result : null;
#else
var request = Resources.LoadAsync<T>(_path);
await request.AsUniTask(cancellationToken: ct);
return request.asset as T;
#endif
}
}
```
- Addressables 사용 시 `USE_ADDRESSABLES` 전처리기 정의
- `LoadAssetAsync` 핸들은 수명 관리 후 `Release`
- GameObject 직접 참조 대신 경로 문자열 직렬화
---
## 7) USS 스타일 가이드
### 디자인 참조
UI Toolkit 개발 시 다음 위치의 스타일 리소스를 참조하세요:
**우선 참조**: `Assets/Resources/UIToolkit/Style/`
- 프로젝트에서 사용 중인 실제 USS 스타일 파일들이 위치합니다.
- 컴포넌트별로 정리된 스타일을 먼저 확인하세요.
**보조 참조**: `StyleGuide/` 폴더
- `Assets/Resources/UIToolkit/Style/`에 구현되지 않은 스타일만 참조합니다.
- 새 컴포넌트 개발 시 디자인 가이드로 활용합니다.
2026-01-08 20:15:57 +09:00
| 파일 | 설명 |
|------|------|
| `style_guide_Colors.png` | 색상 팔레트 |
| `style_guide_Typography.png` | 타이포그래피 (폰트, 크기) |
| `style_guide_Buttons.png` | 버튼 스타일 |
| `style_guide_Text Field.png` | 텍스트 필드 |
| `style_guide_Dropdowns.png` | 드롭다운 |
| `style_guide_Checkbox.png` | 체크박스 |
| `style_guide_List.png` | 리스트 |
| `style_guide_Tabs.png` | 탭 |
| `style_guide_Panel.png` | 패널 |
| `style_guide_Sidebar.png` | 사이드바 |
| `style_guide_Menu.png` | 메뉴 |
| `style_guide_Dialogs.png` | 다이얼로그/모달 |
| `style_guide_Notifications.png` | 알림 |
| `style_guide_Status Bar.png` | 상태 바 |
| `style_guide_Extensions.png` | 확장 컴포넌트 |
| `style_guide_Templates.png` | 템플릿 |
### BEM 네이밍
```css
.panel { }
.panel__header { }
.panel__header--highlighted { }
.panel__content { }
```
### 규칙
- 반복 값은 USS 변수 사용: `--color-primary`, `--spacing-md`
- 라이트/다크 테마는 별도 USS로 분리
- **새 UI 컴포넌트 개발 시 StyleGuide 이미지와 일치하는 스타일 적용**
---
2026-01-13 20:39:45 +09:00
## 8) 아이콘 사용 가이드 (Icons)
### 아이콘 사용 우선순위
UI 아이콘 적용 시 다음 순서를 반드시 준수해야 합니다.
1. **1순위 (Material Icons)**: `UTKMaterialIcons` 클래스 확인
- 폰트 기반 아이콘(Unicode)을 우선 사용합니다.
- 예: `UTKButton.SetMaterialIcon(UTKMaterialIcons.Home)`
2. **2순위 (Image Icons)**: `UTKImageIcons` 클래스 사용
- 필요한 아이콘이 `UTKMaterialIcons`에 없는 경우에만 `UTKImageIcons`를 사용합니다.
- 예: `UTKButton.SetImageIcon(UTKImageIcons.CustomIcon)`
> **권장**: 일관된 UI 스타일과 메모리 효율을 위해 가능한 Material Icons 사용을 권장합니다.
---
## 9) 주석 원칙 (C# XML)
2026-01-08 20:15:57 +09:00
```csharp
/// <summary>
/// 사용자 데이터를 비동기로 로드합니다.
/// </summary>
/// <param name="userId">사용자 ID.</param>
/// <param name="ct">취소 토큰.</param>
/// <returns>사용자 데이터 또는 null.</returns>
public async UniTask<UserData?> LoadUserAsync(string userId, CancellationToken ct)
```
- 클래스: 역할/책임/사용 예
- 메서드: 요약 + 파라미터/반환 (복잡 로직만 상세)
- 속성: 한 줄 요약
---
2026-01-13 20:39:45 +09:00
## 10) 디자인 패턴 요약
2026-01-08 20:15:57 +09:00
| 패턴 | 사용 시점 |
|------|-----------|
| **DI/Composition Root** | 서비스/ViewModel 주입 일원화 |
| **Event Aggregator** | 컴포넌트 간 느슨한 통신 |
| **Command + Undo** | UI 액션에 되돌리기 필요 시 |
| **Strategy** | 정렬/필터 규칙 교체 |
| **State/FSM** | 모드 전환 (편집/선택/드래그) |
| **Factory** | 뷰/프리팹 생성 캡슐화 |
| **Object Pool** | 대량 아이템 재사용 |
| **Repository** | 데이터 소스 추상화 |
---
2026-01-13 20:39:45 +09:00
## 11) View 기본 패턴
2026-01-08 20:15:57 +09:00
```csharp
#nullable enable
using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine.UIElements;
/// <summary>
/// UI Toolkit View 기본 구조.
/// </summary>
public abstract class UIViewBase : IDisposable
{
protected VisualElement Root { get; private set; } = null!;
protected CancellationTokenSource? Cts { get; private set; }
public virtual void Initialize(VisualElement root)
{
Root = root ?? throw new ArgumentNullException(nameof(root));
Cts = new CancellationTokenSource();
QueryElements();
RegisterEvents();
}
protected abstract void QueryElements();
protected abstract void RegisterEvents();
protected abstract void UnregisterEvents();
public virtual void Dispose()
{
UnregisterEvents();
Cts?.Cancel();
Cts?.Dispose();
Cts = null;
}
}
```
---
2026-01-13 20:39:45 +09:00
## 12) Unity Nullable 주의
2026-01-08 20:15:57 +09:00
```csharp
// Unity Object는 == null 오버로드됨
if (gameObject == null) { } // 파괴된 객체도 true
// 순수 참조 null 확인 시
if (ReferenceEquals(obj, null)) { }
```
- 직렬화 필드에 `?` 표기, 런타임 Null/파괴 상태 방어적 처리
---
2026-01-13 20:39:45 +09:00
## 13) 품질 자동화
2026-01-08 20:15:57 +09:00
### .editorconfig 권장
```ini
dotnet_diagnostic.CS1591.severity = error # 공개 멤버 문서 주석 필수
dotnet_analyzer_diagnostic.category-Nullable.severity = error
dotnet_diagnostic.IDE0060.severity = warning # 미사용 매개변수
```
### CI
- XML 문서 누락/nullable 경고 → 빌드 실패 처리
- 에디트 모드/플레이 모드 테스트 분리 실행