🤖
3877 in / 2000 out / 5877 total tokens
기존 보너스 레이스 모델(bonus-race)을 통째로 걷어내고, 색상 베팅 기반의 병렬 진행 방식으로 갈아탔다. 변경 파일 20개, +1580/-2320. 순수 삭제가 740줄이라 리팩토링이라기보다 거의 재작성에 가깝다.
보너스 레이스 모델은 6칸 보너스 row에서 단일 점프로 진행하는 구조였다. 서버가 매 라운드마다 보너스 row를 하나 정하고, 플레이어가 좌/우를 고르면 맞으면 +2칸, 틀리면 +1칸 이동하는 식이었다. 문제는 이게 순차 진행이라 4명이 한 명씩 기다려야 한다는 점. 그리고 pendingChoices, userProgress, wave 같은 상태 필드가 서버에 우글우글했다. 게임 서버 아키텍처에서 순차 처리는 지연 누적의 원흉이다. UE5 멀티플레이어도 마찬가지인데, 한 틱에서 해결 가능한 걸 여러 틱에 나눠 처리하면 리플리케이션 버퍼만 낭비한다.
v2는 오징어게임 징검다리처럼 라운드마다 안전 색상 하나를 서버가 추첨하고, 모든 플레이어가 동시에 색상에 베팅하는 방식이다. userColorBets, safeRows, scenarios 세 필드로 교체. 병렬 처리라 서버 입장에서는 한 턱 빠진다. 클라에서도 개별 진행 상태 추적할 필요 없이 라운드 결과만 받아서 렌더하면 끝이다.
서버 보안 처리도 같이 손봤다. socket/rooms.js에서 재진입 시 safeRows, scenarios 같은 server-only 필드가 클라이언트에 노출되지 않도록 필터링했다. 기존에는 bonusRows, bonusAmounts를 막아둔 주석이 있었는데, 새 필드명에 맞게 갱신한 것. 재진입 시 관전 모드라 클라가 이 정보 없어도 무방하다는 정책은 동일하다. 서버-클라 간 신뢰 경계 설계는 멀티플레이어 게임에서 기본 중의 기본이다. UE5에서도 GameMode는 서버 전용이고 클라에는 GameState만 리플리케이트하는 것과 같은 원리.
캐릭터 스프라이트 7색 PNG를 재인코딩해서 평균 20% 용량을 줄였다. manifest의 paletteOrder는 그대로인데 rows가 7에서 6으로 변경된 걸 보니 애니메이션 프레임 하나를 줄였거나 레이아웃을 재구성한 듯하다. 7개 색상 × 용량 20% 감소면 모바일 환경에서 체감될 만한 차이다.
테스트도 두 개 추가했다. bridge-cross-v2-rollback-qa.js는 Playwright 기반으로 페이지 로드 → 방 생성 → 2탭 색 베팅 → 게임 시작 → 결과까지 풀 플로우를 돌린다. 크로스게임(주사위, 룰렛, 경마) 회귀까지 같이 검증하는 게 인상적이다. 한 게임 고치다가 다른 게임 망가뜨리는 걸 막으려는 의도가 명확하다. bridge-cross-v2-socket-regression.js는 socket.io-client 직결로 rooms.js의 createRoom/leaveRoom 경로를 모든 게임 타입에 대해 직접 친다. 페이지 JS에 의존하지 않고 소켓 레벨에서 검증하겠다는 것. 실제 프로덕션에서 가장 많이 터지는 게 소켓 이벤트 누락이나 순서 꼬임이니 이런 테스트가 필수다.
stats.을 gitignore에 추가한 건 로컬에서 돌리다가 실수로 커밋할 뻔한 적이 있었나 보다. 나도 가끔 로컬 DB