카테고리 없음

내일배움캠프 16주차 수요일 TIL

news0516 2025. 2. 12. 20:19

웹소켓 처리를 위한 기본 개념 다시 정리

NestJS
서버 애플리케이션 프레임워크
클라이언트의 http 요청이나 웹소켓 이벤트를 처리하고, 비즈니스 로직을 실행하며, 여러 모듈과 서비스를 체계적으로 구성한 상태에서 db와 연결해 백엔드를 구성

Socket.io
클라이언트와 서버  간 실시간, 양방향 통신을 가능하게 한다.

Redis
레디스는 인메모리 데이터 저장소로, 매우 빠른 속도로 데이터를 읽고 쓸 수 있다.
(예: 방 정보, 플레이어 상태, 투표 기록, 역할 할당 등)
게임 진행 상황을 빠르게 업데이트하고, 여러 클라이언트에 최신 상태로 제공하는데 유용

Docker
도커는 애플리케이션을 컨테이너라는 독립된 환경에서 실행할 수 있게 해주는 도구로, 이 프로젝트에서는 redis 서버를 손 쉽게 실행하기 위해 사용. 시스템에 별도로 레디스를 설치하지 않아도, 컨테이너 안에서 레디스를 실행ㅇ시켜 개발 및 테스트 가능

서버 구조 도식화(임시)

 

  • 클라이언트:
    • 웹 브라우저에서 HTTP API 요청은 API 서버로, 실시간 게임 이벤트 처리는 웹소켓 연결을 통해 게임 서버로 전달
  • EC2 인스턴스 내 서비스:
    • API 서버 (NestJS):
      • EC2 인스턴스에 직접 배포되어 HTTP API 요청(인증, 사용자 관리 등)을 처리하며, RDS 데이터베이스와 연동하여 영속 데이터를 관리
    • 게임 서버 (도커 컨테이너):
      • 도커 컨테이너로 운영되며, 실시간 게임 로직과 이벤트 처리를 담당
      • Redis 서버를 구독하여 API 서버에서 발행하는 게임 시작 이벤트 등의 메시지를 받아 처리
  • RDS 데이터베이스:
    • API 서버가 영속 데이터를 저장하고 관리하기 위해 사용하는 관계형 데이터베이스
  • Redis 서버:
    • API 서버가 게임 시작 이벤트와 같은 메시지를 발행하며, 게임 서버는 Redis를 구독하여 실시간 메시지를 전달



게임서버 관련 웹소켓 명세서(임시)
마피아 게임의 로직을 대략적으로 정리한 후 필요한 기능들을 정리하였다.

 

