# 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', [], [\n method('OnBeginPlay', …),\n method('ReqLoadAscension', …),\n … 161개 …\n])` 에서 메서드 배열을 런별로 분리: 1. `tools/deck/cb/.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 Methods = [ method('', `…`, …), …, // ← 원본 method() 호출 verbatim 이동 method('', `…`, …), ]; ``` 2. writeCodeblocks의 해당 런 method() 호출 스팬을 제거하고, methods 배열을 spread로 교체: `codeblock(…, [], [ ...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 `, 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 즉시 노출, 증분으로 범위 최소. 단방향 의존 순환 없음.