# {프로젝트명} — 코딩 컨벤션

> **버전**: v1
> **작성일**: {날짜}
> **담당**: 개발팀장
> **적용 범위**: {프로젝트명} 전 C# 코드

---

## 1. 네이밍 규칙

> 아래 규칙은 NerdNavis.Framework 아키텍처 설계안(네이밍 매핑표)을 기반으로 한다.
> `My*` 접두사, `u*` 접두사, `Mgr` 접미사는 **전면 금지** (수상한잡화점 패턴 계승 금지).

| 대상 | 규칙 | 예시 |
|------|------|------|
| **네임스페이스** | PascalCase | `{ProjectNamespace}.Core` |
| **클래스** | PascalCase | `GameManager`, `PlayerController` |
| **인터페이스** | `I` + PascalCase | `ISaveProvider`, `INetworkService` |
| **메서드** | PascalCase | `LoadData()`, `HandleDamage()` |
| **프로퍼티** | PascalCase | `CurrentHp`, `IsGameOver` |
| **private 필드** | `_` + camelCase | `_currentHp`, `_isInitialized` |
| **const / static readonly** | SCREAMING_SNAKE_CASE | `MAX_HP`, `DEFAULT_TIMEOUT` |
| **enum 타입** | PascalCase | `GameState`, `ItemType` |
| **enum 값** | PascalCase | `GameState.Playing`, `ItemType.Weapon` |
| **지역 변수** | camelCase | `int damage`, `string key` |
| **이벤트 구조체(EventBus용)** | PascalCase + `Event` 접미사 | `PlayerDiedEvent`, `StageCompleteEvent` |

**금지 패턴**:
- `My*` 접두사: `MyCoroutine` → `CoroutineRunner` 사용
- `u*` 소문자 접두사: `uScrollViewMgr` → `ScrollViewController`
- `Mgr` 접미사: `UIAtlasMgr` → `UIAtlasManager`
- `Base` 접미사 (추상 클래스): `CustomParserBase` → `abstract class CustomParser`
- `Info` 접미사 (데이터 클래스): `GachaGradeInfo` → `GachaGrade`
- `Handler` 접미사 (스케줄러): `SchedulHandler` → `GameScheduler`

---

## 2. 파일 구조

```csharp
// 헤더 주석 (선택, 복잡한 파일만)
// 파일명: PlayerController.cs
// 네임스페이스: {ProjectNamespace}.Core

using System;
using System.Collections.Generic;
using UnityEngine;
using NerdNavis.Core.Patterns;   // Framework 참조는 using 으로만

namespace {ProjectNamespace}.Core
{
    public class PlayerController : MonoBehaviour
    {
        // 1. 직렬화 필드 (Inspector)
        [SerializeField] private int _maxHp = 100;

        // 2. private 필드
        private int _currentHp;

        // 3. 프로퍼티
        public int CurrentHp => _currentHp;

        // 4. Unity 라이프사이클 메서드 (Awake > Start > Update > ...)
        private void Awake() { }
        private void Start() { }

        // 5. public 메서드
        public void TakeDamage(int amount) { }

        // 6. private 메서드
        private void Die() { }
    }
}
```

**규칙**:
- 1파일 = 1클래스 (partial class 예외: 에디터 확장, 자동 생성 코드)
- 파일명 = 클래스명 (대소문자 포함 일치)
- 폴더 경로 ≈ 네임스페이스 구조

---

## 3. NerdNavis.Framework 사용 패턴

### 3.1 MonoSingleton<T> — MonoBehaviour 싱글톤

```csharp
// 씬 라이프사이클과 동기화되는 서비스에 사용
// Update/OnDestroy 훅이 필요한 경우
public class GameManager : MonoSingleton<GameManager>
{
    // [SingletonOption(Persistent = true)] 로 DontDestroyOnLoad 제어
    protected override void OnAwake()
    {
        // 초기화 코드
    }
}

// 사용
GameManager.Instance.StartGame();
```

### 3.2 ServiceLocator — 순수 C# 서비스 DI

```csharp
// MonoBehaviour와 독립된 서비스, 인터페이스 바인딩, 테스트 Mock 주입 시 사용
// Bootstrap 씬에서 등록
ServiceLocator.Register<ISaveProvider>(new JsonSaveProvider());
ServiceLocator.Register<INetworkService>(new PlayFabNetworkService());

// 사용처
var save = ServiceLocator.Resolve<ISaveProvider>();
save.Save(playerData);

// 테스트 시 Mock 교체
ServiceLocator.Register<ISaveProvider>(new MockSaveProvider());
ServiceLocator.Clear(); // 테스트 후 정리
```

