feat(charselect): 메이커 저작 stock 이관 + 컨트롤러 이미지 주입 (Phase 2 파일럿) #72
127
docs/superpowers/plans/2026-06-16-codeblock-modularization.md
Normal file
127
docs/superpowers/plans/2026-06-16-codeblock-modularization.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Phase 1b — codeblock 메서드 모듈화 구현 계획
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: superpowers:executing-plans 로 태스크 단위 구현. 단계는 `- [ ]` 체크박스.
|
||||
|
||||
**Goal:** `gen-slaydeck.mjs` `writeCodeblocks()`의 메서드 161개를 연속-런 모듈 `cb/*.mjs`로 분리하되 출력 `SlayDeckController.codeblock`은 바이트 동일.
|
||||
|
||||
**Architecture:** 단방향 의존 orchestrator→cb→lib. method/prop/codeblock 헬퍼+공유상수를 `lib/codeblock.mjs`로. 메서드는 **원본 순서 보존**을 위해 기능 버킷이 아닌 **연속 구간**으로 나눠 `writeCodeblocks`가 순서대로 spread-concat. prop 103개는 오케스트레이터 유지.
|
||||
|
||||
**Tech Stack:** Node ESM. 검증 = `diffcheck`(codeblock 바이트 동일) + 미러 `node --test`.
|
||||
|
||||
**의존:** Phase 1(#70)의 모듈 gen-slaydeck 위에 스택(`feature/cb-modularization`). #70 머지 후 main에 리베이스.
|
||||
|
||||
---
|
||||
|
||||
## 🔑 검증 게이트 (모든 Task 공통)
|
||||
각 추출 후:
|
||||
```
|
||||
node tools/deck/gen-slaydeck.mjs
|
||||
node tools/verify/diffcheck.mjs
|
||||
```
|
||||
**합격**: `RootDesk/MyDesk/SlayDeckController.codeblock`(+ui·common) **IDENTICAL**. 워킹트리 ` M`은 autocrlf churn → `git checkout --`로 복원, **산출물은 커밋 안 함**(소스만).
|
||||
|
||||
---
|
||||
|
||||
### Task 1: `lib/codeblock.mjs` — 헬퍼 + 공유 상수 추출
|
||||
|
||||
**Files:** Create `tools/deck/lib/codeblock.mjs`; Modify `tools/deck/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1:** `lib/codeblock.mjs` 생성. gen-slaydeck.mjs에서 이동:
|
||||
- 함수: `prop`·`method`·`codeblock` (정의 본문 그대로).
|
||||
- `writeCodeblocks` 지역 상수 9개(현 `:292-300`): `RUN_LENGTH`(5) `GOLD_PER_WIN`(25) `CARD_PRICE`(30) `REST_HEAL`(30) `RELIC_PRICE`(60) `ACT_COUNT`(5) `ACT_MAPS`(['map01'..'map05']) `LOBBY_MAP`('lobby') `LOBBY_SPAWN`('Vector3(-5, 0.03, 0)'). → writeCodeblocks 본문에서 제거(import로 대체).
|
||||
- 끝에 `export { prop, method, codeblock, RUN_LENGTH, GOLD_PER_WIN, CARD_PRICE, REST_HEAL, RELIC_PRICE, ACT_COUNT, ACT_MAPS, LOBBY_MAP, LOBBY_SPAWN };`
|
||||
- ⚠️ `prop`/`method`/`codeblock`이 다른 헬퍼(없음 — 순수)·데이터를 참조하지 않는지 확인. 참조 시 함께 import.
|
||||
|
||||
- [ ] **Step 2:** gen-slaydeck.mjs에 `import { prop, method, codeblock, RUN_LENGTH, GOLD_PER_WIN, CARD_PRICE, REST_HEAL, RELIC_PRICE, ACT_COUNT, ACT_MAPS, LOBBY_MAP, LOBBY_SPAWN } from './lib/codeblock.mjs';` 추가(기존 lib import 옆).
|
||||
|
||||
- [ ] **Step 3:** 검증 게이트 → codeblock IDENTICAL → churn 복원.
|
||||
|
||||
- [ ] **Step 4:** 커밋
|
||||
```
|
||||
git add tools/deck/lib/codeblock.mjs tools/deck/gen-slaydeck.mjs
|
||||
git commit -m "refactor(cb): lib/codeblock.mjs로 헬퍼·상수 추출 (출력 바이트 동일)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 메서드 추출 공통 레시피 (Task 2~의 각 런)
|
||||
|
||||
`writeCodeblocks`의 `const combat = codeblock('SlayDeckController','SlayDeckController', [<props>], [\n method('OnBeginPlay', …),\n method('ReqLoadAscension', …),\n … 161개 …\n])` 에서 메서드 배열을 런별로 분리:
|
||||
1. `tools/deck/cb/<name>.mjs` 생성:
|
||||
```js
|
||||
import { method, RUN_LENGTH, GOLD_PER_WIN, CARD_PRICE, REST_HEAL, RELIC_PRICE, ACT_COUNT, ACT_MAPS, LOBBY_MAP, LOBBY_SPAWN } from '../lib/codeblock.mjs';
|
||||
import { /* lib/data.mjs 전체 — 자동 파생 */ } from '../lib/data.mjs';
|
||||
import { /* lib/ui-helpers.mjs 전체 — 자동 파생 */ } from '../lib/ui-helpers.mjs';
|
||||
|
||||
export const <name>Methods = [
|
||||
method('<First>', `…`, …),
|
||||
…, // ← 원본 method() 호출 verbatim 이동
|
||||
method('<Last>', `…`, …),
|
||||
];
|
||||
```
|
||||
2. writeCodeblocks의 해당 런 method() 호출 스팬을 제거하고, methods 배열을 spread로 교체:
|
||||
`codeblock(…, [<props>], [ ...bootMethods, ...stateMethods, …, ...shopMethods ])`.
|
||||
3. **검증 게이트**(codeblock IDENTICAL) → churn 복원 → 커밋.
|
||||
4. ⚠️ 메서드가 writeCodeblocks 지역변수/다른 메서드를 **JS레벨로 참조**하면(드묾) undefined throw/diffcheck로 노출 → 그 변수도 lib로 옮기거나 인자화.
|
||||
- import 이름은 lib export에서 **자동 파생**(누락 방지). 메서드 본문은 Lua 문자열이라 보간(`${RUN_LENGTH}`·`${luaCardsTable(...)}`)만 JS평가.
|
||||
|
||||
### 런 → 모듈 경계 (원본 순서, 161개)
|
||||
| 모듈 | export | 첫 메서드 → 끝 메서드 | 수 |
|
||||
|---|---|---|---|
|
||||
| `cb/boot.mjs` | `bootMethods` | `OnBeginPlay` → `AscStartHpPenalty` | 11 |
|
||||
| `cb/state.mjs` | `stateMethods` | `HideGameHud` → `CloseBoard` | 12 |
|
||||
| `cb/soul.mjs` | `soulMethods` | `ShowSoulShop` → `ApplySoulUnlocks` | 11 |
|
||||
| `cb/charselect.mjs` | `charSelectMethods` | `ShowCharacterSelect` → `SetEntityEnabled` | 5 |
|
||||
| `cb/run.mjs` | `runMethods` | `StartRun` → `ReviveMonsterEntity` | 6 |
|
||||
| `cb/deckturn.mjs` | `deckTurnMethods` | `Shuffle` → `RenderPiles` | 8 |
|
||||
| `cb/deckview.mjs` | `deckViewMethods` | `OpenDeckInspect` → `ApplyAllDeckCardVisual` | 12 |
|
||||
| `cb/hand.mjs` | `handMethods` | `GetHandSlotX` → `SelectDiscardSlot` | 18 |
|
||||
| `cb/combat.mjs` | `combatMethods` | `PlayCard` → `ContinueAfterBoss` | 20 |
|
||||
| `cb/jobs.mjs` | `jobMethods` | `ShowJobChoice` → `SetJob` | 5 |
|
||||
| `cb/runend.mjs` | `runEndMethods` | `TeleportToActMap` → `EndRun` | 3 |
|
||||
| `cb/render.mjs` | `renderMethods` | `BuffsLabel` → `RenderRun` | 12 |
|
||||
| `cb/reward.mjs` | `rewardMethods` | `CardPool` → `PickReward` | 4 |
|
||||
| `cb/items.mjs` | `itemMethods` | `HasRelic` → `RenderRelics` | 12 |
|
||||
| `cb/tooltip.mjs` | `tooltipMethods` | `BuildCardKeywordTooltip` → `HideTooltip` | 6 |
|
||||
| `cb/map.mjs` | `mapMethods` | `ShowMap` → `PickNode` | 7 |
|
||||
| `cb/shop.mjs` | `shopMethods` | `ShowShop` → `OpenChest` | 9 |
|
||||
|
||||
최종 concat 순서(= 원본): `[ ...bootMethods, ...stateMethods, ...soulMethods, ...charSelectMethods, ...runMethods, ...deckTurnMethods, ...deckViewMethods, ...handMethods, ...combatMethods, ...jobMethods, ...runEndMethods, ...renderMethods, ...rewardMethods, ...itemMethods, ...tooltipMethods, ...mapMethods, ...shopMethods ]`.
|
||||
|
||||
---
|
||||
|
||||
### Task 2: 런 추출 배치 A (말단부터 — 위험 낮은 순)
|
||||
**Files:** Create `cb/shop.mjs map.mjs tooltip.mjs items.mjs reward.mjs`; Modify gen-slaydeck.mjs
|
||||
|
||||
- [ ] 레시피로 **shop → map → tooltip → items → reward** 추출·검증·커밋(런 1개당 또는 묶음당 게이트 통과 필수).
|
||||
|
||||
### Task 3: 런 추출 배치 B
|
||||
**Files:** Create `cb/render.mjs runend.mjs jobs.mjs combat.mjs`; Modify gen-slaydeck.mjs
|
||||
- [ ] 레시피로 **render → runend → jobs → combat** 추출·검증·커밋.
|
||||
|
||||
### Task 4: 런 추출 배치 C
|
||||
**Files:** Create `cb/hand.mjs deckview.mjs deckturn.mjs run.mjs`; Modify gen-slaydeck.mjs
|
||||
- [ ] 레시피로 **hand → deckview → deckturn → run** 추출·검증·커밋.
|
||||
|
||||
### Task 5: 런 추출 배치 D (앞부분 — 마지막)
|
||||
**Files:** Create `cb/charselect.mjs soul.mjs state.mjs boot.mjs`; Modify gen-slaydeck.mjs
|
||||
- [ ] 레시피로 **charselect → soul → state → boot** 추출·검증·커밋. 완료 후 writeCodeblocks는 props 배열 + `[ ...17 spreads ]` + write만 남아야 함.
|
||||
|
||||
---
|
||||
|
||||
### Task 6: 마무리 — RULES + 회귀 + PR
|
||||
|
||||
**Files:** Modify `RULES.md`
|
||||
|
||||
- [ ] **Step 1:** RULES §1의 gen-slaydeck 모듈 설명에 `tools/deck/cb/*.mjs`(메서드)·`tools/deck/lib/codeblock.mjs`(헬퍼·상수) 추가. 단일소스 표/보조 생성기 일관성 유지.
|
||||
- [ ] **Step 2:** 회귀: `node --test tools/balance/sim-balance.test.mjs` · `node --test tools/map/rogue-map.test.mjs` (exit 0).
|
||||
- [ ] **Step 3:** 최종 재생성 + 검증 게이트(누적 codeblock IDENTICAL). `git status --short` 산출물 변경 없음.
|
||||
- [ ] **Step 4:** RULES 커밋 → push → PR(`node tools/git/gitea-pr.mjs create <spec.json>`, UTF-8). PR 제목·본문 한국어(RULES §4).
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
- **스펙 커버리지**: lib/codeblock(T1) · 메서드 17런 모듈화(T2~5) · prop 유지(범위 명시) · 바이트동일 게이트(공통) · RULES(T6) · 미러회귀(T6). 누락 없음.
|
||||
- **플레이스홀더**: 런 경계는 첫/끝 메서드로 구체 지정(161개 합), 상수 9개 명시, import 자동 파생. "verbatim 이동"은 리팩터 특성(바이트 검증이 정확성 보장).
|
||||
- **타입 일관성**: export명(`xMethods`)↔concat spread 일치. lib/codeblock export↔orchestrator/cb import 일치.
|
||||
- **리스크**: 메서드 JS레벨 외부참조 → diffcheck/throw 즉시 노출, 증분으로 범위 최소. 단방향 의존 순환 없음.
|
||||
Reference in New Issue
Block a user