Files
maplecontest/docs/superpowers/plans/2026-06-16-codeblock-modularization.md

8.2 KiB

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~의 각 런)

writeCodeblocksconst combat = codeblock('SlayDeckController','SlayDeckController', [<props>], [\n method('OnBeginPlay', …),\n method('ReqLoadAscension', …),\n … 161개 …\n]) 에서 메서드 배열을 런별로 분리:

  1. tools/deck/cb/<name>.mjs 생성:
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>', `…`, ),
];
  1. writeCodeblocks의 해당 런 method() 호출 스팬을 제거하고, methods 배열을 spread로 교체: codeblock(…, [<props>], [ ...bootMethods, ...stateMethods, …, ...shopMethods ]).
  2. 검증 게이트(codeblock IDENTICAL) → churn 복원 → 커밋.
  3. ⚠️ 메서드가 writeCodeblocks 지역변수/다른 메서드를 JS레벨로 참조하면(드묾) undefined throw/diffcheck로 노출 → 그 변수도 lib로 옮기거나 인자화.
  • import 이름은 lib export에서 자동 파생(누락 방지). 메서드 본문은 Lua 문자열이라 보간(${RUN_LENGTH}·${luaCardsTable(...)})만 JS평가.

런 → 모듈 경계 (원본 순서, 161개)

모듈 export 첫 메서드 → 끝 메서드
cb/boot.mjs bootMethods OnBeginPlayAscStartHpPenalty 11
cb/state.mjs stateMethods HideGameHudCloseBoard 12
cb/soul.mjs soulMethods ShowSoulShopApplySoulUnlocks 11
cb/charselect.mjs charSelectMethods ShowCharacterSelectSetEntityEnabled 5
cb/run.mjs runMethods StartRunReviveMonsterEntity 6
cb/deckturn.mjs deckTurnMethods ShuffleRenderPiles 8
cb/deckview.mjs deckViewMethods OpenDeckInspectApplyAllDeckCardVisual 12
cb/hand.mjs handMethods GetHandSlotXSelectDiscardSlot 18
cb/combat.mjs combatMethods PlayCardContinueAfterBoss 20
cb/jobs.mjs jobMethods ShowJobChoiceSetJob 5
cb/runend.mjs runEndMethods TeleportToActMapEndRun 3
cb/render.mjs renderMethods BuffsLabelRenderRun 12
cb/reward.mjs rewardMethods CardPoolPickReward 4
cb/items.mjs itemMethods HasRelicRenderRelics 12
cb/tooltip.mjs tooltipMethods BuildCardKeywordTooltipHideTooltip 6
cb/map.mjs mapMethods ShowMapPickNode 7
cb/shop.mjs shopMethods ShowShopOpenChest 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 즉시 노출, 증분으로 범위 최소. 단방향 의존 순환 없음.