오늘의 TIL (Today I Learned) - 마피아 게임 WebSocket 서버 개발
오늘 작업한 내용
오늘은 마피아 게임의 밤(NIGHT) 페이즈 로직을 집중적으로 개발하면서, 게임 진행에 필요한 핵심 기능들을 추가하고 개선했다.
1. 특정 역할(role)을 가진 살아있는 플레이어 찾기
getPlayerByRole(roomId: string, role: string)
: 특정 역할(예: 경찰, 의사, 마피아)을 가진 살아있는 플레이어의 ID를 반환하는 메서드를 구현했다.
- Redis에 저장된 현재 게임 데이터에서
players
배열을 조회하여, 해당 역할을 가진 살아있는 플레이어를 필터링하여 ID를 반환하도록 설계했다.
- 만약 해당 역할의 플레이어가 없다면
null
을 반환한다.
2. 밤(NIGHT) 페이즈 시작 처리
startNightPhase(roomId: string)
:
- 게임의 phase를
night
으로 설정하고, 밤 횟수(nightNumber)를 증가시킴.
- 현재 게임에 남아있는 마피아 목록과 사망자 목록을 조회하여, 클라이언트에
밤 시작 이벤트
를 전송함.
- 게임 상태 변경을 위해 Redis에 데이터를 저장하고, 로그를 남겼다.
3. 경찰 조사 결과 처리
getPoliceResult(roomId: string)
:
- 현재 살아있는 경찰이 조사한 대상의 역할(시민 또는 마피아)을 반환하는 기능을 구현했다.
- 조사 대상이 없으면 경찰 ID만 반환하고, 조사한 대상이 있다면 마피아 여부를 판별하여 역할 정보를 제공한다.
- 결과는 JSON 형식으로
{ policeId, targetUserId, role }
형태로 반환됨.
4. 플레이어 사망 처리
markPlayerAsDead(roomId: string, playerId: number)
:
- 특정 플레이어를 사망 처리(Alive → Dead) 하는 기능을 구현했다.
- 해당 플레이어의
isAlive
값을 false
로 변경하고, Redis에 반영함.
5. 밤 횟수(night count) 관리
getNightCount(roomId: string)
:
- 밤이 진행될 때마다
nightNumber
를 증가시키는 기능을 추가했다.
- Redis에
nightNumber
값을 저장하여 밤 횟수를 지속적으로 관리할 수 있도록 설계했다.
6. 의사 보호 기능
setPlayerAlive(roomId: string, playerId: number)
:
- 특정 플레이어를 살아있는 상태로 복구하는 기능을 추가했다.
- 의사가 특정 플레이어를 보호하면, 해당 플레이어는 사망하지 않도록 처리함.
- Redis의
players
데이터를 업데이트하여 상태를 반영함.
7. 밤 행동 완료 여부 저장
setNightActionComplete(roomId: string, role: 'mafia' | 'police' | 'doctor')
:
- 밤 동안 마피아, 경찰, 의사가 각자의 행동을 완료했는지 여부를 Redis에 저장하도록 구현했다.
- 예를 들어, 마피아가 타겟을 선택했거나 경찰이 조사를 완료했다면,
nightAction:mafia = true
와 같은 데이터를 저장함.
8. 모든 밤 행동 완료 여부 체크
checkAllNightActionsCompleted(roomId: string)
:
- 마피아, 경찰, 의사가 모든 밤 행동을 완료했는지 확인하는 기능을 구현했다.
- 살아있는 플레이어만 체크하며, 죽은 플레이어는 자동으로 완료된 것으로 간주함.
- 모든 역할이 행동을 완료하면
true
, 아직 완료되지 않았다면 false
를 반환함.
9. 게임 종료 조건 체크
checkEndGame(roomId: string)
:
- 게임이 종료될 조건을 검사하는 기능을 구현했다.
- 남아있는 마피아 수와 시민 수를 비교하여 승리 조건을 판단한다.
- 마피아 ≥ 시민이면 마피아 승리
- 마피아가 0명이면 시민 승리
- 게임이 종료될 경우
{ isGameOver: true, winningTeam: 'mafia' | 'citizens' }
형식으로 결과를 반환함.
10. 밤 결과 처리 (마피아, 경찰, 의사 능력 반영)
processNightResult(roomId: string)
:
- 밤이 끝났을 때, 마피아의 공격, 의사의 보호, 경찰의 조사 결과를 반영하는 기능을 구현했다.
- 마피아가 지목한 플레이어 중 랜덤으로 한 명을 선택하여 사망 처리하지만, 의사가 보호한 경우 생존함.
- 경찰 조사 결과도 반환하여 경찰이 마피아를 정확히 조사했는지 여부를 전달함.
- 최종 결과는
{ killedUserId, details, policeResult }
형태로 반환됨.
11. 밤이 끝나고 낮(DAY) 페이즈로 전환
triggerNightProcessing(roomId: string)
:
- 모든 밤 행동이 완료되면 자동으로 밤 결과를 처리하고, 게임이 종료되었는지 검사함.
- 게임이 끝나지 않았다면 낮(DAY) 페이즈로 전환되도록 구현함.
- Redis에
phase = 'day'
로 업데이트한 뒤, 낮이 시작되도록 설정함.
12. 현재 게임의 Phase(낮/밤) 가져오기
getGamePhase(roomId: string)
:
- 게임의 현재 상태(낮인지, 밤인지)를 조회하는 기능을 구현함.
- Redis에서
phase
값을 읽어와 현재 진행 중인 페이즈를 반환함.
오늘의 배운 점 & 개선할 점
배운 점
- Redis를 활용한 게임 상태 관리 방법을 익혔고, WebSocket 기반의 이벤트 처리 구조를 정리할 수 있었다.
- 마피아, 경찰, 의사의 행동을 비동기적으로 처리하면서, 게임 로직을 좀 더 깔끔하게 정리할 수 있었다.
- 게임 종료 로직을 명확하게 정의하여, 게임이 자연스럽게 종료될 수 있도록 개선했다.
개선할 점
- 밤 페이즈에서 마피아의 투표 방식(랜덤 vs 다수결 반영)을 좀 더 정교하게 만들 필요가 있음.
- 낮(DAY) 페이즈에서 플레이어들이 토론하고 투표하는 기능을 추가해야 함.
- 클라이언트 측에서 WebSocket을 통해 밤이 끝난 후 결과를 적절히 UI에 표시하는 부분을 추가해야 함.
완성도를 높여서 더욱 재밌는 마피아 게임을 만들어보자!