# {프로젝트명} — 데이터 파이프라인

> **버전**: v1
> **작성일**: {날짜}
> **담당**: 개발팀장
> **적용 범위**: {프로젝트명} 데이터 테이블 설계 및 로딩 구조

---

## 1. SOT(Single Source of Truth) 정의

| 항목 | 값 |
|------|-----|
| **SOT 포맷** | Excel (.xlsm 또는 .xlsx) |
| **SOT 경로** | `{Unity프로젝트경로}/Assets/ResWork/Table/{프로젝트명}.xlsm` |
| **변환 도구** | `{선택 예: Editor 스크립트(자동화 권장) / VBA 매크로 / Python 스크립트}` |
| **런타임 포맷** | JSON (UTF-8) |
| **JSON 경로** | `{Unity프로젝트경로}/Assets/ResWork/Table/Export/*.json` |
| **경로 규칙** | 테이블명 = Excel 시트명 = JSON 파일명 = C# 클래스명 (대소문자 일치) |

**SOT 규칙**:
- Excel 파일이 유일한 SOT. JSON은 Export 결과물이며 직접 편집 금지
- Excel 수정 후 Export 없이 Unity에 반영된 것처럼 처리하는 행위 금지 (C5 정직성)
- 백업 파일(`{프로젝트명}_Ino.xlsm` 등)과 SOT를 혼동하지 않도록 파일명 규칙 명확화

---

## 2. 파이프라인 흐름

```
[기획팀]                [개발팀]                     [런타임]
Excel 편집
    │
    ▼
Export Tool 실행
(Editor 스크립트 권장)
    │
    ▼
JSON 생성
Assets/ResWork/Table/Export/*.json
    │
    ▼
Unity AssetDatabase.Refresh()
(에디터 자동 감지)
    │
    ▼
Runtime DataTable Load
(Awake: TextAsset → json string cache)
(Start: JsonConvert.DeserializeObject → Dictionary)
    │
    ▼
Memory Dictionary Cache
O(1) 조회
```

**자동화 목표**: Excel 수정 → Export Tool 실행 → 반영까지 수동 단계 최소화.
수상한잡화점 교훈: Export 단계가 수동이면 기획 수정 후 반영 누락이 발생함.

---

## 3. DataTable 베이스 클래스 (NerdNavis.Framework 활용)

```csharp
// NerdNavis.Core.Data.DataTable 기반 — 직접 상속
// 프로젝트별 테이블은 아래 패턴으로 구현

using NerdNavis.Core.Data;
using Newtonsoft.Json;

namespace {ProjectNamespace}.Data
{
    // 테이블 행 데이터 클래스
    public class CardTableData : TableDataBase
    {
        [JsonProperty("_ID")] public int Id;
        [JsonProperty("_Name")] public string Name;
        [JsonProperty("_Damage")] public int Damage;
        // ... 각 컬럼 필드
    }

    // 테이블 매니저 클래스 (MonoSingleton + DataTable 조합)
    public class CardTable : DataTable<CardTableData>
    {
        // Dictionary 인덱스 — 자주 쓰는 키 기준으로 추가 정의
        private Dictionary<int, CardTableData> _byId;

        protected override void BuildIndex(List<CardTableData> records)
        {
            _byId = records.ToDictionary(r => r.Id);
        }

        // 권장: _orNull 패턴 (KeyNotFoundException 방지)
        public CardTableData GetByIdOrNull(int id)
            => _byId.TryGetValue(id, out var data) ? data : null;
    }
}
```

**수상한잡화점 교훈 — 금지 패턴**:
```csharp
// 금지: Direct 인덱서 → 미등록 키 시 KeyNotFoundException 런타임 크래시
public CardTableData Get(int id) => _byId[id]; // ❌

// 권장: TryGet 또는 _orNull 패턴
public CardTableData GetOrNull(int id)
    => _byId.TryGetValue(id, out var data) ? data : null; // ✅
```

---

## 4. 로딩 전략

| 시점 | 대상 테이블 | 방식 |
|------|------------|------|
| **앱 시작 (Bootstrap 씬)** | 핵심 공통 테이블 (GlobalValue, Localization 등) | 동기 전체 로딩 → `TableChecker.AllLoaded` 확인 후 진행 |
| **씬 전환 시** | 해당 씬에서만 필요한 테이블 | 씬 로딩 중 비동기 로딩 |
| **온디맨드** | 이벤트·대화 등 대용량·선택적 테이블 | 필요 시 Addressables 연동 로딩 |