**주의**: 인터페이스 타입으로만 등록·조회 (구체 타입 바인딩 시 결합도 상승).
`Resolve` 실패 시 `ServiceNotRegisteredException` 발생 — silent null 금지.

### 3.3 CoroutineRunner — 전역 코루틴 관리

```csharp
// 키 기반 중복 제어, 일시정지·재시작 지원
// MonoBehaviour 없이 코루틴 실행 가능

// 시작 (키로 중복 방지)
CoroutineRunner.Start("LoadData", LoadDataCoroutine());

// 중지
CoroutineRunner.Stop("LoadData");

// 완료 시 자동 해제
```

### 3.4 Log — 중앙 로거

```csharp
// Debug.Log 직접 호출 금지 → Log 클래스 사용
Log.Info("GameManager", "게임 시작");
Log.Warn("DataManager", $"데이터 키 없음: {key}");
Log.Error("ServerManager", "서버 연결 실패", exception);

// 카테고리 필터링 → 빌드 변형별 로그 레벨 제어 가능
```

### 3.5 EventBus — 타입 안전 이벤트

```csharp
// 이벤트 정의 (struct 권장)
public struct PlayerDiedEvent
{
    public int PlayerId;
    public string Cause;
}

// 구독
EventBus.Subscribe<PlayerDiedEvent>(OnPlayerDied);
private void OnPlayerDied(PlayerDiedEvent e) { /* ... */ }

// 발행
EventBus.Publish(new PlayerDiedEvent { PlayerId = 1, Cause = "Fall" });

// 구독 해제 (OnDestroy에서 반드시 해제)
EventBus.Unsubscribe<PlayerDiedEvent>(OnPlayerDied);
```

**금지**: 문자열 키 이벤트 (`EventBus.Raw` 하위 특수 용도만 허용).

---

## 4. 금지 사항

| 금지 항목 | 사유 | 대안 |
|-----------|------|------|
| **수치 하드코딩** | 수정 시 코드 수정 필요, 기획-개발 분리 파괴 | DataTable 참조 (04_데이터_파이프라인 참조) |
| **암호키 평문 하드코딩** | IL2CPP 빌드도 메모리 덤프로 추출 가능 (수상한잡화점 Critical) | 서버 측 키 관리 또는 런타임 유도 (02_기술스택 참조) |
| **`Resources.Load` 남용** | 빌드 시 모든 Resources/ 포함 → 빌드 크기 증가 | Addressables |
| **문자열 이벤트 키** | 컴파일 타임 검증 불가, 오타 런타임 버그 | `EventBus<TEvent>` 타입 이벤트 |
| **`Find` / `FindWithTag` 게임 로직** | 씬 오브젝트 의존, 테스트 불가 | 직접 참조 or ServiceLocator |
| **`My*` / `u*` 접두사** | 비표준, 가독성 저하 | 표준 네이밍 규칙(섹션 1) |
| **`Assembly-CSharp` 단일 어셈블리** | 컴파일 시간 증가, 의존성 관리 불가 (수상한잡화점 교훈) | asmdef 분리 (01_아키텍처_설계 참조) |
| **전역 네임스페이스** | 클래스명 충돌 위험 | `{ProjectNamespace}.*` 체계 |
| **`Debug.Log` 직접 호출 (배포 코드)** | 릴리스 빌드 로그 노출 | `Log.Info/Warn/Error` |

---

## 5. Git 커밋 메시지

```
{type}: {summary}

[optional body]
```

| type | 용도 |
|------|------|
| `feat` | 신규 기능 추가 |
| `fix` | 버그 수정 |
| `refactor` | 기능 변경 없는 코드 구조 개선 |
| `docs` | 문서 변경 |
| `test` | 테스트 추가/수정 |
| `chore` | 빌드·설정·의존성 변경 |
| `data` | 데이터 테이블 변경 |

**예시**:
```
feat: 전투 데미지 계산 서버 검증 구조 추가
fix: DataTable 로딩 완료 확인 타이밍 버그 수정
refactor: UIManager 씬 전환 로직 분리
data: CardList 밸런스 조정 반영 (기획팀 승인)
```

---

## 변경 이력

| 버전 | 일자 | 작성자 | 내용 |
|------|------|--------|------|
| v1 | {날짜} | 개발팀장 | 템플릿 초안. Framework 3축(MonoSingleton·ServiceLocator·EventBus) 패턴 정립 |
