BurningTimesAi/프로젝트/코어프레임워크/04_Tier1_3종_상호작용_설계_v1.md

280 lines
14 KiB
Markdown
Raw Permalink Normal View History

# Tier 1 3종 상호작용 설계 v1 — Event · Container · Data
> **작성자**: 개발팀장 (PD님 #36 즉시 수행 지시, 2026-04-17)
> **상태**: 초안 → 구현 병행 반영
chore(BT·residual): 수상한잡화점·너드나비스 잔존 최종 정리 PD님 최종 점검 요청에 따라 실측 잔존 정리. ## asmdef 4개 rename (파일명 BT.Framework.* 통일) - 코어코드/BT.Framework/Editor/NerdNavis.Framework.Editor.asmdef → BT.Framework.Editor.asmdef - Runtime/NerdNavis.Framework.asmdef → BT.Framework.asmdef - Tests/Editor/NerdNavis.Framework.Editor.Tests.asmdef → BT.Framework.Editor.Tests.asmdef - Tests/Runtime/NerdNavis.Framework.Tests.asmdef → BT.Framework.Tests.asmdef - 내부 name·rootNamespace는 이미 BT.Framework·BurningTimes로 치환 완료 상태 - .meta 파일 부재 (Unity 프로젝트 미편입) → rename만으로 안전 ## 코어 프레임워크 본문 추상화 - BT.Framework/README.md · CoroutineRunner.cs · KeyMaker.cs: "수상한 잡화점" → "이전 프로젝트" - 프로젝트/코어프레임워크/01·03·04_*.md: 동일 추상화 ## 과거 프로젝트 전용 스크립트 삭제 - scripts/md_to_docx.js: 이전 프로젝트 서버 docx 생성 전용 (146·228줄 "수상한잡화점 서버 파트" 하드코딩). BT 활용 가치 0 → 삭제 ## 기타 문구 정리 - 시행착오 아카이브 README: "이전 NerdNavis 조직의 수상한잡화점 프로젝트" → "이전 게임 개발 프로젝트" 자연화 - 감사_plan_auditor_v1 frontmatter: "너드나비스 → BurningTimes, 수상한잡화점 plan-auditor" → "이전 프로젝트 plan-auditor" - INDEX.md: "이전 NerdNavis 조직의 수상한잡화점" → "이전 조직의 이전 프로젝트" - 2026-04-18 세션교훈 공지: "수상한 잡화점" → "이전 프로젝트" ## 의도적 잔존 (C5 정직성 · 역사 기록) - 2026-04-21 대화로그 (본 세션 PD 지시·집행 기록) - PD 지시 로그 BT2 (PD 지시 원문 인용) - SKILL.md 1463 (폐기 아카이브 링크 설명) - feedback_agent_path_boundary (Phase 2-B 실증) - paths.local.json·README.md (외부 URL·PD 지시 경로) - 2026-04-16~20 대화로그 (당시 시점 기록) ## Discord 웹훅 실증 본 push로 Gitea → Discord 웹훅 알림 작동 확인 예정 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 17:05:55 +00:00
> **참조**: `01_아키텍처_개요_v1.md` §4-2~§4-3 (EventBus·ObservableList 개선안), `02_이전 프로젝트_추출대상_v1.md` (Data 추출 대상)
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
> **적용 범위**: `BT.Framework` 내 `BurningTimes.Core.Event`·`BurningTimes.Core.Container`·`BurningTimes.Core.Data` 3개 모듈의 공개 API 경계와 상호 의존 방향
---
## 0. 왜 이 문서가 필요한가 (P18 설계 문서화)
Tier 1 잔여 3종(Data·Event·Container)은 서로 **일부 기능이 겹치는 것처럼 보이나 역할이 명확히 다르며**, 경계 없이 구현하면 아래 위험이 발생한다.
- `Container`의 "변경 이벤트"와 `Event`의 "타입 안전 이벤트 버스"가 동일 문제를 두 번 푸는 것처럼 보이는 중복 설계
- `Data`(마스터 테이블) 로딩 완료를 어디서 통지할지 불분명 → 수신자가 폴링으로 풀게 되는 반패턴
- 세 모듈 간 **의존 방향이 순환**하면 assembly definition(asmdef) 분리가 불가능해지고 Tier 2 모듈의 단일 의존 원칙(§ 아키텍처 §2) 붕괴
본 문서는 이 세 모듈의 경계를 확정하여, 이후 Tier 2 모듈(Save/Audio/Economy 등)이 안전하게 Tier 1 위에 얹힐 수 있도록 한다.
---
## 1. 각 모듈의 단일 책임 (Single Responsibility)
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
### 1-1. `BurningTimes.Core.Event` — 타입 안전 **프로세스 내 이벤트 버스**
- 목적: 서로 모르는 객체들 사이의 **1:N 비동기 통지** (느슨한 결합)
- 스케일: "전역 관심사"를 선언한 쪽이 Publish, 듣고 싶은 쪽이 Subscribe
- 특징: 이벤트를 **타입**(struct 또는 class)으로 식별, 문자열 키 아님
- 비목적: 객체 내부 상태 변경 알림(이건 Container의 책임), 파일 I/O·네트워크(상위 Tier)
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
### 1-2. `BurningTimes.Core.Container` — **관찰 가능한 자료구조**
- 목적: **하나의 컬렉션의 변경을 구독자가 추적**할 수 있는 List/Dictionary/Queue
- 스케일: 단일 컬렉션 인스턴스의 내부 변경 알림 (로컬)
- 특징: 변경이 발생한 **자기 자신**이 이벤트를 직접 발행(인스턴스 이벤트, 전역 버스 아님)
- 비목적: 전역 이벤트 전파(이건 Event의 책임), 영속화
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
### 1-3. `BurningTimes.Core.Data` — **마스터 테이블 로더**
- 목적: 기획·밸런싱 정적 데이터(CSV·JSON)를 런타임에서 **타입 안전하게 조회**
- 스케일: 애플리케이션 수명 동안 보통 1회 로드, 키 기반 조회 빈번
- 특징: `DataTable<TKey, TRow>`는 읽기 전용 뷰 + 인덱스, `DataTableSO`는 Unity ScriptableObject 래퍼
- 비목적: 유저 세이브 데이터(Tier 2 Save), 런타임 쓰기(관찰 컬렉션은 Container)
### 1-4. 경계 판정 기준 (혼동 방지)
| 상황 | 어느 모듈을 쓸 것인가 |
|------|---------------------|
| "플레이어 사망"을 UI·사운드·업적이 동시에 반응 | **Event**`PlayerDiedEvent` struct 발행 |
| 인벤토리 리스트가 바뀌면 UI가 슬롯을 다시 그림 | **Container**`ObservableList<ItemStack>` 변경 이벤트 구독 |
| 몬스터 ID로 `MonsterDef` 레코드 조회 | **Data**`DataTable<int, MonsterDef>.Get(id)` |
| 마스터 테이블 로딩이 끝났음을 전역에 알림 | **Event** + **Data** (Data가 자기 이벤트 struct를 Publish) |
---
## 2. 의존 방향 (DAG, 순환 금지)
```
Data ──────────┐
Event
Container ─────┘
```
- **Data → Event**: Data는 로딩 완료·재로드 통지를 위해 Event를 **옵션으로** 사용한다. Data 내부 핵심 로직은 Event 없이도 동작해야 한다(테스트 격리성). Event는 "통지 채널"로만 쓰이며 Data의 조회 API에는 개입하지 않는다.
- **Container → Event**: Container는 `Event`**의존하지 않는다**. 관찰은 **인스턴스 이벤트(C# `event` 키워드)** 로 제공. 전역 전파가 필요하면 사용자가 Container 이벤트를 수신한 뒤 직접 EventBus로 다시 Publish한다(분리 원칙).
- **Event ← Container**: 역방향 참조 없음.
- **Event → Data / Container**: 역방향 참조 없음. Event는 순수 범용 모듈.
**결론**: Event가 가장 원시(primitive), Data·Container는 동일 Tier이나 Event에만 **한방향**으로 의존. 순환 없음.
### 2-1. asmdef 반영 (구현 가이드)
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
- `BT.Framework.asmdef` 단일 asmdef 유지 (현재 구조)
- 만약 장래에 모듈별 asmdef 분리 시: `Event``Container`·`Data` 순서로 의존성 선언
---
## 3. 공개 API 경계 (implementation contract)
### 3-1. `EventBus` (Event)
```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
namespace BurningTimes.Core.Event
{
public static class EventBus
{
public static void Subscribe<TEvent>(Action<TEvent> handler);
public static void Unsubscribe<TEvent>(Action<TEvent> handler);
public static void Publish<TEvent>(TEvent e);
public static void Clear<TEvent>(); // 테스트·씬 전환용
public static void ClearAll(); // 테스트·애플리케이션 종료용
}
}
```
**계약**:
- 스레드 안전은 **메인 스레드 전제** (Unity 표준). 멀티스레드는 Tier 3 Network 도입 시 재검토.
- `Publish` 중 핸들러가 예외를 던져도 나머지 구독자는 호출 보장(catch-and-log).
- `Publish` 중 발생한 Subscribe/Unsubscribe는 **다음 Publish에 반영** (iteration snapshot 방식).
- 이벤트 타입은 struct 권장(박싱 회피). class도 허용.
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.Event.Raw.RawEventBus` 하위 네임스페이스에 분리 (특수 용도).
### 3-2. `ObservableList<T>` / `ObservableDictionary<TKey,TValue>` / `ObservableQueue<T>` (Container)
```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
namespace BurningTimes.Core.Container
{
public class ObservableList<T> : IList<T>, IReadOnlyList<T>
{
public event Action<int, T> Added; // (index, item)
public event Action<int, T> Removed; // (index, item)
public event Action Reset; // Clear·대량 교체
// 표준 IList<T> 멤버...
}
public class ObservableDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
public event Action<TKey, TValue> Added;
public event Action<TKey, TValue> Removed;
public event Action<TKey, TValue, TValue> Updated; // (key, oldValue, newValue)
public event Action Reset;
// 표준 IDictionary 멤버...
}
public class ObservableQueue<T> : IReadOnlyCollection<T>
{
public event Action<T> Enqueued;
public event Action<T> Dequeued;
public event Action Reset;
// Enqueue/Dequeue/Peek/Count/Clear
}
}
```
**계약**:
- 이벤트는 **인스턴스 멤버** (전역 아님). 여러 리스트가 있으면 각각 자기 이벤트 발행.
- 이벤트 핸들러에서 같은 컬렉션을 변경해도 **현재 iteration은 깨지지 않음** (iteration snapshot 또는 deferred ops).
- null 핸들러 안전 (`?.Invoke`).
- 기존 `UniList`·`UniEventList`·`UniObserverList` 3종이 본 1종으로 통합 (§01 §4-3).
### 3-3. `DataTable<TKey, TRow>` / `DataTableSO` (Data)
```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
namespace BurningTimes.Core.Data
{
public interface IDataRow<TKey> { TKey Key { get; } }
public class DataTable<TKey, TRow> where TRow : IDataRow<TKey>
{
public IReadOnlyList<TRow> Rows { get; }
public bool TryGet(TKey key, out TRow row);
public TRow Get(TKey key); // 없으면 예외
public bool Contains(TKey key);
public int Count { get; }
public DataTable(IEnumerable<TRow> rows);
public static DataTable<TKey, TRow> FromCsv(string csvText, Func<string[], TRow> rowFactory);
public static DataTable<TKey, TRow> FromJson(string jsonText);
}
public abstract class DataTableSO<TKey, TRow> : ScriptableObject
where TRow : IDataRow<TKey>
{
public DataTable<TKey, TRow> Table { get; }
public void Load(); // 런타임 초기화 훅
}
// 표준 이벤트 (Event 모듈과 연동)
public readonly struct DataTableLoadedEvent
{
public readonly Type TableType;
public readonly int RowCount;
}
}
```
**계약**:
- `DataTable<TKey,TRow>`**불변 스냅샷** (로드 후 Rows 변경 불가). 변경이 필요하면 `Container` 사용.
- 로딩 완료 통지는 사용자 선택 — 필요 시 `EventBus.Publish(new DataTableLoadedEvent { ... })`.
- CSV 파싱 기본은 최소 구현(쉼표·따옴표 이스케이프만). 고도화 필요 시 `CustomParser` 훅.
- Editor 측 CSV/Excel 컨버터는 `Editor/Data/`로 분리(§01 §2 구조). 런타임은 파싱된 결과만 소비.
---
## 4. 상호작용 시나리오 (설계 검증)
### 4-1. 마스터 테이블 로드 후 이벤트 통지
```csharp
// 부트 코드
var monsterTable = DataTable<int, MonsterDef>.FromJson(File.ReadAllText(path));
DataRegistry.Register(monsterTable);
EventBus.Publish(new DataTableLoadedEvent { TableType = typeof(MonsterDef), RowCount = monsterTable.Count });
// 수신 쪽 (UI·사운드·스폰 등)
EventBus.Subscribe<DataTableLoadedEvent>(evt => {
if (evt.TableType == typeof(MonsterDef)) RefreshMonsterList();
});
```
- Data가 Event를 **쓰지만 의존하지 않는다**: 위 Publish 코드는 "사용자 코드" 영역. DataTable 자체는 Event 없이도 완결.
### 4-2. 관찰 컬렉션 변경을 전역에 2차 전파
```csharp
var inventory = new ObservableList<ItemStack>();
inventory.Added += (idx, item) => EventBus.Publish(new InventoryItemAddedEvent { Item = item });
```
- Container는 자기 이벤트만 내고, **사용자가 선택적으로** EventBus에 연결. Container는 Event를 import조차 하지 않음.
### 4-3. Data는 로드 후 Container로 "뷰"를 제공하지 않는다
- `DataTable`은 읽기 전용 정적 데이터용, `Container`는 런타임 가변 상태용. 서로 역할이 겹치지 않음.
- 잘못된 예: `ObservableList<MonsterDef>`로 마스터 테이블을 담는 것 → 마스터는 불변이므로 `DataTable`이 정답.
---
## 5. 검증 방법 (테스트 설계)
### 5-1. Event
- Subscribe/Publish/Unsubscribe 기본 플로우
- 다중 구독자 호출 순서(등록 순)
- Publish 중 예외 발생해도 나머지 호출 보장
- Publish 중 Subscribe가 현재 iteration에 포함되지 않음
- `Clear<TEvent>()`·`ClearAll()` 동작
### 5-2. Container
- `ObservableList`: Add/Insert/Remove/RemoveAt/Clear 각 이벤트
- `ObservableDictionary`: Add/Remove/[key]=value(Updated)/Clear
- `ObservableQueue`: Enqueue/Dequeue/Clear
- 이벤트 핸들러 내 재변경 안전성 (iteration 깨짐 금지)
### 5-3. Data
- `DataTable` 기본 조회(Get·TryGet·Contains·Count)
- 불변성 — 생성 후 Rows 길이 고정
- `FromCsv` 기본 케이스 (쉼표·따옴표 이스케이프·빈 줄 무시)
- `FromJson` 기본 케이스 (Unity JsonUtility 또는 Newtonsoft.Json 선택 — 본 구현은 `JsonUtility` 기본, 한계 명시)
- `DataTableLoadedEvent` 발행 수동 테스트(사용자 코드 패턴 검증)
---
## 6. 선택된 방향과 대안 (Trade-off / 기각안 영구 기록)
### 6-1. Event: 인스턴스 버스 vs 정적 버스
- **채택**: 정적 `EventBus` (§01 §4-2 예시와 일치)
- **기각**: 인스턴스 `EventBus` 주입형 (IoC)
- **기각 이유**: 게임 런타임 단일 프로세스 전제에서 정적이 토큰·호출 비용 최저, Unity 환경 광범위 관례. 테스트 격리는 `Clear<T>()`·`ClearAll()`로 충분. IoC가 필요한 시점은 Tier 3 Network·멀티 컨텍스트 도입 시 재검토.
### 6-2. Event: 약한 참조 vs 강한 참조
- **채택**: 강한 참조(`Action<TEvent>` 직접 보관). 명시적 Unsubscribe 의무.
- **기각**: `WeakReference` 기반 자동 해제
- **기각 이유**: 약한 참조는 핸들러가 람다 캡처일 때 수명 예측이 어렵고 구독자가 이른 GC로 사라지는 버그가 보고됨(업계 관례). 명시 해제가 단순·예측가능. 구독자 누수는 정적 분석 도구로 잡는 편이 낫다.
### 6-3. Container: 변경 이벤트 세분화 vs 단일 `Changed` 이벤트
- **채택**: `Added`·`Removed`·`Reset` (Dictionary는 `Updated` 추가) 세분화
- **기각**: 단일 `Changed(ChangeArgs e)`
- **기각 이유**: 세분화하면 구독자가 관심있는 이벤트만 구독해 불필요 dispatch를 줄일 수 있고, 람다 시그니처가 명확해 가독성이 높다. 박싱되는 `ChangeArgs` 할당도 회피.
### 6-4. Data: 제네릭 `TKey` vs `int` 전용
- **채택**: `DataTable<TKey, TRow>` 제네릭
- **기각**: `DataTable<TRow>` (`int` 키 고정)
- **기각 이유**: 기존 `MasterTableBase`가 int 키 고정이어서 확장성 부족했음. 문자열 키·enum 키 요구가 기획 쪽에서 자주 발생하므로 제네릭화. 비용은 딕셔너리 1개 분.
### 6-5. Data: CSV 파서 자체 구현 vs 외부 라이브러리
- **채택**: 최소 자체 구현 (쉼표·따옴표·줄바꿈). 필요 시 `CustomParser` 훅.
- **기각**: `CsvHelper` NuGet 패키지 도입
- **기각 이유**: Tier 1은 외부 의존성 최소 원칙(§01). 마스터 테이블 CSV는 포맷이 기획팀 통제하에 있어 최소 파서로 충분. 복잡한 케이스가 발생하면 Editor 측 컨버터가 JSON으로 변환하여 런타임에 전달하는 경로가 더 깨끗하다.
### 6-6. Data: JSON 파싱 — `JsonUtility` vs `Newtonsoft.Json`
- **채택**: Unity `JsonUtility` 기본, 한계 명시(Dictionary·polymorphism 미지원 등)
- **기각**: `Newtonsoft.Json` 기본 채택
- **기각 이유**: 기본 Unity 번들 의존성 최소. Newtonsoft는 선택 패키지이며 모든 PC에 설치 보장이 어려움. 고급 케이스는 사용자 측 커스텀 파싱으로 우회 가능. 추후 필요 시 `FromJsonNewtonsoft` 보조 진입점을 옵션 모듈로 추가.
### 6-7. 3종의 공통 Assembly vs 모듈별 asmdef
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
- **채택**: 단일 `BT.Framework.asmdef` 유지 (현재 구조)
- **기각**: `Core.Event.asmdef` / `Core.Container.asmdef` / `Core.Data.asmdef` 분리
- **기각 이유**: 분리는 순환 차단에 이점이 있으나 Tier 1 총 파일 수가 아직 작고, Unity 패키지 소비자가 단일 참조로 쓰기 편하다. 본 설계의 DAG가 명확하므로 분리 필요성이 낮음. Tier 2 모듈이 확장되어 부분 사용 요구가 커지면 재검토.
---
## 7. 변경 이력
| 일시 | 변경 | 주체 |
|------|------|------|
| 2026-04-17 | v1 초안 작성 (PD님 #36 즉시 수행 지시) | 개발팀장 |