Files
EnglewoodLAB/CLAUDE.md

8.5 KiB

Unity 개발 지침 (UI Toolkit · MVVM · 성능 · Nullable)

본 지침은 Unity UI Toolkit 기반 프로젝트의 아키텍처, 성능, 코드 품질을 일관되게 유지하기 위한 범용 규칙입니다. 프로젝트 스타일: #nullable enable, UniTask, DOTween, 한국어 주석


0) 작업 진행 규칙

⚠️ 최우선 규칙: 임의로 진행하지 않고, 반드시 사용자에게 확인 후 진행합니다.

  • 코드 수정, 파일 생성/삭제, 리팩토링 등 모든 변경 작업은 사전에 계획을 설명하고 승인을 받은 후 진행합니다.
  • 요구사항이 모호하거나 여러 접근 방식이 가능한 경우, 추측하지 말고 질문합니다.
  • 버그 수정이라도 원인 분석 결과를 먼저 공유하고, 수정 방향에 대해 합의 후 코드를 변경합니다.
  • 단순한 오타 수정, 한 줄 변경 등 명백하고 사소한 작업만 즉시 진행할 수 있습니다.

1) 핵심 원칙

UI 프레임워크

  • UI Toolkit(UIElements) 필수 사용. uGUI(Canvas 기반)는 레거시로 취급합니다.
  • UXML(구조)과 USS(스타일)를 분리하고, C# 코드에서 인라인 스타일 지정을 지양합니다.

이벤트 콜백 등록/해제

RegisterValueChangedCallback 대신 RegisterCallback<ChangeEvent<T>>를 사용합니다. RegisterValueChangedCallback은 확장 메서드로 UnregisterCallback과 대칭이 맞지 않아 이벤트 해제가 불가능합니다.

// ❌ 금지: 해제 불가
field.RegisterValueChangedCallback(OnValueChanged);

// ✅ 권장: 대칭적 등록/해제
field.RegisterCallback<ChangeEvent<float>>(OnValueChanged);
field.UnregisterCallback<ChangeEvent<float>>(OnValueChanged);

커스텀 VisualElement (Unity 6)

레거시 UxmlFactory/UxmlTraits 방식을 사용하지 않고, 소스 생성기 기반의 [UxmlElement][UxmlAttribute]를 사용합니다.

  • 클래스에 [UxmlElement] 어트리뷰트 추가
  • 클래스를 partial로 선언 (소스 생성기 요구사항)
  • UXML 속성은 [UxmlAttribute("케밥-케이스")]로 명시
// ✅ 권장 (Unity 6)
[UxmlElement]
public partial class UTKExample : VisualElement
{
    [UxmlAttribute("is-enabled")]
    public bool IsEnabled { get; set; }
}

// ❌ 금지 (레거시)
public class UTKExample : VisualElement
{
    public new class UxmlFactory : UxmlFactory<UTKExample, UxmlTraits> { }
}

아키텍처 (MVVM/MVC)

레이어 책임 금지 사항
View 표시/레이아웃, 이벤트 라우팅 비즈니스 로직, 상태 보유
ViewModel/Presenter 상태 관리, 데이터 변환, 바인딩 속성 Unity API 직접 호출
Model/Service 도메인 로직, 데이터 접근 UI 참조
  • MVVM: UI 상태/양방향 동기화가 많을 때
  • MVC: 입력 → 도메인 액션 → UI 반영 흐름이 단순할 때

필수 규약

  • 파일 선두에 #nullable enable, 모든 참조형에 ? 명시
  • 비동기는 UniTask + CancellationToken 사용 (Task/코루틴 지양)
  • 느슨한 결합: 인터페이스/이벤트로 연결

2) 폴더 구조

Assets/
├── Scripts/
│   ├── {프로젝트명}/       # 프로젝트별 코드
│   └── UVC/                # 공통 라이브러리 ⭐
│       ├── Core/           #   DI, Injector, Singleton
│       ├── Data/           #   DataMapper, MQTT/HTTP 통신
│       ├── Pool/           #   오브젝트 풀링
│       ├── UI/             #   uGUI 컴포넌트 (레거시)
│       └── UIToolkit/      #   UI Toolkit 컴포넌트 ⭐
├── Resources/
│   └── UIToolkit/          # 공통 UI 리소스 ⭐
│       ├── Style/          #   USS 스타일 파일 (우선 참조)
│       ├── Common/         #   공통 UXML 컴포넌트
│       ├── List/           #   리스트 컴포넌트
│       ├── Modal/          #   모달 컴포넌트
│       ├── Property/       #   속성 편집기
│       └── Window/         #   윈도우 컴포넌트
├── Plugins/                # 서드파티 (Best.HTTP, DOTween 등)
├── Sample/                 # 샘플 씬
└── Scenes/                 # 앱 씬

