14 KiB
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)
- AI 핵심 행동 지침 (Core Directives)
- 1순위: UI Toolkit 개발 표준 (UI Toolkit)
- 2순위: 코드 품질 및 위생 (Code Quality)
- 3순위: 현대적 C# 및 아키텍처 (Modern C# & Architecture)
- 4순위: 성능 최적화 및 금지 패턴 (Performance)
- 5순위: 유니티 폴더 구조 표준 (Folder Structure)
- 6순위: 품질 자동화 (Quality Automation)
- 코드 자체 검증 체크리스트 (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 생명주기 메서드 순서:
Awake→OnEnable→Start→Update→OnDisable→OnDestroy