# {프로젝트명} — 데이터 파이프라인 > **버전**: 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 { // Dictionary 인덱스 — 자주 쓰는 키 기준으로 추가 정의 private Dictionary _byId; protected override void BuildIndex(List 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>(_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 | {날짜} | 개발팀장 | 템플릿 초안. 수상한잡화점 데이터 파이프라인 패턴 + 시뮬레이터 이원화 방지 규칙 반영 |