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>
This commit is contained in:
깃 관리자 2026-04-21 02:05:55 +09:00
parent 616e3d3e10
commit 8ff5a1f156
15 changed files with 24 additions and 291 deletions

View File

@ -1,267 +0,0 @@
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const {
Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
HeadingLevel, AlignmentType, BorderStyle, WidthType, ShadingType,
LevelFormat, TableOfContents, PageBreak, TabStopType, TabStopPosition
} = require('docx');
const srcPath = process.argv[2];
const dstPath = process.argv[3];
if (!srcPath || !dstPath) { console.error('usage: md_to_docx.js <src.md> <dst.docx>'); process.exit(1); }
const FONT = 'Malgun Gothic';
const raw = fs.readFileSync(srcPath, 'utf8');
// Strip YAML frontmatter
let md = raw;
if (md.startsWith('---')) {
const end = md.indexOf('\n---', 3);
if (end > 0) md = md.slice(end + 4).replace(/^\s*\n/, '');
}
const lines = md.split(/\r?\n/);
const border = { style: BorderStyle.SINGLE, size: 6, color: 'CCCCCC' };
const cellBorders = { top: border, bottom: border, left: border, right: border };
function run(text, opts = {}) {
return new TextRun({ text, font: FONT, size: opts.size || 22, bold: !!opts.bold, italics: !!opts.italic });
}
// Parse inline **bold**, *italic*, `code` -> TextRun[]
function parseInline(text, baseOpts = {}) {
const runs = [];
const re = /(\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`)/g;
let last = 0; let m;
while ((m = re.exec(text)) !== null) {
if (m.index > last) runs.push(run(text.slice(last, m.index), baseOpts));
const tok = m[0];
if (tok.startsWith('**')) runs.push(run(tok.slice(2, -2), { ...baseOpts, bold: true }));
else if (tok.startsWith('`')) runs.push(new TextRun({ text: tok.slice(1, -1), font: 'Consolas', size: baseOpts.size || 22 }));
else runs.push(run(tok.slice(1, -1), { ...baseOpts, italic: true }));
last = m.index + tok.length;
}
if (last < text.length) runs.push(run(text.slice(last), baseOpts));
if (runs.length === 0) runs.push(run(text, baseOpts));
return runs;
}
function para(text, opts = {}) {
return new Paragraph({
children: parseInline(text, opts),
spacing: { before: 60, after: 60 },
...(opts.heading ? { heading: opts.heading } : {}),
});
}
function heading(text, level) {
const map = { 1: HeadingLevel.HEADING_1, 2: HeadingLevel.HEADING_2, 3: HeadingLevel.HEADING_3, 4: HeadingLevel.HEADING_4 };
const size = { 1: 36, 2: 30, 3: 26, 4: 24 }[level] || 22;
return new Paragraph({
heading: map[level] || HeadingLevel.HEADING_4,
children: [new TextRun({ text, font: FONT, size, bold: true })],
spacing: { before: 240, after: 120 },
});
}
function bullet(text, level = 0) {
return new Paragraph({
numbering: { reference: 'bullets', level },
children: parseInline(text),
spacing: { before: 40, after: 40 },
});
}
function numbered(text, level = 0) {
return new Paragraph({
numbering: { reference: 'numbers', level },
children: parseInline(text),
spacing: { before: 40, after: 40 },
});
}
function codeBlock(text) {
return new Paragraph({
children: [new TextRun({ text, font: 'Consolas', size: 20 })],
shading: { type: ShadingType.CLEAR, fill: 'F4F4F4' },
spacing: { before: 60, after: 60 },
});
}
function quote(text) {
return new Paragraph({
children: parseInline(text, { italic: true }),
indent: { left: 360 },
spacing: { before: 60, after: 60 },
border: { left: { style: BorderStyle.SINGLE, size: 18, color: '2E75B6', space: 12 } },
});
}
// Parse pipe table starting at index i, returns { table, nextIndex }
function parseTable(startIdx) {
const rows = [];
let i = startIdx;
while (i < lines.length && /^\s*\|.*\|\s*$/.test(lines[i])) {
rows.push(lines[i].trim());
i++;
}
if (rows.length < 2) return null;
// Header | separator | body
const split = (r) => r.slice(1, -1).split('|').map(c => c.trim());
const header = split(rows[0]);
const body = rows.slice(2).map(split);
const colCount = header.length;
const totalWidth = 9000;
const colWidth = Math.floor(totalWidth / colCount);
const columnWidths = new Array(colCount).fill(colWidth);
const makeCell = (txt, isHeader) => new TableCell({
borders: cellBorders,
width: { size: colWidth, type: WidthType.DXA },
shading: isHeader ? { type: ShadingType.CLEAR, fill: 'D5E8F0' } : undefined,
margins: { top: 80, bottom: 80, left: 120, right: 120 },
children: [new Paragraph({ children: parseInline(txt, { bold: isHeader, size: 20 }) })],
});
const tableRows = [
new TableRow({ children: header.map(h => makeCell(h, true)) }),
...body.map(r => new TableRow({ children: r.concat(new Array(Math.max(0, colCount - r.length)).fill('')).slice(0, colCount).map(c => makeCell(c, false)) }))
];
return {
table: new Table({ width: { size: totalWidth, type: WidthType.DXA }, columnWidths, rows: tableRows }),
nextIndex: i,
};
}
const children = [];
// Cover
children.push(new Paragraph({
children: [new TextRun({ text: '인간 서버 개발자 업무 지시서', font: FONT, size: 44, bold: true })],
alignment: AlignmentType.CENTER,
spacing: { before: 400, after: 200 },
}));
children.push(new Paragraph({
children: [new TextRun({ text: '수상한잡화점 서버 파트 — v1.0', font: FONT, size: 28 })],
alignment: AlignmentType.CENTER,
spacing: { after: 120 },
}));
children.push(new Paragraph({
children: [new TextRun({ text: '발행: 개발팀장 · 수신: 인간 서버 개발자 · 일자: 2026-04-17', font: FONT, size: 22, italics: true })],
alignment: AlignmentType.CENTER,
spacing: { after: 600 },
}));
children.push(new Paragraph({ children: [new PageBreak()] }));
// Manual index (no TOC field, avoids Word's external-reference warning)
children.push(new Paragraph({
children: [new TextRun({ text: '목차', font: FONT, size: 32, bold: true })],
spacing: { before: 120, after: 240 },
}));
for (const L of lines) {
const hm = /^(#{1,3})\s+(.*)$/.exec(L);
if (!hm) continue;
const lv = hm[1].length;
const indent = (lv - 1) * 360;
children.push(new Paragraph({
children: [new TextRun({ text: hm[2], font: FONT, size: 22 })],
indent: { left: indent },
spacing: { before: 20, after: 20 },
}));
}
children.push(new Paragraph({ children: [new PageBreak()] }));
let i = 0;
while (i < lines.length) {
const line = lines[i];
// Table
if (/^\s*\|.*\|\s*$/.test(line) && i + 1 < lines.length && /^\s*\|[\s:\-|]+\|\s*$/.test(lines[i + 1])) {
const result = parseTable(i);
if (result) { children.push(result.table); children.push(new Paragraph({ children: [new TextRun('')] })); i = result.nextIndex; continue; }
}
// Heading
const hMatch = /^(#{1,6})\s+(.*)$/.exec(line);
if (hMatch) { children.push(heading(hMatch[2], Math.min(hMatch[1].length, 4))); i++; continue; }
// Code block
if (/^```/.test(line)) {
const buf = [];
i++;
while (i < lines.length && !/^```/.test(lines[i])) { buf.push(lines[i]); i++; }
i++;
if (buf.length) children.push(codeBlock(buf.join('\n')));
continue;
}
// Quote
if (/^>\s?/.test(line)) {
children.push(quote(line.replace(/^>\s?/, '')));
i++; continue;
}
// Horizontal rule
if (/^---+\s*$/.test(line)) {
children.push(new Paragraph({ border: { bottom: { style: BorderStyle.SINGLE, size: 6, color: '999999', space: 1 } }, spacing: { before: 120, after: 120 } }));
i++; continue;
}
// Bullet
const bulletMatch = /^(\s*)[-*]\s+(.*)$/.exec(line);
if (bulletMatch) {
const level = Math.min(Math.floor(bulletMatch[1].length / 2), 3);
children.push(bullet(bulletMatch[2], level));
i++; continue;
}
// Numbered
const numMatch = /^(\s*)\d+\.\s+(.*)$/.exec(line);
if (numMatch) {
const level = Math.min(Math.floor(numMatch[1].length / 2), 3);
children.push(numbered(numMatch[2], level));
i++; continue;
}
// Empty
if (/^\s*$/.test(line)) { i++; continue; }
// Paragraph
children.push(para(line));
i++;
}
const doc = new Document({
creator: 'BurningTimes 개발팀',
title: '인간 서버 개발자 업무 지시서 — 수상한잡화점',
styles: {
default: { document: { run: { font: FONT, size: 22 } } },
paragraphStyles: [
{ id: 'Heading1', name: 'Heading 1', basedOn: 'Normal', next: 'Normal', quickFormat: true,
run: { size: 36, bold: true, font: FONT, color: '1F3864' },
paragraph: { spacing: { before: 360, after: 180 }, outlineLevel: 0 } },
{ id: 'Heading2', name: 'Heading 2', basedOn: 'Normal', next: 'Normal', quickFormat: true,
run: { size: 30, bold: true, font: FONT, color: '2E75B6' },
paragraph: { spacing: { before: 280, after: 140 }, outlineLevel: 1 } },
{ id: 'Heading3', name: 'Heading 3', basedOn: 'Normal', next: 'Normal', quickFormat: true,
run: { size: 26, bold: true, font: FONT },
paragraph: { spacing: { before: 200, after: 100 }, outlineLevel: 2 } },
{ id: 'Heading4', name: 'Heading 4', basedOn: 'Normal', next: 'Normal', quickFormat: true,
run: { size: 24, bold: true, font: FONT },
paragraph: { spacing: { before: 160, after: 80 }, outlineLevel: 3 } },
],
},
numbering: {
config: [
{ reference: 'bullets', levels: [0, 1, 2, 3].map(lv => ({ level: lv, format: LevelFormat.BULLET, text: ['•','◦','▪','▫'][lv], alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 720 * (lv + 1), hanging: 360 } } } })) },
{ reference: 'numbers', levels: [0, 1, 2, 3].map(lv => ({ level: lv, format: LevelFormat.DECIMAL, text: `%${lv+1}.`, alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 720 * (lv + 1), hanging: 360 } } } })) },
],
},
sections: [{
properties: {
page: {
size: { width: 11906, height: 16838 }, // A4
margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 },
},
},
children,
}],
});
Packer.toBuffer(doc).then(buf => {
fs.mkdirSync(path.dirname(dstPath), { recursive: true });
fs.writeFileSync(dstPath, buf);
console.log('OK', dstPath, buf.length, 'bytes');
});