Event Name Direction Payload Example Description / Notes
CONNECT Client → Server json {
"token": "사용자 인증 토큰", "userId": "사용자 아이디"
}
최초 연결 및 사용자 인증 요청.
AUTH_SUCCESS Server → Client json {
"userId": "사용자 아이디",
"message": "인증 성공",
"roomInfo": null
}
인증 성공 시 응답. (옵션: 현재 참여 중인 룸 정보 포함)
AUTH_FAILURE Server → Client json {
"errorCode": "AUTH_001",
"message": "인증 실패. 토큰이 유효하지 않습니다."
}
인증 실패 시 응답.
ROOM:JOIN Client → Server json {
"roomId": "방 아이디",
"userId": "사용자 아이디"
}
게임룸 입장 요청. 서버는 Redis의 룸 상태를 업데이트.
ROOM:LEAVE Client → Server json {
"roomId": "방 아이디",
"userId": "사용자 아이디"
}
게임룸 퇴장 요청. 서버는 참가자 목록을 갱신 후 브로드캐스트.
ROOM:UPDATE Server → Clients (Broadcast) json {
"roomId": "방 아이디",
"players": [ {
"userId": "user1",
"ready": false,
"alive": true},
{ "userId": "user2",
"ready": true,
"alive": true
} ],
"gamePhase": "waiting"
}
게임룸 상태(참가자, 준비 상태, 단계 등) 변경 시 전체 클라이언트에 전송.
ROOM:MAX_PLAYERS_REACHED Server → Clients json {
"roomId": "방 아이디",
"message": "모든 플레이어가 입장했습니다. 10초 후 자동 게임 시작됩니다."
}
설정된 최대 인원(8명) 도달 시 알림.
ROOM:COUNTDOWN_START Server → Clients json {
"roomId": "방 아이디",
"countdown": 10,
"message": "게임 시작까지 10초 남았습니다."
}
최대 인원 도달 후 10초 카운트다운 시작 알림.
TIMER:UPDATE Server → Clients json {
"roomId": "방 아이디",
"phase": "countdown",
"remainingTime": 7,
"message": "게임 시작까지 7초 남았습니다."
}
카운트다운 진행 중 남은 시간 업데이트 (주기적으로 전송).
ROOM:GAME_START Server → Clients json {
"roomId": "방 아이디",
"phase": "role_assignment", "startTime": "ISO8601 타임스탬프",
"message": "자동 게임 시작합니다. 역할을 분배합니다."
}
카운트다운 종료 후 자동 게임 시작. 역할 분배 단계로 전환됨.
ROLE:ASSIGN Server → 특정 클라이언트 (개별) json {
"roomId": "방 아이디",
"userId": "수신자 아이디",
"role": "mafia
}
citizen
ROOM:DAY_START Server → Clients json {
"roomId": "방 아이디",
"phase": "day",
"dayNumber": 1,
"startTime": "ISO8601 타임스탬프",
"message": "DAY1 시작: 토의 및 투표를 진행하세요."
}
역할 분배 후 첫 번째 낮(DAY1) 시작 알림.
CHAT:MESSAGE Client → Server / Broadcast json {
"roomId": "방 아이디",
"userId": "사용자 아이디",
"message": "채팅 내용",
"timestamp": "ISO8601 타임스탬프"
}
낮 단계 토의 중 채팅 메시지 전송 (서버는 이를 전체 브로드캐스트).
VOTE:MAFIA Client → Server json {
"roomId": "방 아이디",
"userId": "투표자 아이디",
"voteFor": "투표 대상 사용자 아이디"
}
1차 투표: 마피아 의심 투표 요청.
VOTE:MAFIA_RESULT Server → Clients json {
"roomId": "방 아이디",
"phase": "day",
"voteType": "mafia",
"result": {
"targetUserId": "최다 득표자 아이디",
"voteCount": 4 },
"message": "1차 투표 결과,
[targetUserId] 에게 가장 많은 표가 모였습니다."
}
1차 투표 결과 전파. 최다 득표자 결정 및 공개.
VOTE:SURVIVAL Client → Server json {
"roomId": "방 아이디",
"userId": "투표자 아이디",
"vote": "yes
no" } ```
VOTE:SURVIVAL_RESULT Server → Clients json {
"roomId": "방 아이디",
"phase": "day",
"voteType": "survival",
"result": {
"targetUserId": "최다 득표자 아이디",
"finalDecision": "dead
alive" }, "message": "[targetUserId] 의 생존 여부가 투표 결과 [finalDecision] 처리되었습니다." } ```
ROOM:NIGHT_START Server → Clients json {
"roomId": "방 아이디",
"phase": "night",
"nightNumber": 1,
"startTime": "ISO8601 타임스탬프",
"message": "NIGHT 단계 시작: 특수 능력을 사용하세요." }
낮 투표 후 밤(NIGHT) 단계 시작 알림.
ACTION:MAFIA_TARGET Client → Server json {
"roomId": "방 아이디",
"userId": "마피아 플레이어 아이디",
"targetUserId": "공격 대상 사용자 아이디"
}
마피아가 공격할 대상을 선택.
ACTION:DOCTOR_PROTECT Client → Server json {
"roomId": "방 아이디",
"userId": "의사 플레이어 아이디",
"targetUserId": "보호 대상 사용자 아이디"
}
의사가 보호할 대상을 선택.
ACTION:POLICE_CHECK Client → Server json {
"roomId": "방 아이디",
"userId": "경찰 플레이어 아이디",
"targetUserId": "조사 대상 사용자 아이디"
}
경찰이 조사할 대상을 선택. (조사 결과는 개별 POLICE:RESULT로 전송)
ROOM:NIGHT_RESULT Server → Clients json {
"roomId": "방 아이디",
"nightNumber": 1,
"result":{
"killedUserId": "사망 처리된 사용자 아이디 또는 null",
"details": "의사 보호로 인해 살해 취소됨 또는 마피아 공격 성공"
},
"message": "밤 동안의 사건 결과: [details]"
}
밤 동안의 특수 능력 결과 처리 후 결과 전파.
POLICE:RESULT Server → 특정 클라이언트 (경찰) json {
"roomId": "방 아이디",
"userId": "경찰 플레이어 아이디",
"targetUserId": "조사 대상 사용자 아이디", |
"role": "mafia
citizen
ROOM:DAY_START (후속) Server → Client json {
"roomId": "방 아이디",
"phase": "day",
"dayNumber": 2,
"startTime": "ISO8601 타임스탬프",
"message": "DAY2 시작: 밤 사건 결과 - [요약]" }
밤 결과 처리 후 다음 날(DAY2) 단계 시작 알림.
GAME:OVER Server → Client json {
"roomId": "방 아이디",
"winningTeam": "citizens
mafia",
"finalState": {
"players": [ {
"userId": "user1",
"role": "citizen",
"alive": true
},
{ "userId": "user3"
, "role": "mafia",
"alive": false
}
] },
"message": "게임 종료: [winningTeam] 승리!"
ERROR Server → Client json { "errorCode": "ERROR_CODE", "message": "오류 상세 설명", "details": {} } 요청 처리 중 발생한 오류 정보를 전송.