작성·운영: 몽실 · 규칙 대조 및 수정:
대결방은 최대 6명이 각자 자기 보드를 진행하면서 점수와 작은 보드 화면을 교환하는 기능입니다. 서버리스 함수와 Redis 저장소를 사용하며, 브라우저가 약 2초마다 상태를 보내고 응답을 받습니다. 여기서는 현재 구현의 동작과 한계를 설명합니다.
한 번의 상태 교환
| 순서 | 처리 | 화면에 보이는 결과 |
|---|---|---|
| 1. 브라우저 → 서버 | 방 코드, 닉네임, 참가 토큰, 점수, 생존 여부, 보드 스냅샷, 공격·소비 수량 전송 | 내 게임은 로컬에서 계속 진행 |
| 2. 서버 → 저장소 | 참가자를 확인하고 상태와 공격 수신함 갱신 | 다른 참가자가 다음 조회에서 확인할 상태가 준비됨 |
| 3. 서버 → 브라우저 | 참가자 순위, 보드와 수신 공격 수량 응답 | 미니보드·순위 갱신, 공격 예고 표시 |
2초는 요청을 보내는 간격이며 화면 간 동기화 지연의 상한은 아닙니다. 요청 대기와 네트워크 지연이 더해질 수 있습니다. 상대 미니보드는 연속 영상이 아니라 최근 스냅샷입니다.
같은 닉네임과 마지막 빈자리
브라우저에 보관하는 참가 토큰으로 방 안의 닉네임 소유권을 구분합니다. 다른 토큰이 이미 사용 중인 닉네임을 차지할 수 없도록 확인합니다. 이는 계정 로그인이나 다른 기기로 이어지는 사용자 인증을 제공한다는 뜻은 아닙니다.
참가 처리에서는 닉네임 중복, 방 상태, 6명 정원 확인과 참가자 저장을 Redis의 한 스크립트 안에서 실행합니다. 동시에 마지막 자리에 들어온 두 요청을 각각 빈자리로 판단하는 문제를 막기 위한 구조입니다.
같은 과일 순서, 서로 다른 플레이
방을 시작할 때 같은 난수 시드를 받아 같은 순서의 과일을 사용합니다. 플레이어가 드롭한 위치와 시점은 서로 다르므로 보드 배치까지 같아지는 것은 아닙니다. 서버에서 받은 시작 시각과 시계 차이를 이용해 카운트다운합니다.
아이템전에서는 콤보로 만든 공격 수량을 서버의 원자 증가 연산으로 상대 수신함에 더합니다. 받는 쪽은 수량을 확인한 뒤 약 0.9초의 예고를 거쳐 체리를 투하합니다. 낙하 위치는 무작위이며, 공격이 전달되는 데는 폴링과 네트워크 시간이 추가됩니다.
현재 브라우저 코드는 진행 중인 상태 전송이 끝나기 전에 다음 전송을 겹쳐 보내지 않도록 관리합니다. 이전 방의 늦은 응답이 새 방에 적용되지 않도록 방 변경도 구분합니다. 이 동작을 확인하는 코드가 tests/battle-update.test.cjs에 있습니다.
연결이 끊겼을 때의 처리
- 대기실: 마지막 상태 보고 이후 30초가 지나도록 응답이 없는 참가자는 정리 대상이 됩니다. 남은 참가자가 없다면 방을 삭제하고, 방장이 나갔다면 남은 참가자에게 권한을 넘깁니다.
- 대결 중: 마지막 보고 시각을 기준으로 생존 여부를 계산합니다. 30초를 넘는 무응답은 응답 화면에서 탈락으로 취급됩니다. 방이 아직 진행 중이면 다시 상태를 보낸 참가자가 복귀할 수 있습니다.
- 전원 종료: 모든 참가자가 종료 상태이면 최신 저장 상태를 다시 읽고 방을 종료합니다. 이미 끝난 방이 복귀 요청만으로 진행 상태가 되는 것은 아닙니다.
화면 잠금과 모바일 백그라운드 제한도 상태 전송을 늦출 수 있습니다. 끊김을 조사할 때는 기기·브라우저, 잠금 여부, 대기실인지 게임 중인지가 재현에 필요한 정보입니다.
단위 테스트와 실제 대결은 확인 범위가 다릅니다
tests/room.test.cjs는 저장소를 모사한 환경에서 동시 닉네임 참가, 정원 제한, 시작 직후 참가 같은 조건을 검사합니다. tests/battle-update.test.cjs는 겹치는 갱신과 게임 종료 직전 전송, 이전 방 응답을 검사합니다. 이는 외부 저장소와 실제 6개 브라우저를 연결한 전체 대결 테스트와 구별해야 합니다.
직접 재현할 때는 서로 다른 브라우저 두 개에서 방을 만들고 참가한 뒤, 시작 순서·미니보드 갱신·한쪽 종료·최종 순위·리매치를 차례대로 확인할 수 있습니다. 공격 문제라면 송신 콤보와 수신 체리 수, 관찰 시각을 함께 기록하세요. 이 절차는 재현 방법이며 완료된 실험 결과를 뜻하지 않습니다.