View File

@ -17,5 +17,5 @@
- 이전 NerdNavis 조직 기록(2026-04-16 ~ 2026-04-20 조직운영 대화로그)은 조직 기억 자산으로 보존 (당시 시점 이벤트 기록) - 이전 NerdNavis 조직 기록(2026-04-16 ~ 2026-04-20 조직운영 대화로그)은 조직 기억 자산으로 보존 (당시 시점 이벤트 기록)
## 참고 ## 참고
- 이전 NerdNavis 조직의 수상한잡화점 프로젝트 진행 과정 교훈은 `공유/조직자산/시행착오_아카이브/` 14종에 영구 보존 - 이전 조직의 이전 프로젝트 진행 과정 교훈은 `공유/조직자산/시행착오_아카이브/` 14종에 영구 보존
- BurningTimes 조직 착수 시점 기록은 `조직운영/2026-04-21.md`가 기준점 - BurningTimes 조직 착수 시점 기록은 `조직운영/2026-04-21.md`가 기준점

View File

@ -84,7 +84,7 @@ PD님 3회 직접 개입으로 완성된 최종형을 조직 표준 프로세스
### 예외 (원칙 1 유지 대상 — 파일 성격 배너) ### 예외 (원칙 1 유지 대상 — 파일 성격 배너)
**파일 자체의 성격**을 표시하는 배너만 상단 유지 (방향 전환 배너와 구별): **파일 자체의 성격**을 표시하는 배너만 상단 유지 (방향 전환 배너와 구별):
- `🔴 아카이브됨 — 대체: X` — 완전 폐기된 설계 문서 원본 (예: `07_*.md`) - `🔴 아카이브됨 — 대체: X` — 완전 폐기된 설계 문서 원본 (예: `07_*.md`)
- `🟢 완료 실적 아카이브` — 완료 실적 성격 전환 문서 (예: `02_수상한잡화점_추출대상_v1.md`) - `🟢 완료 실적 아카이브` — 완료 실적 성격 전환 문서 (예: `02_이전 프로젝트_추출대상_v1.md`)
### 실증 결과 (2026-04-18) ### 실증 결과 (2026-04-18)
- 5문서 최신화 (M1·M2·m1·m2·m3) 전수 구용어 0건 달성 - 5문서 최신화 (M1·M2·m1·m2·m3) 전수 구용어 0건 달성