참고: 각 폴더에 CLAUDE.md 파일이 있어 모듈별 상세 가이드를 제공합니다.


3) 네이밍 규칙

항목 규칙 예시
UTK 컴포넌트 클래스 UTK 접두사 + PascalCase UTKButton, UTKAccordionList
UXML 파일 {ComponentName}.uxml UTKAccordionListWindow.uxml
USS 파일 {ComponentName}Uss.uss UTKAccordionListWindowUss.uss
Private 필드 _camelCase _button, _labelCache
이벤트 On 접두사 OnValueChanged, OnClicked
네임스페이스 UVC.UIToolkit (공통), 프로젝트별 별도 UVC.UIToolkit, Factory

⚠️ UXML과 USS 파일명은 반드시 다르게 지정해야 합니다. Resources.Load<T>(path)는 확장자 없이 경로를 받으므로, 동일한 경로명이면 로드 충돌이 발생합니다.


4) 성능 원칙

  • Q<T>(), Query<T>() 결과는 필드에 캐싱 (매 프레임 쿼리 금지)
  • 대량 목록은 ListView/TreeView 가상화 활용
  • 동적 생성/파괴 대신 풀링 또는 display: none 토글
  • Update에서 GC 할당 금지 (LINQ/문자열 연결/클로저 지양)
  • DOTween: 핸들 보관, 수명 종료 시 Kill()

5) 메모리 관리 원칙

  • RegisterCallback<T>UnregisterCallback<T> 반드시 대칭
  • AttachToPanelEvent / DetachFromPanelEvent로 VisualElement 생명주기에 맞춰 이벤트 관리
  • CancellationTokenSourceOnDestroy에서 CancelDispose
  • VisualTreeAsset/USS 동일 리소스 반복 로드 방지 (캐싱)
  • 클로저/람다 캡처로 인한 누수 점검

6) 비동기 (UniTask)

// 공개 API는 UniTask 반환, CancellationToken 필수
public async UniTask<Data?> LoadDataAsync(CancellationToken ct)
{
    return await _repository.FetchAsync().AttachExternalCancellation(ct);
}

// 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))

7) 리소스 로드 규칙

  • Resources.Load<T>() 사용 시 UXML과 USS 경로명 중복 금지 (USS에 Uss 접미사)
  • Assets/Resources/UIToolkit/Style/ 의 USS를 우선 참조하고, 없는 경우 StyleGuide/ 폴더 참조
  • Addressables 사용 시 USE_ADDRESSABLES 전처리기 정의

8) USS 스타일 규칙

  • BEM 네이밍: .block, .block__element, .block__element--modifier
  • 반복 값은 USS 변수 사용: --color-primary, --spacing-md
  • 라이트/다크 테마는 별도 USS로 분리 (UTKThemeManager 사용)
  • 새 컴포넌트 개발 시 StyleGuide/ 이미지와 일치하는 스타일 적용

9) 아이콘 사용 우선순위

  1. 1순위: UTKMaterialIcons — 폰트 기반 Unicode 아이콘 (메모리 효율 우수)
  2. 2순위: UTKImageIcons — Material Icons에 없는 경우에만 사용

10) 주석 원칙

  • 한국어 주석 사용
  • 공개 멤버는 XML 문서 주석 필수 (/// <summary>)
  • 클래스: 역할/책임, 메서드: 요약 + 파라미터/반환, 속성: 한 줄 요약, 샘플코드
/// <summary>
/// 사용자 데이터를 비동기로 로드합니다.
/// </summary>
/// <param name="userId">사용자 ID.</param>
/// <param name="ct">취소 토큰.</param>
/// <returns>사용자 데이터 또는 null.</returns>
public async UniTask<UserData?> LoadUserAsync(string userId, CancellationToken ct)

11) Unity Nullable 주의

// Unity Object는 == null이 오버로드됨 (파괴된 객체도 true)
if (gameObject == null) { }

// 순수 참조 null 확인 시
if (ReferenceEquals(obj, null)) { }
  • 직렬화 필드에 ? 표기, 런타임 Null/파괴 상태 방어적 처리

12) 디자인 패턴 요약

패턴 사용 시점
DI/Composition Root 서비스/ViewModel 주입 일원화
Event Aggregator 컴포넌트 간 느슨한 통신
Command + Undo UI 액션에 되돌리기 필요 시
Strategy 정렬/필터 규칙 교체
State/FSM 모드 전환 (편집/선택/드래그)
Object Pool 대량 아이템 재사용
Repository 데이터 소스 추상화