**초기화 라이프사이클 (수상한잡화점 패턴 계승)**:
```csharp
// Awake: JSON 문자열 캐시 + TextAsset 객체 언로드 (메모리 절약)
private void Awake()
{
    _jsonCache = _textAsset.text;
    Resources.UnloadAsset(_textAsset);
}

// Start: 역직렬화 + Dictionary 인덱스 구축
private void Start()
{
    var records = JsonConvert.DeserializeObject<List<T>>(_jsonCache);
    BuildIndex(records);
    IsLoaded = true;
}
```

**메모리 상주 정책**:
- 전투 핫패스 테이블 (GlobalValue, CardList 등): 게임 실행 내내 상주
- 씬 전용 테이블: 씬 Unload 시 해제
- 로컬라이제이션 테이블: 언어 전환 시 재로딩

**에디터 핫 리로드** (개발 편의):
- Editor PlayMode에서 Excel Export 후 `AssetDatabase.Refresh()` → 자동 재로딩
- 런타임 핫패치는 현재 미지원 (필요 시 별도 설계)

---

## 5. 무결성 검증

### 5.1 Export 시 자동 검증 (Export Tool에 내장)

- FK 정합성: 참조 테이블의 ID 존재 여부 확인
- null 체크: 필수 컬럼의 빈 값 감지
- 범위 검증: 수치 컬럼의 최솟값·최댓값 범위 이탈 감지

### 5.2 런타임 검증 (개발 빌드 전용)

```csharp
// 개발 빌드에서만 실행되는 검증 코드
#if DEVELOPMENT_BUILD || UNITY_EDITOR
DataValidator.ValidateAll();
#endif
```

### 5.3 테이블 버전 메타

```json
{
  "version": "1.0.3",
  "exportedAt": "2026-04-16",
  "records": [ ... ]
}
```

JSON 헤더에 `version`/`exportedAt` 포함하여 롤백·동기화 추적 가능하게 한다.

---

## 6. ⚠️ 시뮬레이터 이원화 방지

> 런타임과 시뮬레이터/분석 도구가 **반드시 동일 데이터 소스**를 참조해야 한다.

**금지 패턴**:
- 기획팀 시뮬레이터가 Excel에서 직접 읽고, 런타임은 JSON을 읽는 구조 → 수치 불일치 발생
- 시뮬레이터 전용 파싱 로직이 별도 존재 → SOT 분기

**올바른 구조**:
```
Excel (SOT)
    │
    ├── Export → JSON (런타임 참조)
    │               └── DataTable 로딩 → 게임 실행
    │
    └── Export → JSON (시뮬레이터 참조)  ← 동일 JSON 파일 공유
                        └── 시뮬레이터/분석 도구
```

기획팀 밸런싱 시뮬레이터는 런타임 DataTable과 **동일한 JSON Export 파일**을 입력 소스로 사용한다.
별도 파싱 로직이 필요하다면 런타임 DataTable 클래스를 직접 재사용하거나, 공통 파서 모듈을 분리한다.

---

## 7. 리스크 및 개선 방향

| 항목 | 위험 | 개선 방향 |
|------|------|----------|
| 수동 Export | 기획 수정 후 반영 누락 | Export 자동화 스크립트 + CI 통합 |
| JSON 평문 저장 | APK 분해 시 테이블 노출 | 빌드 시 암호화 래퍼 또는 바이너리 포맷 검토 |
| Direct 인덱서 | 미등록 키 시 런타임 크래시 | `_orNull` / `TryGet` 패턴 전수 적용 |
| 핫패스 문자열 키 조회 | 모바일 GC/딕셔너리 조회 비용 | 상수 캐시 / enum 키 도입 |
| 테이블 버전 메타 없음 | 롤백·동기화 추적 곤란 | JSON 헤더에 version/exportedAt 도입 (섹션 5.3) |

---

## 변경 이력

| 버전 | 일자 | 작성자 | 내용 |
|------|------|--------|------|
| v1 | {날짜} | 개발팀장 | 템플릿 초안. 수상한잡화점 데이터 파이프라인 패턴 + 시뮬레이터 이원화 방지 규칙 반영 |