View File

@ -1,7 +1,7 @@
# 시행착오 아카이브 — BurningTimes 조직 자산 # 시행착오 아카이브 — BurningTimes 조직 자산
## 목적 ## 목적
BurningTimes 신설 조직이 이전 NerdNavis 조직의 **수상한잡화점** 프로젝트 진행 과정에서 축적한 시행착오·성공·실패 패턴을 계승하여, 동일한 실수를 반복하지 않고 검증된 방법은 재활용하기 위한 **조직 자산 아카이브**. BurningTimes 신설 조직이 **이전 게임 개발 프로젝트** 진행 과정에서 축적한 시행착오·성공·실패 패턴을 계승하여, 동일한 실수를 반복하지 않고 검증된 방법은 재활용하기 위한 **조직 자산 아카이브**.
## 배경 ## 배경
- 조직 전환: NerdNavis (2025-2026) → BurningTimes (2026-04-21 출범) - 조직 전환: NerdNavis (2025-2026) → BurningTimes (2026-04-21 출범)

View File

@ -1,14 +1,14 @@
--- ---
type: 시행착오 아카이브 type: 시행착오 아카이브
scope: plan-auditor (기획팀 감사관) scope: plan-auditor (기획팀 감사관)
source_project: 수상한잡화점 (2026-04-17 ~ 2026-04-20) source_project: 이전 프로젝트 (2026-04-17 ~ 2026-04-20)
target_org: BurningTimes (2026-04-21~) target_org: BurningTimes (2026-04-21~)
target_project: EerieVillage (기묘한 고을: 조선퇴마뎐) target_project: EerieVillage (기묘한 고을: 조선퇴마뎐)
maintainer: 총괄PM maintainer: 총괄PM
created: 2026-04-21 created: 2026-04-21
version: v1 version: v1
related_rules: C5·C6·C7(구)·C13·C14·C19·C23·C29·C31·C34-11·C36 · P16·P17·P19·P23·P24(→C32)·P28 · P30 related_rules: C5·C6·C7(구)·C13·C14·C19·C23·C29·C31·C34-11·C36 · P16·P17·P19·P23·P24(→C32)·P28 · P30
rationale: 너드나비스 → 버닝타임즈 전환 시, 수상한잡화점 plan-auditor가 축적한 기획 감사 노하우·실증 패턴을 조직 자산으로 이관하여 EerieVillage 기획 착수 시 동일 실수 반복 방지 rationale: BurningTimes 신설 조직 전환 시, 이전 프로젝트 plan-auditor가 축적한 기획 감사 노하우·실증 패턴을 조직 자산으로 이관하여 EerieVillage 기획 착수 시 동일 실수 반복 방지
--- ---
# plan-auditor 시행착오 아카이브 (v1) # plan-auditor 시행착오 아카이브 (v1)

