BurningTimesAi/프로젝트/신규 프로젝트/개발/04_데이터_파이프라인.md

220 lines
7.1 KiB
Markdown
Raw Normal View History

# {프로젝트명} — 데이터 파이프라인
> **버전**: 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 단계가 수동이면 기획 수정 후 반영 누락이 발생함.
---
feat(BT·신설): 조직 전환 Phase 2-A — Skill/Framework rename + 조직명 치환 + 새 프로젝트 셋업 PD님 2026-04-21 지시 8개 중 ②③④⑤⑥⑦⑧ 구조 전환부 이행 (①③ 노하우 재정리는 Phase 2-B, 삭제는 Phase 2-C). ## 집행 내역 1. .claude/skills/너드나비스-코어룰/ → BurningTimes-코어룰/ (Move-Item, R096·R100) 2. 코어코드/NerdNavis.Framework/ → 코어코드/BT.Framework/ (Move-Item, R100·R073·R081) 3. sed 일괄 치환 201파일 (치환 순서: NerdNavis.Framework → BT.Framework **선행** → NerdNavis → BurningTimes → 너드나비스 → BurningTimes) - 제외: .git/·.live/·.bak_*·memory/org/audit_logs/ 4. paths.local.json.template: DISCORD_WEBHOOK 추가 + Unity/Framework __SET_PER_PC__ placeholder 5. paths.local.json 실파일 생성 (gitignore, 본 PC 실값: Unity E:/NerdNavis/EerieVillage + Discord 웹훅) 6. 프로젝트/EerieVillage/ 신설 (개발·기획·관리 + README: 기묘한 고을: 조선퇴마뎐, Unity 6000.3.13f1 LTS, 2D PlatformerMicrogame) 7. README.md clone URL NerdNavis_AiDev/BurningTimesAi.git 실 URL 정정 8. 대화로그 공유/대화로그/조직운영/2026-04-21.md 신설 9. PD 지시 로그 양팀 BT1·BT2 항목 등록 (진행중) ## NerdNavis 의도적 잔존 3종 (C5 정직성) - GIT_REMOTE URL: Gitea 조직 hierarchy NerdNavis_AiDev (PD 별도 결정 영역) - UNITY_PROJECT_ROOT 실값: E:/NerdNavis/EerieVillage (PD 지시 6번) - EerieVillage README "BT.Framework (구 NerdNavis.Framework 계승)" 이력 표기 ## 감사 pm-auditor 사전 감사 Critical 2건 (대화로그·PD 지시 로그 선등록) 정정 완료. 매니페스트: bt-phase2a. ## 보류 (Phase 2-B → 2-C 예정) - Phase 2-B: 전 에이전트 동원 수상한잡화점 시행착오 노하우 추출·조직 자산화 - Phase 2-C: 수상한잡화점 일괄 삭제 + memory/org feedback "수상한잡화점" → "이전 프로젝트" 추상화 + PD 지시 로그 초기화 + 조직공지 정리 + SKILL P17·P29 재해석 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 15:44:48 +00:00
## 3. DataTable 베이스 클래스 (BT.Framework 활용)
```csharp
feat(BT·신설): 조직 전환 Phase 2-A — Skill/Framework rename + 조직명 치환 + 새 프로젝트 셋업 PD님 2026-04-21 지시 8개 중 ②③④⑤⑥⑦⑧ 구조 전환부 이행 (①③ 노하우 재정리는 Phase 2-B, 삭제는 Phase 2-C). ## 집행 내역 1. .claude/skills/너드나비스-코어룰/ → BurningTimes-코어룰/ (Move-Item, R096·R100) 2. 코어코드/NerdNavis.Framework/ → 코어코드/BT.Framework/ (Move-Item, R100·R073·R081) 3. sed 일괄 치환 201파일 (치환 순서: NerdNavis.Framework → BT.Framework **선행** → NerdNavis → BurningTimes → 너드나비스 → BurningTimes) - 제외: .git/·.live/·.bak_*·memory/org/audit_logs/ 4. paths.local.json.template: DISCORD_WEBHOOK 추가 + Unity/Framework __SET_PER_PC__ placeholder 5. paths.local.json 실파일 생성 (gitignore, 본 PC 실값: Unity E:/NerdNavis/EerieVillage + Discord 웹훅) 6. 프로젝트/EerieVillage/ 신설 (개발·기획·관리 + README: 기묘한 고을: 조선퇴마뎐, Unity 6000.3.13f1 LTS, 2D PlatformerMicrogame) 7. README.md clone URL NerdNavis_AiDev/BurningTimesAi.git 실 URL 정정 8. 대화로그 공유/대화로그/조직운영/2026-04-21.md 신설 9. PD 지시 로그 양팀 BT1·BT2 항목 등록 (진행중) ## NerdNavis 의도적 잔존 3종 (C5 정직성) - GIT_REMOTE URL: Gitea 조직 hierarchy NerdNavis_AiDev (PD 별도 결정 영역) - UNITY_PROJECT_ROOT 실값: E:/NerdNavis/EerieVillage (PD 지시 6번) - EerieVillage README "BT.Framework (구 NerdNavis.Framework 계승)" 이력 표기 ## 감사 pm-auditor 사전 감사 Critical 2건 (대화로그·PD 지시 로그 선등록) 정정 완료. 매니페스트: bt-phase2a. ## 보류 (Phase 2-B → 2-C 예정) - Phase 2-B: 전 에이전트 동원 수상한잡화점 시행착오 노하우 추출·조직 자산화 - Phase 2-C: 수상한잡화점 일괄 삭제 + memory/org feedback "수상한잡화점" → "이전 프로젝트" 추상화 + PD 지시 로그 초기화 + 조직공지 정리 + SKILL P17·P29 재해석 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 15:44:48 +00:00
// BurningTimes.Core.Data.DataTable 기반 — 직접 상속
// 프로젝트별 테이블은 아래 패턴으로 구현
feat(BT·신설): 조직 전환 Phase 2-A — Skill/Framework rename + 조직명 치환 + 새 프로젝트 셋업 PD님 2026-04-21 지시 8개 중 ②③④⑤⑥⑦⑧ 구조 전환부 이행 (①③ 노하우 재정리는 Phase 2-B, 삭제는 Phase 2-C). ## 집행 내역 1. .claude/skills/너드나비스-코어룰/ → BurningTimes-코어룰/ (Move-Item, R096·R100) 2. 코어코드/NerdNavis.Framework/ → 코어코드/BT.Framework/ (Move-Item, R100·R073·R081) 3. sed 일괄 치환 201파일 (치환 순서: NerdNavis.Framework → BT.Framework **선행** → NerdNavis → BurningTimes → 너드나비스 → BurningTimes) - 제외: .git/·.live/·.bak_*·memory/org/audit_logs/ 4. paths.local.json.template: DISCORD_WEBHOOK 추가 + Unity/Framework __SET_PER_PC__ placeholder 5. paths.local.json 실파일 생성 (gitignore, 본 PC 실값: Unity E:/NerdNavis/EerieVillage + Discord 웹훅) 6. 프로젝트/EerieVillage/ 신설 (개발·기획·관리 + README: 기묘한 고을: 조선퇴마뎐, Unity 6000.3.13f1 LTS, 2D PlatformerMicrogame) 7. README.md clone URL NerdNavis_AiDev/BurningTimesAi.git 실 URL 정정 8. 대화로그 공유/대화로그/조직운영/2026-04-21.md 신설 9. PD 지시 로그 양팀 BT1·BT2 항목 등록 (진행중) ## NerdNavis 의도적 잔존 3종 (C5 정직성) - GIT_REMOTE URL: Gitea 조직 hierarchy NerdNavis_AiDev (PD 별도 결정 영역) - UNITY_PROJECT_ROOT 실값: E:/NerdNavis/EerieVillage (PD 지시 6번) - EerieVillage README "BT.Framework (구 NerdNavis.Framework 계승)" 이력 표기 ## 감사 pm-auditor 사전 감사 Critical 2건 (대화로그·PD 지시 로그 선등록) 정정 완료. 매니페스트: bt-phase2a. ## 보류 (Phase 2-B → 2-C 예정) - Phase 2-B: 전 에이전트 동원 수상한잡화점 시행착오 노하우 추출·조직 자산화 - Phase 2-C: 수상한잡화점 일괄 삭제 + memory/org feedback "수상한잡화점" → "이전 프로젝트" 추상화 + PD 지시 로그 초기화 + 조직공지 정리 + SKILL P17·P29 재해석 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 15:44:48 +00:00
using BurningTimes.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 | {날짜} | 개발팀장 | 템플릿 초안. 수상한잡화점 데이터 파이프라인 패턴 + 시뮬레이터 이원화 방지 규칙 반영 |