Files
SHI_Sample_UI/CLAUDE.md
2026-03-09 01:05:56 +09:00

14 KiB

CLAUDE.md — Claude Code Instructions: Unity & C# Development Standards

이 문서는 Claude가 Unity 및 C# 프로젝트에서 코드를 작성, 리팩토링, 코드 리뷰할 때 반드시 준수해야 하는 **최우선 개발 표준(System Instructions)**입니다.


프로젝트 개요

Unity 프로젝트 (ClaudeCodeTest)

개발 환경

  • 엔진: Unity 6
  • 언어: C# (.NET)
  • 플랫폼: Windows 11

목차 (Table of Contents)

  1. AI 핵심 행동 지침 (Core Directives)
  2. 1순위: UI Toolkit 개발 표준 (UI Toolkit)
  3. 2순위: 코드 품질 및 위생 (Code Quality)
  4. 3순위: 현대적 C# 및 아키텍처 (Modern C# & Architecture)
  5. 4순위: 성능 최적화 및 금지 패턴 (Performance)
  6. 5순위: 유니티 폴더 구조 표준 (Folder Structure)
  7. 6순위: 품질 자동화 (Quality Automation)
  8. 코드 자체 검증 체크리스트 (Self-Review)

1. AI 핵심 행동 지침 (Core Directives)

Claude는 코드를 출력하기 전, 다음 3가지 핵심 규칙이 적용되었는지 반드시 확인합니다.

  • 안전성 (Safety): 모든 파일 상단에 #nullable enable을 포함하고 컴파일 경고가 발생하지 않도록 코드를 작성하라.
  • 취소 가능성 (Cancellability): 모든 UniTask 비동기 메서드에는 CancellationToken을 필수로 적용하라.
  • 성능 (Performance): Update() 등 매 프레임 호출되는 Hot Path 내에서는 가비지(GC)를 유발하거나 무거운 Unity API를 절대 호출하지 마라.

2. 1순위: UI Toolkit 개발 표준 (UI Toolkit)

2.1 핵심 원칙

  • UI Toolkit(UIElements) 필수 사용. uGUI(Canvas 기반)는 레거시로 취급한다.
  • UXML(구조)과 USS(스타일)를 분리하고, C# 코드에서 인라인 스타일 지정을 지양한다.
  • 파일 선두에 #nullable enable, 모든 참조형에 ? 명시.
  • 비동기는 UniTask + CancellationToken 사용 (Task/코루틴 지양).
  • 느슨한 결합: 인터페이스/이벤트로 레이어 간 연결.

2.2 이벤트 콜백 등록 규칙

⚠️ 중요: RegisterValueChangedCallback 대신 RegisterCallback<ChangeEvent<T>>를 사용한다.

RegisterValueChangedCallback은 확장 메서드로 UnregisterCallback과 대칭이 맞지 않아 이벤트 해제가 어렵다.

// ❌ 잘못된 예: 해제가 어려운 방식
field.RegisterValueChangedCallback(OnValueChanged);
// field.UnregisterValueChangedCallback(OnValueChanged); // 이런 메서드 없음!

// ✅ 올바른 예: 대칭적인 등록/해제
field.RegisterCallback<ChangeEvent<float>>(OnValueChanged);
field.UnregisterCallback<ChangeEvent<float>>(OnValueChanged);

private void OnValueChanged(ChangeEvent<float> evt)
{
    // evt.newValue, evt.previousValue 사용
}
메서드 권장 이유
RegisterValueChangedCallback 해제용 메서드 없음
RegisterCallback<ChangeEvent<T>> UnregisterCallback 대칭

2.3 UXML/USS 파일 네이밍 규칙

⚠️ 중요: UXML과 USS 파일명은 반드시 다르게 지정해야 한다.

Resources.Load<T>(path)는 확장자 없이 경로를 받기 때문에, 동일한 경로에 UXML과 USS가 모두 존재하면 로드 충돌이 발생할 수 있다.

// ❌ 잘못된 예: 동일한 경로명 사용
private const string UXML_PATH = "UIToolkit/Window/UTKAccordionListWindow";
private const string USS_PATH  = "UIToolkit/Window/UTKAccordionListWindow";

// Resources.Load<VisualTreeAsset>(UXML_PATH); // .uxml 로드
// Resources.Load<StyleSheet>(USS_PATH);       // .uss 로드 실패 가능

