10 KiB
ArenaScene 모듈화 작업지시서
작성일: 2026-05-23
목적
src/game/ArenaScene.js가 매치 생명주기, Phaser scene hook, HUD DOM, 관전 카메라, 미니맵, 최종교전 슬로우모션, 승리 연출, 사망 통계까지 함께 들고 있다. 이번 작업은 기능별 모듈로 책임을 나누고 ArenaScene은 scene orchestration과 외부 공개 메서드의 얇은 진입점으로 줄이는 리팩터링이다.
이번 작업은 동작 보존 리팩터링으로 진행한다. 게임 규칙, UI 문구, 튜닝 상수, 에셋, API 스펙은 바꾸지 않는다.
현재 코드 관찰
- 대상 파일은
src/game/ArenaScene.js다.src/main.js는 대소문자 포함./game/ArenaScene.js를 import하므로 파일명/import 경로는 유지한다. - 현재 파일 크기는 약 42KB이고 역할이 아래 구간에 섞여 있다.
| 구간 | 현재 책임 |
|---|---|
ArenaScene constructor, preload(), create(), startMatch() |
scene 상태 초기화, 입력 바인딩, 매치 시작 |
spawnSplitFighters() |
전투 중 분열 전투원 생성 |
resetMatchDeathStats() ~ persistDailyDeathStats() |
실제 매치 사망 집계, 상단 battle notice 스케줄링, API 연동 |
resetKillLog() ~ getKillLogNodes() |
킬로그 DOM |
update() ~ getObservedCombatCenter() |
전투 update loop, 관전 카메라, 선택 관전, presentation 관전 |
triggerFinalCombatSlowMotion() ~ restoreSceneTimeScale() |
최종교전 슬로우모션 |
setPaused() |
실제 매치 pause/resume |
setMainCameraZoom() ~ snapMinimapFrameValue() |
줌과 미니맵 viewport frame |
updateScoreboard() |
팀 badge HUD DOM |
finishMatch() |
종료 판정, presentation 재시작, 사망 통계 저장, 승리 상태 |
| 파일 하단 helper들 | 승리 overlay/audio, death notice 문구, kill log node 생성, 스폰 확장, spectator 순수 계산 |
유지해야 할 외부 계약
리팩터링 중 아래 표면은 깨지지 않게 유지한다. 구현을 다른 모듈로 옮기더라도 필요하면 ArenaScene에 같은 이름의 delegate 메서드를 남긴다.
앱 진입점 계약
src/main.js는 다음을 사용한다.
new ArenaScene({ getInitialMatchConfig, setStatus })arenaScene.startMatch(matchConfig)arenaScene.isMatchPaused()arenaScene.togglePause()
combat 계약
src/game/combat.js는 scene에 아래 optional callback 메서드가 있다고 보고 호출한다.
scene.observeCombat(attacker, defender)scene.triggerFinalCombatSlowMotion(attacker, defender, attackAnimation)winner.scene.recordKill(winner, defender)fighter.scene.spawnSplitFighters(fighter, splitOnDeath)
이 계약은 우선 유지한다. combat.js까지 구조를 바꾸는 작업은 이번 리팩터링의 필수 범위가 아니다.
Phaser hook 계약
preload()create()update(time)
hook은 ArenaScene에 남기고 기능 모듈로 위임한다.
권장 분리 경계
아래 파일명은 권장안이다. 기존 코드 패턴과 충돌하면 더 나은 이름을 써도 되지만, 책임 경계는 유지한다.
| 권장 모듈 | 옮길 책임 | 비고 |
|---|---|---|
src/game/arenaMatchRuntime.js |
fighter plan 확장, spawn cluster 계산, team size sync, 분열 스폰 보조 로직 | 순수 helper를 먼저 빼고 scene mutation은 작은 함수로 감싼다. |
src/game/arenaSpectatorCamera.js |
spectator state 계산, 관전 대상 선택, presentation follow, 선택 fighter camera focus | camera 관련 계산과 scene 동작을 한 경계로 묶는다. |
src/game/arenaMinimap.js |
minimap camera 생성, zoom 연동, viewport frame 렌더링 | spectator 모듈이 너무 커지면 분리한다. |
src/game/arenaFinalCombatEffects.js |
final combat slow motion 진입/유지/복귀, easing, timeScale 적용/복원 | Arcade Physics timeScale 역수 적용을 그대로 보존한다. |
src/ui/arenaScoreboard.js |
팀 badge DOM 갱신과 team click 핸들링 | DOM 생성 책임을 scene에서 뺀다. |
src/ui/arenaKillLog.js |
kill log reset/append/node factory/avatar URL helper | document 접근과 node cache 경계를 명확히 한다. |
src/ui/battleDeathNotice.js |
종족별 death count helper, notice 문구 생성, notice DOM/timer, 오늘 사망 통계 fetch/persist 흐름 | presentation match 제외 조건을 유지한다. |
src/ui/victoryCelebration.js |
victory overlay DOM, confetti, fanfare audio priming/playback | setStatus() wrapper가 이 모듈만 호출하게 만든다. |
ArenaScene에 남길 책임은 다음 정도로 제한한다.
- scene 상태의 루트 소유권
- Phaser hook과 입력 이벤트 등록
- 매치 시작/종료 orchestration
main.js와combat.js가 부르는 공개 delegate 메서드- 기능 모듈을 호출하는 update 흐름
구현 원칙
- 첫 패스에서는 상태 저장소를 새로 만들지 않는다. 이미 scene에 있는 상태를 유지하고, 기능 모듈은 필요한 scene 또는 명시적 인자를 받게 한다.
- 먼저 파일 하단의 순수 helper를 추출한다. 예를 들면 spectator 계산, spawn plan 계산, death count/message 계산은 scene method 이동보다 리스크가 낮다.
- DOM 전용 로직은
src/ui/로 옮긴다. Phaser object 조작과 DOM node 생성이 한 함수에 섞이지 않게 한다. - 외부 호출 메서드는 한 번에 제거하지 않는다. 예를 들어
recordKill()은ArenaScene에 남겨appendKillLog()와 death count 갱신 함수에 위임해도 된다. - 공통 manager class를 크게 만들지 않는다. 기능별 함수 또는 상태가 분명한 작은 controller가 우선이다.
- 기존 상수는 가능한 한 현재 위치를 유지한다. 기능 전용 상수가 새 모듈과 함께 이동할 때만 옮긴다.
- 문서 업데이트가 필요한 구조 변경이면
CONTEXT.md,agent.md,todo.md의 설명도 실제 변경에 맞게 갱신한다.
권장 작업 순서
1. 기준선 확인
npm run build로 현재 build 기준선을 확인한다.- 자동 테스트 스크립트는
package.json에 없다. 빌드와 수동 smoke test를 기본 검증으로 잡는다.
2. pure helper부터 추출
- fighter plan/spawn cluster helper
- death count/message helper
- spectator state/position helper
- victory confetti/audio helper 중 DOM 밖 계산
이 단계에서는 ArenaScene 동작 순서와 method signature를 바꾸지 않는다.
3. DOM UI를 분리
- scoreboard
- kill log
- battle notice/death stats notice
- victory celebration
DOM module은 생성한 node, cached node, timer 정리 책임을 문서화된 함수 경계로 드러낸다.
4. camera/effect를 분리
- spectator camera와 selection focus
- minimap 생성/viewport frame
- final combat slow motion
카메라가 같은 프레임에서 여러 feature에게 조작되므로 update()의 우선순위는 그대로 유지한다.
현재 우선순위는 대략 다음과 같다.
- fighter HUD sync
- pause면 minimap frame만 갱신하고 return
- 매치 진행 중 fighter update
- presentation이면 presentation follow 후 return
- 선택 fighter focus가 있으면 return
- match over면 return
- spectator state 기반 camera follow
- minimap viewport frame 갱신
5. scene를 얇게 정리
- hook과 orchestration만 남았는지 확인한다.
- 외부 delegate 메서드가 계속 같은 이름으로 동작하는지 확인한다.
- import graph가 순환하지 않는지 확인한다. 특히
combat.js와 scene feature module이 서로 import하지 않게 한다.
리팩터링 중 주의점
presentationMode전투는 실제 매치와 다르다. 사망 통계 fetch/persist, battle notice, pause, 승리 연출 오디오 priming의 조건을 바꾸지 않는다.matchId는 비동기 death stats fetch와 전투 delayed action의 stale result를 막는 기준이다. 초기화 순서를 흐트러뜨리지 않는다.- 새 매치 시작 시 battle notice timer, slow-motion timer/animation frame, combat object, selected fighter 상태가 정리되어야 한다.
- 최종교전 슬로우모션은 Phaser timer, tween, animation, Arcade Physics를 함께 건드린다.
arcadePhysicsTimeScale()의 역수 규칙을 잃지 않는다. - 미니맵 viewport frame은 main camera에서 ignore되고 zoom 상태에 따라 alpha/visibility가 바뀐다.
setStatus()는 단순 상태 전달만 하지 않는다. 기존 승리/무승부 message에 맞춰 celebration overlay를 제거/생성한다.- scoreboard badge 클릭은
selectRandomTeamFighter()로 이어지고 선택 fighter는 spectator 관전보다 우선한다. - Slime
spawnMultiplier와splitOnDeath는 team size, scoreboard, finish 판정에 영향을 준다.
완료 조건
ArenaScene.js가 기능별 모듈을 호출하는 orchestration 파일로 줄어든다.main.js와combat.js의 기존 호출 계약이 깨지지 않는다.- 실제 매치와 presentation 매치 흐름이 기존처럼 구분된다.
- 신규 모듈의 위치가 게임 로직(
src/game)과 DOM UI(src/ui) 책임을 반영한다. npm run build가 통과한다.- 관련 문서가 실제 구조와 맞게 갱신된다.
수동 smoke test
최소한 아래 흐름을 확인한다.
- 앱 최초 로드 시 presentation 전투가 시작되고 실제 매치 입력 UI가 정상 동작한다.
- 실제 매치를 시작하면 팀 badge와 전투원이 표시되고 restart가 새 매치를 시작한다.
- pause/continue가 physics, timer, tween, sprite animation을 같이 멈추고 재개한다.
- 마우스 wheel zoom, spectator follow, fighter 클릭 focus, team badge 클릭 focus, minimap viewport frame이 동작한다.
- 처치 발생 시 kill log와 scoreboard 생존 수가 갱신된다.
- Slime이 배정된 매치에서 spawn multiplier와 split-on-death가 깨지지 않는다.
- 실제 매치가 끝나면 승리/무승부 상태와 victory celebration 흐름이 정상이다.
- 실제 매치가 충분히 지속되면 battle notice 흐름이 동작한다. death stats API가 실패하더라도 scene이 멈추지 않고 warning 수준으로 끝난다.
산출물 기대치
- 기능별 신규 모듈
- 얇아진
src/game/ArenaScene.js - 필요 시 갱신된
CONTEXT.md,agent.md,todo.md - 검증 결과 요약