View File

@ -4,7 +4,7 @@ BurningTimes 자체 범용 Unity 프레임워크.
## 개요 ## 개요
기존 외부 의존 코어(`BurningTimesCore`)가 이전·퇴사로 사용 불가해짐에 따라, BurningTimes가 자체적으로 보유·유지하는 범용 코어를 새로 구축한다. 수상한 잡화점 등 사내 프로젝트에서 반복되는 패턴을 Tier 단위로 흡수하여 차기 프로젝트부터 바로 활용 가능한 형태로 제공한다. 기존 외부 의존 코어(`BurningTimesCore`)가 이전·퇴사로 사용 불가해짐에 따라, BurningTimes가 자체적으로 보유·유지하는 범용 코어를 새로 구축한다. 이전 프로젝트 등 사내 프로젝트에서 반복되는 패턴을 Tier 단위로 흡수하여 차기 프로젝트부터 바로 활용 가능한 형태로 제공한다.
## 설치 (Unity Package Manager) ## 설치 (Unity Package Manager)
@ -45,7 +45,7 @@ Documentation~/ # Unity 임포트 제외 (~ 접두)
- **제네릭 우선**: 하드코딩 메서드는 제네릭 팩토리로 재설계 - **제네릭 우선**: 하드코딩 메서드는 제네릭 팩토리로 재설계
- **싱글톤 최소화**: 필요 최소 외 DI/이벤트 기반으로 전환 - **싱글톤 최소화**: 필요 최소 외 DI/이벤트 기반으로 전환
자세한 내용은 `개발실/코어_설계/01_아키텍처_개요_v1.md`, `02_수상한잡화점_추출대상_v1.md` 참조. 자세한 내용은 `개발실/코어_설계/01_아키텍처_개요_v1.md`, `02_이전 프로젝트_추출대상_v1.md` 참조.
## 라이선스 ## 라이선스

View File

@ -14,7 +14,7 @@ namespace BurningTimes.Core.Coroutine
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para>기존 BurningTimesCore의 <c>CoroutineHandler</c>와 <c>CoroutineRunner</c> 2종을 하나로 통합했다. /// <para>기존 BurningTimesCore의 <c>CoroutineHandler</c>와 <c>CoroutineRunner</c> 2종을 하나로 통합했다.
/// 수상한 잡화점의 <c>MyCoroutine</c>도 이 러너로 흡수된다.</para> /// 이전 프로젝트의 <c>MyCoroutine</c>도 이 러너로 흡수된다.</para>
/// <para>호스트는 <see cref="CoroutineHost"/> 내부 <see cref="MonoBehaviour"/>로, /// <para>호스트는 <see cref="CoroutineHost"/> 내부 <see cref="MonoBehaviour"/>로,
/// <c>DontDestroyOnLoad</c> 처리된다. 첫 호출 시점에 지연 생성되며, 씬 전환과 무관하게 유지된다.</para> /// <c>DontDestroyOnLoad</c> 처리된다. 첫 호출 시점에 지연 생성되며, 씬 전환과 무관하게 유지된다.</para>
/// <para>주요 기능:</para> /// <para>주요 기능:</para>