// ✅ 올바른 예: USS 파일명에 접미사 추가
private const string UXML_PATH = "UIToolkit/Window/UTKAccordionListWindow";
private const string USS_PATH  = "UIToolkit/Window/UTKAccordionListWindowUss";

// 파일 구조:
// - UTKAccordionListWindow.uxml
// - UTKAccordionListWindowUss.uss
파일 유형 네이밍 패턴 예시
UXML {ComponentName}.uxml UTKAccordionListWindow.uxml
USS {ComponentName}Uss.uss UTKAccordionListWindowUss.uss

2.4 커스텀 VisualElement (Unity 6)

Unity 6에서는 레거시 UxmlFactory/UxmlTraits 방식을 사용하지 않고, 소스 생성기 기반의 [UxmlElement][UxmlAttribute]를 사용한다.

필수 규칙:

  • 클래스에 [UxmlElement] 어트리뷰트 추가
  • 클래스를 partial로 선언 (소스 생성기 요구사항)
  • UXML 속성은 [UxmlAttribute]케밥 케이스 소문자 명시
// ✅ 올바른 예: Unity 6 방식
[UxmlElement]
public partial class UTKCodeBlock : VisualElement
{
    [UxmlAttribute("title")]
    public string Title { get; set; }

    [UxmlAttribute("is-enabled")]
    public bool IsEnabled { get; set; }

    [UxmlAttribute("border-width")]
    public int BorderWidth { get; set; }
}

// ❌ 잘못된 예: 레거시 방식 (사용 금지)
public class UTKCodeBlock : VisualElement
{
    public new class UxmlFactory : UxmlFactory<UTKCodeBlock, UxmlTraits> { }
    public new class UxmlTraits : VisualElement.UxmlTraits { ... }
}

// ❌ 잘못된 예: 케밥 케이스 미사용
[UxmlAttribute]           // Unity 6에서 UXML 속성 매핑 실패
public string Text { get; set; }

[UxmlAttribute("Text")]   // 대문자는 UXML과 불일치
public string Text { get; set; }

UXML 사용 예:

<utk:UTKCodeBlock title="예제" is-enabled="true" border-width="2" />

2.5 아키텍처 (MVVM/MVC)

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

3. 2순위: 코드 품질 및 위생 (Code Quality)

