실시간 멀티플레이 협동 카페 운영 게임 · 개인 프로젝트 · 2025.12 ~ Backend Java 17, Spring Boot 3.5, Spring WebSocket(STOMP/SockJS), Redis(Lettuce, Lua) · Frontend React, TypeScript, Phaser 3
preserveReceiveOrder로 해결| Situation | 브라우저 게임이라 UDP를 쓸 수 없어 WebSocket(TCP)으로 위치를 동기화. 다른 플레이어가 멈췄다 몰아서 움직이고 순간이동함 |
|---|---|
| Task | 프로토콜을 바꾸지 않고 TCP 안에서 체감 지연과 끊김을 줄이기 |
| Action | 매 프레임(약 60Hz) 전송 → 50ms 주기(20Hz) + 정지 시 미전송 · 내 캐릭터는 로컬 즉시 이동 · 원격 캐릭터는 목표 좌표 + Lerp 보간 · 지연 구간을 5개로 나눠 바꿀 수 있는 구간에 집중 |
| Result | 1인당 송신 메시지 최대 1/3로 감소, 순간이동 제거. Lerp 계수의 표시 지연(약 236ms)을 계산해 다음 개선(스냅샷 보간) 근거 확보 |
| Situation | 서버 재시작 후 유령 플레이어 잔존, 같은 닉네임 입장 시 기존 플레이어 덮어쓰기, 클라이언트 값을 믿어 room:null 키 생성, 이동마다 Redis 동기 쓰기 |
|---|---|
| Task | 상태 저장소의 정합성을 지키면서 위치 전달 경로에서 Redis 대기를 제거 |
| Action | HSETNX로 닉네임 선점 · 세션 속성 기반 식별 · 이동은 즉시 브로드캐스트하고 저장은 100ms 배치 · 배치가 만든 "퇴장자 부활" 경쟁 조건을 Lua HEXISTS+HSET로 원자화 · 시작 시 SCAN 기반 유령 정리 · 저장소 인터페이스 분리 |
| Result | Redis 호출 초당 200회 → 방당 최대 10회. 중복 입장·부활·room:null 재현 테스트 모두 통과 |
| Situation | 같은 클라이언트가 보낸 이동 (10,20)→(30,40) 중 (10,20)이 최종 저장됨. 7회 중 5회 재현 |
|---|---|
| Task | 원인을 확정하고, 처리량을 해치지 않으면서 순서를 보장 |
| Action | 대기를 넣으면 통과한다는 사실만으로 덮지 않고 실패 시점 상태를 출력해 원인 확정 → STOMP inbound 스레드 풀의 병렬 처리. setPreserveReceiveOrder / setPreservePublishOrder로 세션 안에서만 직렬화 |
| Result | 이동 30개 연속 전송 테스트 포함 전체 11개 × 5회 반복 통과 |
| Situation | 늦은 입장자에게 보낸 기존 플레이어 목록이 예외 없이 사라짐. 우회로 쓴 /topic/private/{nickname}은 누구나 구독 가능 |
|---|---|
| Task | 인증 없이 특정 세션에만 안전하게 전달 |
| Action | 세션 ID 헤더를 붙인 convertAndSendToUser • /queue 브로커 등록 + /user/queue/* 구독. N개 메시지를 ROOM_STATE 1개로 통합 |
| Result | sync·errors·pong 3개 1:1 채널을 테스트로 검증 |