D8 (P1) stat_key 정합성 검증 리포트
작성: 2026-04-21 game-designer 근거: scaffold sync audit 2026-04-21 D8 (P1) /designs/prestige_items.tsvv1.0 APPROVED /designs/prestige_effect_matrix.tsv/designs/prestige_system_spec.mdv1.0 APPROVED /designs/sprint-03-idle-research.mdv1.0 APPROVED / 메모리project_sprint02_w1_prestige_items.md,project_sprint02_w2_prestige_tech_sketch.md상태: v1.0 APPROVED (PD 승인 2026-04-21) 범위: 읽기 전용 교차 검증. TSV·수치 시트·코드 편집 0건. 본 문서 신규 생성 1건만.
PD 승인 결정표 (2026-04-21)
| 결정 | 내용 | 코멘트 |
|---|---|---|
N1 BuildSummary 네이밍 | B안 = 외부 표준명 + Importer 매핑 | Sprint 03 리서치 문서 전반에서 IdleRateMul/GoldMul/ExpMul/DropMul 그대로 사용. PrestigeItemImporter 가 TSV stat_key 컬럼을 외부 표준명으로 매핑(1:1 Dictionary). TSV는 game-designer 관점의 가독성(% 접미사) 유지. BuildSummary 필드명은 Calculator 가독성 우선. game-designer 권장안 채택 |
| N2 Sprint 03 문서 소급 수정 | 승인 — sprint-03-idle-research.md §2.1 / §5 축3, 축8 의 "#24 IdleCapHours" → TSV 실제 "#25 IdleCapHoursFlat (FlatStat, 별도 경로)" 로 정정 | 문서만 수정, 코드 0. IdleCapHoursFlat (item_id=25) 은 PrestigeCalculator.GetIdleCapHours(state) 헬퍼 통해 24 + 2 × min(level, 5) 수식으로 파생 (Lv5 상한 34h). 곱연산 BuildSummary와 분리 경로 |
Sprint 02 W2 착수 조건 해소: 두 결정 완료로 code-lead는 2026-05-18 Sprint 02 킥오프 시 PrestigeItemImporter + PrestigeCalculator.BuildSummary + GetIdleCapHours 헬퍼 즉시 착수 가능.
Importer 매핑 표 (N1-B 결과):
TSV stat_key | BuildSummary 필드 | effect_type | 경로 |
|---|---|---|---|
IdleYieldPct | IdleRateMul | Multiplier (%) | 곱연산 |
GoldGainPct | GoldMul | Multiplier (%) | 곱연산 |
ExpGainPct | ExpMul | Multiplier (%) | 곱연산 |
DropRatePct | DropMul | Multiplier (%) | 곱연산 |
IdleCapHoursFlat | 헬퍼 경유 GetIdleCapHours(state) | FlatStat (h) | 별도 |
| 나머지 25개 stat_key | 원본 그대로 | — | 곱/합연산 (TSV 컬럼) |
§0 TL;DR (PD 1분)
- 판정: YELLOW — 구조적 gap은 없으나 stat_key 이름 규약이 Sprint 03 기대 필드(
IdleRateMul/GoldMul/ExpMul/DropMul)와 불일치. Sprint 02 W2 Importer 착수 전 "정규화 규칙 1건" 확정 필요. - Sprint 02 W2 착수 가능 여부: 조건부 가능 — 아래 2개 결정만 PD 낙점되면 즉시 착수 가능:
- D1:
BuildSummary필드명을 TSV stat_key 그대로 따라갈지 (IdleYieldPct필드), vs Sprint 03 네이밍(IdleRateMul) 으로 매핑 테이블을 둘지 - D2:
item_id=25 IdleCapHoursFlat의 별도 경로 처리 규약 (덧셈 집계, 곱연산 BuildSummary와 분리) - 수치 gap 0건 — 30항목 전부 존재. Sprint 03이 기대하는 "방치 수익 증폭", "골드", "경험치", "드롭률", "방치 캡 시간" 다섯 축 모두 TSV에 실재 (item_id 20/22/23/24/25).
- 메모리 오기재 1건 발견 —
project_sprint02_w1_prestige_items.md가 "item_id=24 IdleCapHours (Utility)" 라 기록했으나 실제 TSV 기준 item_id=24는IdleYieldPct(방치 수익 증폭), item_id=25가IdleCapHoursFlat(방치 캡 시간). 튜닝 대기 3건 중 "item_id=24 방치수익 3.0%/Lv → 1.5%/Lv" 은IdleYieldPct지칭으로 올바름. 단IdleCapHours라는 이름은 실제로 item_id=25 를 가리켜야 정확.
§1 stat_key 마스터 테이블 (30행)
TSV 원본 기준. 컬럼 5개만 추출 (전체 15컬럼 중).
| item_id | tab | display_name_kr | stat_key | effect_type |
|---|---|---|---|---|
| 0 | Offense | 공격력 | AttackPowerPct | PercentStat |
| 1 | Offense | 치명타 확률 | CriticalRatePct | PercentStat |
| 2 | Offense | 치명타 피해 | CriticalPowerPct | PercentStat |
| 3 | Offense | 공격 속도 | AttackSpeedPct | PercentStat |
| 4 | Offense | 스킬 쿨다운 감소 | SkillCoolDownPct | PercentStat |
| 5 | Offense | 관통력 | PenetrationPct | PercentStat |
| 6 | Offense | 원소 피해 | ElementalDamagePct | PercentStat |
| 7 | Offense | 보스 피해 증폭 | BossDamagePct | PercentStat |
| 8 | Offense | 다굴 피해 증폭 | PackDamagePct | PercentStat |
| 9 | Offense | 장비 옵션 증폭 | EquipOptionAmp | Multiplier |
| 10 | Defense | 최대 체력 | MaxHpPct | PercentStat |
| 11 | Defense | 방어력 | DefensePowerPct | PercentStat |
| 12 | Defense | 회피율 | DodgeRatePct | PercentStat |
| 13 | Defense | 받는 피해 감소 | DamageReductionPct | PercentStat |
| 14 | Defense | 체력 재생 | HpRegenPct | PercentStat |
| 15 | Defense | 흡혈 | LifeStealPct | PercentStat |
| 16 | Defense | 받는 치명 확률 감소 | CritDodgePct | PercentStat |
| 17 | Defense | 수호자 공격력 | GuardianAtkPct | PercentStat |
| 18 | Defense | 수호자 체력 | GuardianHpPct | PercentStat |
| 19 | Defense | 영웅 부활 횟수 | ReviveCountFlat | FlatStat |
| 20 | Utility | 드롭률 | DropRatePct | PercentStat |
| 21 | Utility | 매직파인드 | MagicFindPct | PercentStat |
| 22 | Utility | 골드 획득량 | GoldGainPct | PercentStat |
| 23 | Utility | 경험치 획득량 | ExpGainPct | PercentStat |
| 24 | Utility | 방치 수익 증폭 | IdleYieldPct | PercentStat |
| 25 | Utility | 방치 캡 시간 | IdleCapHoursFlat | FlatStat |
| 26 | Utility | 프레스티지 포인트 획득량 | PpGainPct | PercentStat |
| 27 | Utility | 마일스톤 보상 증폭 | MilestoneBonusPct | PercentStat |
| 28 | Utility | 층 입장권 할인 | StageTicketDiscPct | PercentStat |
| 29 | Utility | 수호석 포인트 확률 | GuardianShardRatePct | PercentStat |
§1.1 중복·오타 검사
- 중복 stat_key: 0건 — 30개 모두 고유.
- 오타: 0건 — PascalCase +
Pct/Flat/Amp접미사 규약 일관. - effect_type 분포:
PercentStat× 28 /FlatStat× 2 (item_id=19ReviveCountFlat, item_id=25IdleCapHoursFlat) /Multiplier× 1 (item_id=9EquipOptionAmp). - 특이점:
Multiplier타입 1건(item_id=9) 은PercentStat과 수식이 동일(곱연산 멀티플라이어)하나 타입만 분리. Sprint 02 W2 Importer 에서 동일 경로로 집계하되 타입 태그를 보존하여 Sprint 04 어픽스 연동 시 구분 가능하도록 유지 권장.
§2 Sprint 03 요구 필드 매핑
Sprint 03 sprint-03-idle-research.md §2.3(옵션 B 집계 공식), §5 축8, §9 CP-I4 가 기대하는 PrestigeCalculator.BuildSummary 필드를 TSV stat_key 와 교차.
§2.1 1:1 매핑 테이블
Sprint 03 기대 필드 (BuildSummary.*) | 용도 (수식에서) | 대응 TSV item_id | TSV stat_key | effect_type | 매핑 상태 |
|---|---|---|---|---|---|
IdleRateMul | ppGained = elapsedSec/3600 × maxFloor × 0.05 × IdleRateMul (§3.2 C안) | 24 | IdleYieldPct | PercentStat | YELLOW — 이름 불일치 |
GoldMul | goldTotal = kills × avgGold × GoldMul (§2.3) | 22 | GoldGainPct | PercentStat | YELLOW — 이름 불일치 |
ExpMul | expTotal = kills × avgExp × ExpMul (§2.3) | 23 | ExpGainPct | PercentStat | YELLOW — 이름 불일치 |
DropMul (추정, 리서치 문서에서 함축 — dropTable.RollDrops 확률 곱) | 드롭 수량 보정 | 20 | DropRatePct | PercentStat | YELLOW — 이름 불일치 |
IdleCapHours (스칼라, 곱연산 아님) | cappedSec = min(elapsed, IdleCapHours × 3600) (§2.1, §2.4) | 25 | IdleCapHoursFlat | FlatStat | GREEN — 존재 + 타입 정합 (§3 별도 경로) |
§2.2 Sprint 03 §5 축8 재인용
축4:PrestigeCalculator.BuildSummary— Sprint 02 W2 시그니처 고정.IdleRateMul필드 존재 여부 확인 필요 → Sprint 02 W2 Stat 확정 후 교차 확인 축8:IdleCapHours프레스티지 아이템 (#24) —IdleRewardCalculator가min(elapsed, cap)적용.cap은PrestigeCalculator.CalculateLevel+ 사전 정의 공식에서 파생
발견: Sprint 03 문서가 "프레스티지 아이템 #24" 를 IdleCapHours 로 지칭하나, 실제 TSV 기준 #24는 IdleYieldPct (방치 수익 증폭), #25가 IdleCapHoursFlat (방치 캡 시간). 두 항목이 Utility 탭에서 연달아 배치되어 혼동 가능. Sprint 02 W2 Importer 작성자 + Sprint 03 IdleTicker 작성자는 item_id 가 아닌 stat_key 로 참조 해야 혼동 방지.
§2.3 D1 제안 — BuildSummary 필드명 규약 2안 비교
D1-A안: TSV stat_key 그대로 (IdleYieldPct, GoldGainPct, ExpGainPct, DropRatePct)
- 장점: Importer 가 TSV 컬럼 → struct 필드 자동 매핑 가능. 중간 매핑 테이블 불필요. 디버깅 시 "TSV 어느 행?" 즉시 추적.
- 단점: Sprint 03 의사코드 (
prestigeBonus.IdleRateMul) 를 모두prestigeBonus.IdleYieldPct로 문서 수정 필요. 수식 가독성은...Mul접미사 쪽이 자연스러움. - 수정 대상 문서:
sprint-03-idle-research.md§2.3, §4.2, §9 CP-I4, §10 (약 5개 위치).
D1-B안: BuildSummary 별도 네이밍 + Importer 내부 매핑 (IdleRateMul ← IdleYieldPct 등)
- 장점: 수식 가독성 보존. Sprint 03 코드가
prestigeBonus.IdleRateMul * ...식으로 자연스럽게 읽힘. - 단점: 매핑 테이블 1개 추가 (4~6 라인). TSV 컬럼명 바뀔 때 양쪽 동기화 필요 (단, Sprint 02 W1 이 v1.0 APPROVED 이므로 변경 빈도 ≈ 0).
- 매핑 코드 예시:
// PrestigeCalculator.BuildSummary (Sprint 02 W2 신설 시)
public readonly struct BuildSummary {
public float AttackMul; // ← AttackPowerPct 합산 → 1+sum
public float IdleRateMul; // ← IdleYieldPct 합산 → 1+sum
public float GoldMul; // ← GoldGainPct
public float ExpMul; // ← ExpGainPct
public float DropMul; // ← DropRatePct
public int IdleCapHours; // ← IdleCapHoursFlat (덧셈, §3 별도 경로)
// ... 나머지 24개 필드
}
권장: D1-B안
- 1인 파트타임 부담 증분 = 매핑 테이블 1개 (5분 작성). 대신 Sprint 03 문서 5개 위치 수정 면제.
- 수식 가독성이 향후 Sprint 04 어픽스 연동 시 재사용도 ↑ (어픽스도
AttackMul같은 외부 표준명 쓸 가능성 높음). - 단, 이는 game-designer 의견이며 최종 네이밍은 code-lead 영역 — Sprint 02 W2 착수 시점에 code-lead가 D1-A/B 중 택1 후 PD 승인 받기.
§3 IdleCapHoursFlat 특수 경로 스펙
§3.1 왜 특수 경로인가
IdleCapHoursFlat (item_id=25) 은 30개 중 유일하게 "시간(h) 절대값" 을 나타내는 FlatStat 타입. 나머지 PercentStat 29개는 전부 1 + Σ(level × per_level × diminish) 식 곱연산 멀티플라이어로 집계되지만, IdleCapHours 는 다음 이유로 별도 경로 필수:
- 단위 불일치: PercentStat은 비율(dimensionless), FlatStat은 시간(hours). 같은 스칼라 필드에 곱연산으로 집계 불가.
- 수식 역할 다름: 곱연산 필드는 "보너스 배수" 로 피해·보상에 곱해지지만,
IdleCapHours는 "오프라인 계산 상한" 으로min()연산의 입력. - 소프트캡 동작 다름: PercentStat은
Lv > 5시 log diminishing, FlatStat 25번은FlatSteps곡선 — Lv 1~5 선형 (+2h/레벨), 이후 투자 불가(하드캡). Sprint 02 W1prestige_items_README.md메모리 합의에서 "CapBehavior=Block UI" 로 확정.
§3.2 집계 수식
TSV 값:
base_value = 24(기본 캡 24h)per_level_value = 2(레벨당 +2h)softcap_level = 5(하드캡)post_softcap_diminish = 0.00(Lv 6+ 투자 불가)
level = PrestigeCalculator.CalculateLevel(state.GetPpInvested(item_id=25), softCap=5)
level_clamped = min(level, 5) // FlatSteps 하드캡
idleCapHours = 24 + 2 × level_clamped
= Lv0: 24h / Lv1: 26h / Lv2: 28h / Lv3: 30h / Lv4: 32h / Lv5: 34h
§3.3 Sprint 02 W2 PrestigeCalculator 구현 요구사항
// 신규 헬퍼 (Sprint 03 리서치 §5 축8 에서 이미 예고됨 — "GetIdleCapHours(state) 신규")
public static int GetIdleCapHours(in PlayerPrestigeState state) {
int pp = state.GetPpInvested(stat_key: "IdleCapHoursFlat");
int level = CalculateLevel(pp, softCap: 5);
int capped = math.min(level, 5);
return 24 + 2 * capped; // base + per_level × level (Flat 가산)
}
- 불변 원칙:
PrestigeCalculator.CalculateLevel(int pp, byte softCap)시그니처는 메모리 합의대로 고정. 신규GetIdleCapHours는 헬퍼 메서드 추가 = 시그니처 변경 아님 = OK (Sprint 03 리서치 §5 축8 재확인과 일치). BuildSummary에IdleCapHours필드를 포함할지 vs 별도 헬퍼 호출할지는 code-lead 판단. 본 문서는 "별도 경로 처리"만 명시.
§3.4 Sprint 03 IdleRewardCalculator 호출 패턴
// FlushCatchUp() 내부 (Sprint 03 §4.5)
int capHours = PrestigeCalculator.GetIdleCapHours(prestigeState); // § 3.3 헬퍼
long cappedSec = math.min(elapsedSec, (long)capHours * 3600);
// 이후 Phase 1 Bulk + Phase 2 Tail 계산 (Sprint 03 §2.4 옵션 C)
§3.5 광고 48h 확장 (참고)
Sprint 04+ 광고 부스트 도입 시 capHours + adsBonusHours 형태로 가산. Sprint 02 W2 범위 아님. 현 스펙은 광고 적용 전 기본 캡만 처리.
§4 발견된 gap
§4.1 Gap G1 (YELLOW): 네이밍 규약 불일치 (§2 D1 제안으로 해소 가능)
- Sprint 03 의사코드 필드명(
IdleRateMul/GoldMul/ExpMul/DropMul) vs TSV stat_key 불일치. - 영향: Sprint 02 W2 Importer 착수 전
BuildSummary네이밍 규약 1건 확정만 필요. 수치 변경 0. - 해소 옵션: §2.3 D1-A안 (TSV 따라가기) vs D1-B안 (외부 표준명 + 매핑) 중 택1. 권장: D1-B안.
§4.2 Gap G2 (YELLOW → 해소): Sprint 03 리서치 문서 내 "item_id #24" 지칭 오류
- Sprint 03
sprint-03-idle-research.md§2.1, §5 축8 이 "프레스티지 아이템 #24IdleCapHours" 라 지칭. - 실제 TSV: #24 =
IdleYieldPct(방치 수익 증폭), #25 =IdleCapHoursFlat(방치 캡 시간). - 영향: Sprint 03 착수 시 코드 작성자가 item_id 로 직접 참조하면 잘못된 아이템 로드. 권장: item_id 대신 stat_key 문자열 참조.
- 해소: Sprint 03 리서치 문서 §2.1 (1회), §5 축8 (1회) 의 "#24" → "stat_key=
IdleCapHoursFlat" 로 문서만 소급 수정. 본 검증 보고서가 수정 요청 근거로 작동.
§4.3 Gap G3 (정보): 메모리 project_sprint02_w1_prestige_items.md 오기재
- "item_id=24 IdleCapHours (Utility)" → 실제는 item_id=25 가
IdleCapHoursFlat. - "item_id=24 방치수익 3.0%/Lv → 1.5%/Lv" 은
IdleYieldPct지칭으로 올바름 (튜닝 대기 3건 중 이 건은 영향 없음). - 영향: Sprint 03+ 방치 이코노미 튜닝 재진입 시 item_id 혼동 가능. 권장: 메모리 재기재 또는 해당 튜닝 대기 3건을 stat_key 기반으로 재명시:
AttackPowerPct(공격력): 2.0% → 1.5%/LvIdleYieldPct(방치수익): 3.0% → 1.5%/Lv + 하드캡PpGainPct(PP 획득): 1.5% → diminishing 강화
§4.4 Gap G4 없음 (GREEN)
- Sprint 03 요구 5개 필드 모두 TSV 실재 (
IdleYieldPct/GoldGainPct/ExpGainPct/DropRatePct/IdleCapHoursFlat). - 신규 stat_key 추가 불필요.
- 수치 조정 불필요 (튜닝 대기 3건은 Sprint 03 실측 후 별도 프로세스).
§5 Sprint 02 W2 code-lead 착수 전 확인 체크리스트
Sprint 02 W2 Importer / PrestigeCalculator / BuildSummary 작성 착수 전 다음 5개 항목을 PD 승인 → code-lead 시작.
- CK-1 (D1 결정):
BuildSummary네이밍 규약을 D1-A (TSV stat_key 그대로) vs D1-B (외부 표준명 + Importer 매핑, 권장) 중 택1 하고 PD 승인 획득. - CK-2 (FlatStat 분기): Importer 가
effect_type == FlatStat인 2개 항목(ReviveCountFlat,IdleCapHoursFlat) 을 PercentStat 곱연산 경로와 분리하여 집계.BuildSummary필드 타입이float이 아닌int(또는 전용 scalar). 구현 전 메모리 체크포인트CP-T3 FlatCapped재확인. - CK-3 (헬퍼 메서드):
PrestigeCalculator.GetIdleCapHours(state)헬퍼 메서드 신설. 시그니처 불변 원칙 위반 아님 (신규 추가 = OK). Sprint 03IdleTicker가 단일 호출자. - CK-4 (Sprint 03 문서 소급 수정):
sprint-03-idle-research.md§2.1 / §5 축8 의 "프레스티지 아이템 #24IdleCapHours" 2개 위치를 "stat_key=IdleCapHoursFlat" 로 문서 수정 (코드 영향 0). - CK-5 (Smoke Test 수치): Sprint 03 W2 Smoke Test (
sprint-03-idle-research.md§7 #09) — "100층 유저 10h 방치 → PP ≈ 50 (prestigeBonus=1.0 무투자 가정)". 이 수치는prestige_system_spec.md§3.2(C)maxStage × 0.05 × 10h = 50공식 +IdleYieldPct무투자 = 1.0 배 → 50 PP 기댓값 불변 확인. Sprint 02 W2 Importer 가IdleYieldPct를BuildSummary.IdleRateMul에 제대로 매핑했다면 Smoke Test 그대로 통과.
§6 결론
- 판정 최종: YELLOW — 착수 가능. 구조적 gap 0, 네이밍 규약만 1건 (D1) 확정하면 즉시 Sprint 02 W2 착수.
- CK-1 ~ CK-5 5건 중 CK-1 + CK-4 만 PD 승인 필요 (5분 분량). CK-2 / CK-3 / CK-5 는 code-lead 구현 체크리스트로 이월.
- RED 상태 아님: 30항목 수치 시트 보강(신규 item_id 추가) 불필요. TSV 수치 변경 불필요.
- 가장 빠른 해소 경로: PD가 D1-B안(권장) + CK-4 문서 소급 수정 승인 → 30분 내 Sprint 02 W2 Importer 착수 조건 충족.
§A 참고: 빌드 시뮬 매트릭스와의 정합성 (sanity check)
prestige_effect_matrix.tsv 10개 빌드의 주요 열(atk_bonus / idle_bonus / drop_bonus) 은 stat_key 추상화된 비율 값으로 정의되어 TSV 컬럼명에 의존하지 않음. 즉 D1-A/B 어느 쪽 결정이든 매트릭스 파일은 수정 불요. game-designer 수치 튜닝 경로(메모리 "Calculator 시그니처 불변 원칙") 유지.
(끝)