3.1 명명 규칙 (Microsoft C# Standard)

대상 규칙 예시
클래스, 구조체, 인터페이스(I 접두사 필수), 메서드, 프로퍼티, 이벤트, 상수(const) PascalCase PlayerController, IMovable, MaxSpeed
프라이빗(Private) 필드 _camelCase (_ 접두사 필수) _health, _rigidbody
로컬 변수, 매개변수 camelCase playerSpeed, cancellationToken

3.2 코드 설계 원칙

  • 접근 제어: 모든 필드와 메서드는 명시적 지시가 없는 한 private으로 캡슐화하라.
  • 예외 처리 강제: 논리적 오류나 유효하지 않은 상태에서는 Debug.Log 등으로 넘기지 말고, 즉시 throw new Exception(...) (또는 적절한 예외 타입)을 던져라.
  • 불변성 보장: 생성자 할당 이후 변경되지 않는 필드는 readonly, 컴파일 타임 상수는 const로 선언하라.
  • 자기 설명적 코드: 매직 스트링 대신 nameof()를 사용하며, 인라인 주석 없이도 이해할 수 있도록 명확한 식별자 명칭을 사용하라.

4. 3순위: 현대적 C# 및 아키텍처 (Modern C# & Architecture)

4.1 C# 9 최신 문법 활용

  • 식 본문 멤버 (Expression-bodied): 단일 행 로직은 괄호 {} 대신 화살표 =>를 사용하라.
  • Null 관리: 복잡한 Null 체크 대신 Null 병합 연산자(??, ??=)와 조건부 액세스(?.)를 적극 활용하라.
  • 패턴 매칭: 타입 검사 및 캐스팅 시 is 키워드 및 switch 식(Expression)을 사용하라.
  • 데이터 객체: 단순 데이터 보관용 타입은 record를 사용하고, init 키워드를 통해 불변성을 확보하라.

4.2 비동기 및 아키텍처 (UniTask & DI)

CancellationToken 필수 전파:

  • 생성하는 모든 UniTask 비동기 메서드는 매개변수로 CancellationToken을 받아야 한다.
  • 내부의 모든 비동기 호출(e.g., UniTask.Delay)에 해당 토큰을 넘겨주어, 객체 파괴 시 안전하게 작업이 취소되도록 하라.
  • MonoBehaviour 환경에서는 기본 토큰으로 this.GetCancellationTokenOnDestroy()를 획득하여 전달하라.

데이터 컨트롤러 패턴:

  • 데이터 모델의 직접적인 수정을 금지한다. 반드시 Controller 혹은 Service 계층을 통해서만 데이터를 조작하게 설계하라.

구독 해제 보장:

  • 이벤트나 신호(Signal)를 구독한 객체는 소멸 시점(Dispose, OnDestroy)에 반드시 구독을 해제하라.

5. 4순위: 성능 최적화 및 금지 패턴 (Performance)

5.1 Update 루프 (Hot Path) 내 금지 사항

  • 캐싱 의무화: Update 내에서 GetComponent 계열 메서드 호출을 절대 금지한다. Awake 또는 Start에서 미리 캐싱하여 사용하라.
  • GC 억제: Hot Path에서는 LINQ 구문(Where, Select 등) 사용을 지양하고 전통적인 for 루프를 작성하라.
  • 무거운 탐색 금지: GameObject.Find, FindObjectOfType, FindWithTag 사용을 절대 금지한다. DI(의존성 주입) 또는 인스펙터 참조를 사용하라.

5.2 유니티 비추천 패턴 (Anti-patterns)

  • 타입 불안정성: SendMessage, Invoke, StartCoroutine("string") 등 문자열 기반의 호출 방식을 사용하지 마라.
  • 안전한 컴포넌트 접근: GetComponent 호출 후 null을 체크하는 대신, TryGetComponent를 사용하여 성능을 확보하라.

6. 5순위: 유니티 폴더 구조 표준 (Folder Structure)

AI가 새로운 스크립트나 에셋을 생성할 때, 다음 구조를 기본으로 준수하여 경로를 설정하라.

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/                     # 앱 씬

7. 6순위: 품질 자동화 (Quality Automation)

7.1 .editorconfig 권장 설정

dotnet_diagnostic.CS1591.severity = error      # 공개 멤버 문서 주석 필수
dotnet_analyzer_diagnostic.category-Nullable.severity = error
dotnet_diagnostic.IDE0060.severity = warning   # 미사용 매개변수

7.2 CI 규칙

  • XML 문서 누락 / nullable 경고 → 빌드 실패 처리
  • 에디트 모드 테스트와 플레이 모드 테스트를 분리하여 실행

8. 코드 자체 검증 체크리스트 (Self-Review)

Claude는 응답을 생성하기 전 내부적으로 다음 사항을 점검하십시오.

  • 파일 최상단에 #nullable enable을 선언했는가?
  • 프라이빗 변수명에 _ 접두사를 붙였는가?
  • 에러 상황을 Debug.Log가 아닌 throw Exception으로 처리했는가?
  • 작성한 모든 UniTask 비동기 함수에 CancellationToken이 적용되었는가?
  • 매 프레임 실행되는 코드(Update)에 가비지나 오버헤드(LINQ, GetComponent)가 없는가?
  • 새 스크립트는 Assets/Scripts/{프로젝트명}/ 또는 Assets/Scripts/UVC/ 하위 올바른 경로에 배치했는가?
  • 새 UXML/USS 리소스는 Assets/Resources/UIToolkit/ 하위 올바른 경로에 배치했는가?
  • UXML 파일명과 USS 파일명이 다르게 지정되었는가? (USS는 {Name}Uss.uss 형식)
  • UI 코드에서 RegisterValueChangedCallback 대신 RegisterCallback<ChangeEvent<T>>를 사용했는가?
  • 커스텀 VisualElement에 [UxmlElement], partial, [UxmlAttribute("케밥-케이스")]를 적용했는가?
  • uGUI(Canvas) 컴포넌트를 새로 사용하지 않았는가?

Unity 일반 주의사항

  • Library/ 폴더 내 파일은 절대 수정하지 말 것 (Unity 자동 관리)
  • .meta 파일은 Unity가 자동 생성하므로 직접 편집 자제
  • 씬 파일(.unity)이나 프리팹(.prefab) 수정 시 Unity Editor에서 확인 필요
  • Unity 생명주기 메서드 순서: AwakeOnEnableStartUpdateOnDisableOnDestroy