실시간 멀티플레이 퀴즈 게임 웹 서비스를 개발하며, MVP 단계에 적합한 시스템 설계 과정을 기록합니다.
대규모 인프라 구축보다는 단일 서버에서의 효율성과 빠른 구현에 초점을 맞췄습니다.
요구사항을 구체화 해보자.
실시간 게임은 서버 -> 클라이언트 데이터 전송이 필요하다.
기본 HTTP는 클라이언트의 요청으로 데이터 송수신 과정이 일어나므로 적절하지 않다.
HTTP 폴링
폴링 방식은 클라이언트가 서버에게 주기적으로 새로운 데이터가 있는지 물어보는 방법이다. 폴링은 자주할 수록 비용이 커지므로 실시간 게임에는 적절하지 않다.
HTTP 롱 폴링
롱 폴링의 경우 새 메시지가 반환되거나 타임아웃 될 때까지 연결을 유지한다.
"요청 → 대기 → 응답 → 종료" 의 반복이다. 각 요청은 독립적이다.
서버가 상태를 가지게 되어 로드밸런싱 전략에 따라 이벤트를 발생한 서버와 클라이언트가 대기중인 서버가 동일하지 않을 수 있어 다중화에 불리하다. 또한 오버헤드가 여전히 크다.
SSE(Server-Sent Events)
단방향(서버 -> 클라이언트) 실시간 전송에 특화된 HTTP 기반 스트리밍 방식이다. 양방향 통신이 필요한 게임 설계에 적절하지 않다.
WebSocket
웹소켓은 서버와 클라이언트가 양방향 데이터 전송을 하는데 가장 널리 사용되는 기술이다. 데이터 전송을 하거나 수신할 때 동일한 프로토콜을 사용할 수 있어 단순하고 직관적이다.
실시간 통신을 위해 WebSocket 을 사용하기로 했다.
물론 실시간 통신이 아닌 로그인, 기록 조회 등 다른 기능은 HTTP 를 사용한다.
추가로, 웹소켓에는 STOMP 라는 상위 프로토콜이 있고, 스프링에도 구현되어있다.
순수 WebSocket 과 비교하면 약간의 오버헤드가 있지만, 다음과 같은 장점들이 있다.
결론: WebSocket + STOMP 선택.
회원 인증 방식에는 대표적으로 다음 두개가 있다.
1. Session 방식
2. Token 방식 (JWT)
예전에 작성한 웹과 모바일 앱에 따라 다른 Token vs Session 방식 선택 기준 글 내용에 따라 웹에서 구현이 간단한 세션 방식을 선택했다.
게임 진행 상태를 저장할수 있는 것들은 다음과 같다.
디스크 데이터베이스
게임 진행 상태는 영구적으로 저장할 필요가 없는 정보이므로 적절하지 않다.
Java 객체
POJO로 순수 자바 컬렉션으로 상태를 관리하는 방식이다.
데이터베이스가 아니라서 확장 불가능하고 서버 종속 상태이지만, 가장 간단히 구현할 수 있다.
인메모리 데이터베이스
Redis, Memcached 와 같은 인메모리 데이터베이스이다.
메모리에 저장하므로 빠른 속도와 ttl 설정으로 수명을 정할 수 있어 영구적으로 저장할 필요가 없는 데이터를 다루는데 최적이다. 또한 WAS와 상태를 분리할 수 있어 서버 다중화에 유리하다.
빠른 MVP 개발을 위해 데이터베이스를 사용하지 않고 Java 객체로 구현하기로 했다.
Repository 인터페이스만 잘 정의하면 나중에 Redis 전환이 매끄러우므로 추후 확장할 예정이다.
단일 서버로 10,000명의 접속자를 수용할 수 있는지 검토하기 위해 메모리 사용량을 계산했다.