View File

@ -10,7 +10,7 @@ namespace BurningTimes.Core.Util
/// Dictionary·PlayerPrefs·SaveData 등에서 사용할 합성 키를 일관된 규칙으로 생성하는 유틸리티. /// Dictionary·PlayerPrefs·SaveData 등에서 사용할 합성 키를 일관된 규칙으로 생성하는 유틸리티.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para>설계 문서 §5 Tier 1. 수상한 잡화점에서 `$"{category}_{id}"` 와 `$"{category}:{id}"` /// <para>설계 문서 §5 Tier 1. 이전 프로젝트에서 `$"{category}_{id}"` 와 `$"{category}:{id}"`
/// 가 혼재되어 키 충돌·조회 실패가 발생한 경험을 반영, 프레임워크 전체에서 구분자 <c>':'</c>로 고정한다.</para> /// 가 혼재되어 키 충돌·조회 실패가 발생한 경험을 반영, 프레임워크 전체에서 구분자 <c>':'</c>로 고정한다.</para>
/// <para>구분자 <c>':'</c>를 선택한 이유:</para> /// <para>구분자 <c>':'</c>를 선택한 이유:</para>
/// <list type="bullet"> /// <list type="bullet">

View File

@ -3,7 +3,7 @@
> **작성일**: 2026-04-14 > **작성일**: 2026-04-14
> **작성자**: 개발실장 > **작성자**: 개발실장
> **상태**: PD님 결정(네임스페이스 `BurningTimes.*`, MVP=Tier1+2) 반영 v1.2 > **상태**: PD님 결정(네임스페이스 `BurningTimes.*`, MVP=Tier1+2) 반영 v1.2
> **적용 대상**: 수상한 잡화점 **이후** 프로젝트 (현 프로젝트에는 미적용) > **적용 대상**: 이전 프로젝트 **이후** 프로젝트 (현 프로젝트에는 미적용)
--- ---
@ -14,10 +14,10 @@
| **정식 명칭** | `BT.Framework` | PD님 확정 ✅ (2026-04-14) | | **정식 명칭** | `BT.Framework` | PD님 확정 ✅ (2026-04-14) |
| **UPM 패키지 이름** | `com.nerdnavis.framework` | PD님 확정 ✅ (2026-04-14) | | **UPM 패키지 이름** | `com.nerdnavis.framework` | PD님 확정 ✅ (2026-04-14) |
| **루트 네임스페이스** | `BurningTimes` | PD님 확정 ✅ | | **루트 네임스페이스** | `BurningTimes` | PD님 확정 ✅ |
| **Unity 호환** | 6000.0.x (Unity 6 LTS) 이상 | 수상한 잡화점 환경 기준 | | **Unity 호환** | 6000.0.x (Unity 6 LTS) 이상 | 이전 프로젝트 환경 기준 |
| **API 호환성** | .NET Standard 2.1 | | | **API 호환성** | .NET Standard 2.1 | |
| **저장소** | PD님 NAS Git (세부 논의 대기) | ①②④ 완료 후 확정 예정 | | **저장소** | PD님 NAS Git (세부 논의 대기) | ①②④ 완료 후 확정 예정 |
| **첫 적용 프로젝트** | 수상한 잡화점**다음** 프로젝트 | | | **첫 적용 프로젝트** | 이전 프로젝트**다음** 프로젝트 | |
## 2. 네임스페이스 체계 ## 2. 네임스페이스 체계
@ -35,7 +35,7 @@ BurningTimes 루트 (공용 인터페이스, 공용 enum
│ └── Optimization EnumString/ScopedValue 등 │ └── Optimization EnumString/ScopedValue 등
├── BurningTimes.UI UI 프레임워크 ├── BurningTimes.UI UI 프레임워크
│ ├── UGUI UGUI 기반 (수상한 잡화점 경험 반영) │ ├── UGUI UGUI 기반 (이전 프로젝트 경험 반영)
│ ├── UIToolkit UI Toolkit 기반 (기존 코어 계승) │ ├── UIToolkit UI Toolkit 기반 (기존 코어 계승)
│ ├── Components SafeArea/FitLabel/Layout │ ├── Components SafeArea/FitLabel/Layout
│ └── Extensions │ └── Extensions
@ -236,7 +236,7 @@ var save = ServiceLocator.Resolve<ISaveProvider>();
| CSV 로더 | `BurningTimes.Core.Data` | ✅ 거의 그대로 | | | CSV 로더 | `BurningTimes.Core.Data` | ✅ 거의 그대로 | |
| Toolkit | `BurningTimes.Core.Util` | 🟡 부분 클래스 해체 (4-7) | | | Toolkit | `BurningTimes.Core.Util` | 🟡 부분 클래스 해체 (4-7) | |
| 속성(Attribute) | `BurningTimes.Core.Attribute` | ✅ 거의 그대로 | ReadOnly/ShowIf | | 속성(Attribute) | `BurningTimes.Core.Attribute` | ✅ 거의 그대로 | ReadOnly/ShowIf |
| UI 프레임워크 (UGUI) **주력** | `BurningTimes.UI.UGUI` | 🔴 신규 (수상한 잡화점 경험 반영) | `UIView` UGUI 버전. PD님 지시: 주력 ✅ | | UI 프레임워크 (UGUI) **주력** | `BurningTimes.UI.UGUI` | 🔴 신규 (이전 프로젝트 경험 반영) | `UIView` UGUI 버전. PD님 지시: 주력 ✅ |
| UI 프레임워크 (UIToolkit) **보조** | `BurningTimes.UI.UIToolkit` | ✅ 기존 `UIView` 계승 | 최소 수준 유지, 확장 우선순위 낮음 | | UI 프레임워크 (UIToolkit) **보조** | `BurningTimes.UI.UIToolkit` | ✅ 기존 `UIView` 계승 | 최소 수준 유지, 확장 우선순위 낮음 |
| SafeArea | `BurningTimes.UI.Components` | ✅ 계승 | | | SafeArea | `BurningTimes.UI.Components` | ✅ 계승 | |
| 에디터 도구 | `BurningTimes.Editor.*` | ✅ 계승 (네이밍만 변경) | | | 에디터 도구 | `BurningTimes.Editor.*` | ✅ 계승 (네이밍만 변경) | |
@ -272,7 +272,7 @@ var save = ServiceLocator.Resolve<ISaveProvider>();
- 참조 카운팅 기반 `AddressableHandle<T>` - 참조 카운팅 기반 `AddressableHandle<T>`
- Preload/Unload 정책(`Scene` / `Manual` / `TTL`) - Preload/Unload 정책(`Scene` / `Manual` / `TTL`)
- 로드 실패 폴백 훅 - 로드 실패 폴백 훅
- 수상한 잡화점 `AddrHandleBase` 를 범용화한 형태 - 이전 프로젝트 `AddrHandleBase` 를 범용화한 형태
## 7. Tier 3 목록 (서버팀 셋업 시점 합류) ## 7. Tier 3 목록 (서버팀 셋업 시점 합류)
@ -330,9 +330,9 @@ com.nerdnavis.framework/
└── BT.Framework.EditorTests.asmdef └── BT.Framework.EditorTests.asmdef
``` ```
## 9. 수상한 잡화점 코드 활용 (범용 패턴 추출 대상) ## 9. 이전 프로젝트 코드 활용 (범용 패턴 추출 대상)
수상한 잡화점 Unity 프로젝트 (`D:/BurningTimes/FilGoodBandits/DeckBuilding/Assets/Script/`) 에서 **범용화 가치가 있는 패턴**. 상세는 별도 문서 `02_수상한잡화점_추출대상_v1.md` 로 분리 예정. 이전 프로젝트 Unity 프로젝트 (`D:/BurningTimes/FilGoodBandits/DeckBuilding/Assets/Script/`) 에서 **범용화 가치가 있는 패턴**. 상세는 별도 문서 `02_이전 프로젝트_추출대상_v1.md` 로 분리 예정.
**1차 후보** **1차 후보**
- `My/MyCoroutine.cs` → 코루틴 베이스 → `CoroutineRunner` 참고 자료 - `My/MyCoroutine.cs` → 코루틴 베이스 → `CoroutineRunner` 참고 자료
@ -352,12 +352,12 @@ com.nerdnavis.framework/
| # | 작업 | 산출물 | | # | 작업 | 산출물 |
|---|------|--------| |---|------|--------|
| 1 | 본 설계안 PD님 검토 → 승인 | v1.1 고정 | | 1 | 본 설계안 PD님 검토 → 승인 | v1.1 고정 |
| 2 | 수상한 잡화점 추출 대상 상세 문서화 | `02_수상한잡화점_추출대상_v1.md` | | 2 | 이전 프로젝트 추출 대상 상세 문서화 | `02_이전 프로젝트_추출대상_v1.md` |
| 3 | PD님과 NAS Git 저장소 위치·접근 방식 협의 | 저장소 URL 확정 | | 3 | PD님과 NAS Git 저장소 위치·접근 방식 협의 | 저장소 URL 확정 |
| 4 | 패키지 스켈레톤 생성 (빈 폴더 + package.json + asmdef) | 공란 패키지 | | 4 | 패키지 스켈레톤 생성 (빈 폴더 + package.json + asmdef) | 공란 패키지 |
| 5 | Tier 1 모듈 순차 구현 | 각 모듈별 PR/커밋 | | 5 | Tier 1 모듈 순차 구현 | 각 모듈별 PR/커밋 |
| 6 | Tier 2 모듈 순차 구현 | | | 6 | Tier 2 모듈 순차 구현 | |
| 7 | 수상한 잡화점 개발 준비(Phase 0-B/C) 재개 | | | 7 | 이전 프로젝트 개발 준비(Phase 0-B/C) 재개 | |
| 8 | 서버팀 셋업 시점 Tier 3 합류 + 보안 Critical 3건 재기동 | `project_shop_security_pending.md` 참조 | | 8 | 서버팀 셋업 시점 Tier 3 합류 + 보안 Critical 3건 재기동 | `project_shop_security_pending.md` 참조 |
## 11. PD님 확정 사항 (2026-04-14) ## 11. PD님 확정 사항 (2026-04-14)
@ -378,6 +378,6 @@ com.nerdnavis.framework/
## 부록 A. 변경 이력 ## 부록 A. 변경 이력
- **v1 (2026-04-14)**: 초안. 기존 BurningTimesCore 85개 cs 파일 조사 + 수상한 잡화점 범용 패턴 1차 식별 기반. - **v1 (2026-04-14)**: 초안. 기존 BurningTimesCore 85개 cs 파일 조사 + 이전 프로젝트 범용 패턴 1차 식별 기반.
- **v1.1 (2026-04-14)**: PD님 결정 4건 반영 — 정식 명칭·UPM 이름 확정, Goods는 Tier 2로 이관(`BurningTimes.Economy` 신설), UGUI 주력·UI Toolkit 보조 원칙. - **v1.1 (2026-04-14)**: PD님 결정 4건 반영 — 정식 명칭·UPM 이름 확정, Goods는 Tier 2로 이관(`BurningTimes.Economy` 신설), UGUI 주력·UI Toolkit 보조 원칙.
- **v1.2 (2026-04-14)**: PD님 Tier 1 착수 지시(Logger·ServiceLocator·CoroutineRunner) 반영 — 섹션 4-9 ServiceLocator 설계 신설, Tier 1 모듈 목록에 ServiceLocator 추가. 기존 MonoSingleton·EventBus 설계는 유지되며 3축 역할 분리 원칙 명시. - **v1.2 (2026-04-14)**: PD님 Tier 1 착수 지시(Logger·ServiceLocator·CoroutineRunner) 반영 — 섹션 4-9 ServiceLocator 설계 신설, Tier 1 모듈 목록에 ServiceLocator 추가. 기존 MonoSingleton·EventBus 설계는 유지되며 3축 역할 분리 원칙 명시.

View File

@ -14,7 +14,7 @@
| **목적** | `BT.Framework`(UPM 패키지 `com.nerdnavis.framework`)의 **물리적 배포·참조 방식**을 결정 가능한 형태로 안건화한다 | | **목적** | `BT.Framework`(UPM 패키지 `com.nerdnavis.framework`)의 **물리적 배포·참조 방식**을 결정 가능한 형태로 안건화한다 |
| **용도** | PD님 의사결정 1회로 OI-2 종결. 결정 후 Framework 레포 태그 정책 + Unity 프로젝트 참조 배선 + 신PC 셋업 절차가 일관되게 정의됨 | | **용도** | PD님 의사결정 1회로 OI-2 종결. 결정 후 Framework 레포 태그 정책 + Unity 프로젝트 참조 배선 + 신PC 셋업 절차가 일관되게 정의됨 |
| **범위** | 배포 방식(A/B/C + 하이브리드) 비교 · 권장안 · 선결 조건 · 후속 조치 · 열린 결정 항목 | | **범위** | 배포 방식(A/B/C + 하이브리드) 비교 · 권장안 · 선결 조건 · 후속 조치 · 열린 결정 항목 |
| **❌ 비목적** | ① **수상한 잡화점 투입 안건이 아님** — 수상한 잡화점은 본 프레임워크를 참조하지 않기로 기확정(PD님, 2026-04-15). 본 문서는 그 전제를 사용하지 않는다. ② 네임스페이스·릴리스 범위 등 타 OI 재결정이 아님. ③ Gitea 인프라 운영 정책 변경이 아님 | | **❌ 비목적** | ① **이전 프로젝트 투입 안건이 아님** — 이전 프로젝트은 본 프레임워크를 참조하지 않기로 기확정(PD님, 2026-04-15). 본 문서는 그 전제를 사용하지 않는다. ② 네임스페이스·릴리스 범위 등 타 OI 재결정이 아님. ③ Gitea 인프라 운영 정책 변경이 아님 |
--- ---
@ -30,7 +30,7 @@
> **목표 1 — 코어 프레임워크의 PC 독립 최신화 유지**: 어느 PC에서 작업하든 항상 최신화된 BurningTimes 조직의 자산으로 코어 코드 프레임워크를 유지·관리한다. 환경 이동·PC 추가·재기동 상황에서 프레임워크의 단일 최신 상태가 깨지지 않도록 구조를 설계·운영한다. > **목표 1 — 코어 프레임워크의 PC 독립 최신화 유지**: 어느 PC에서 작업하든 항상 최신화된 BurningTimes 조직의 자산으로 코어 코드 프레임워크를 유지·관리한다. 환경 이동·PC 추가·재기동 상황에서 프레임워크의 단일 최신 상태가 깨지지 않도록 구조를 설계·운영한다.
> >
> **목표 2 — 차기 프로젝트부터 조직 자산으로 적극 활용**: 현행 수상한 잡화점 프로젝트는 코어 프레임워크를 활용하지 않는다. 다음 프로젝트부터 조직 자산으로 적극 도입하여, 범용성 높은 BurningTimes만의 개발 노하우를 축적한다. 프레임워크는 "만들고 끝"이 아니라 "게임을 만들수록 쌓이는 자산"으로 운영한다. > **목표 2 — 차기 프로젝트부터 조직 자산으로 적극 활용**: 현행 이전 프로젝트는 코어 프레임워크를 활용하지 않는다. 다음 프로젝트부터 조직 자산으로 적극 도입하여, 범용성 높은 BurningTimes만의 개발 노하우를 축적한다. 프레임워크는 "만들고 끝"이 아니라 "게임을 만들수록 쌓이는 자산"으로 운영한다.
> >
> **목표 3 — 단기제작 가능한 유능한 게임 개발 스튜디오로의 발전**: 코어 프레임워크를 포함한 효율성 높은 개발 노하우를 지속 축적하여, 궁극적으로 어떤 게임이든 단기간 내 제작 가능한 유능한 스튜디오로 발전한다. > **목표 3 — 단기제작 가능한 유능한 게임 개발 스튜디오로의 발전**: 코어 프레임워크를 포함한 효율성 높은 개발 노하우를 지속 축적하여, 궁극적으로 어떤 게임이든 단기간 내 제작 가능한 유능한 스튜디오로 발전한다.
@ -138,4 +138,4 @@
## 8. 변경 이력 ## 8. 변경 이력
- **v1 (2026-04-15, 재작성)**: 헌법 제1원칙 3대 목표 기반 평가표로 재구성. 4축 섹션(목적·용도·범위·비목적) 상단 추가. OI-5 폐기 반영(수상한 잡화점 투입 전제 삭제). C18·C19 준수 명시. 권장안: C + H1 + 태그 추적 + S1 장래 보류. - **v1 (2026-04-15, 재작성)**: 헌법 제1원칙 3대 목표 기반 평가표로 재구성. 4축 섹션(목적·용도·범위·비목적) 상단 추가. OI-5 폐기 반영(이전 프로젝트 투입 전제 삭제). C18·C19 준수 명시. 권장안: C + H1 + 태그 추적 + S1 장래 보류.

View File

@ -2,7 +2,7 @@
> **작성자**: 개발팀장 (PD님 #36 즉시 수행 지시, 2026-04-17) > **작성자**: 개발팀장 (PD님 #36 즉시 수행 지시, 2026-04-17)
> **상태**: 초안 → 구현 병행 반영 > **상태**: 초안 → 구현 병행 반영
> **참조**: `01_아키텍처_개요_v1.md` §4-2~§4-3 (EventBus·ObservableList 개선안), `02_수상한잡화점_추출대상_v1.md` (Data 추출 대상) > **참조**: `01_아키텍처_개요_v1.md` §4-2~§4-3 (EventBus·ObservableList 개선안), `02_이전 프로젝트_추출대상_v1.md` (Data 추출 대상)
> **적용 범위**: `BT.Framework``BurningTimes.Core.Event`·`BurningTimes.Core.Container`·`BurningTimes.Core.Data` 3개 모듈의 공개 API 경계와 상호 의존 방향 > **적용 범위**: `BT.Framework``BurningTimes.Core.Event`·`BurningTimes.Core.Container`·`BurningTimes.Core.Data` 3개 모듈의 공개 API 경계와 상호 의존 방향
--- ---