League Of Algo Logic
알고리즘 문제 해결과 경쟁 게임 요소를 결합한 멀티플레이어 코딩 테스트 게이미피케이션 플랫폼
Problem
"League Of Algo Logic(LoL)"은 알고리즘 문제 해결과 경쟁 게임 요소를 결합한 멀티플레이어 기반 코딩 테스트 게이미피케이션 플랫폼입니다. 카카오 OAuth 소셜 로그인, 인게임(밴/픽/구매/플레이) 단계 제공, 티어 제도 기반 경쟁 게이미피케이션 경험, 사용자별 맞춤 통계를 통한 실력 분석 데이터를 제공합니다. SSAFY 공통프로젝트 우수상 수상.
Stack
- Vue.js
- 프론트엔드 SPA 구현
- Spring Boot
- STOMP WebSocket 기반 실시간 게임 서버
- Redis
- 인게임 상태 관리 + Lua 스크립트 원자적 트랜잭션 + 분산 락
- MySQL
- 게임 결과, 사용자 통계, 티어 데이터 영속 저장
- Docker
- 컨테이너화 배포
- Jenkins
- CI/CD 파이프라인
- AWS
- EC2 호스팅 + SQS/Lambda/S3 기반 채점 인프라
- Amazon SQS
- 채점 요청 메시지 큐
- AWS Lambda
- 서버리스 코드 채점 실행
- Amazon S3
- 채점 테스트케이스 저장
Key Contributions
STOMP WebSocket 기반 실시간 통신 로직 구현
게임 로비, 밴/픽, 플레이 전 단계에 걸친 실시간 양방향 통신을 STOMP WebSocket으로 설계·구현했습니다. 브로드캐스트(/topic)와 개인 큐(/user/queue)를 분리하여 이벤트 전달 효율을 확보했습니다.
시간 동기화 통신 76% 절감 (1,890회 → 450회/세션)
인게임 상태 관리 로직 구현
Redis Hash + Lua 스크립트로 방 입장, 밴/픽 선택, 플레이어 상태 변경 등 모든 인게임 상태 변경을 원자적으로 처리하는 구조를 설계했습니다. SETNX 기반 분산 락으로 게임 종료 중복 처리를 차단했습니다.
Lua 스크립트 p95 응답 1ms 이내, 네트워크 왕복 2~3회 → 1회
프로젝트 리딩 및 기술 공유 체계 확립
주간 TIL 기술 공유 회의를 도입하여 팀원 간 프로젝트 관련 기술 지식을 체계적으로 공유했습니다. Jira 및 데일리 스크럼을 주도하여 6인 팀의 프로젝트 일정을 조율했습니다.
SSAFY 공통프로젝트 우수상 수상
Troubleshooting
서버 시간 동기화 문제
S밴픽 단계에서 각 플레이어에게 제한 시간이 표시될 때, 클라이언트가 자신의 로컬 타이머를 기준으로 남은 시간을 계산하는 과정에서 플레이어마다 카운트다운 표시가 달라지는 시간 오차가 확인되었습니다. 서버에서는 이미 다음 단계로 상태가 전이되었음에도 클라이언트에서는 아직 이전 단계의 시각으로 존재하여 다음 단계에서의 요청 제출이 실패하는 문제가 발생했습니다. 또한 서버 측에서도 각 단계의 남은 시간과 서버 시각을 각각 다른 시점의 Instant.now()로 계산할 경우, 두 값 사이에 수십 ms의 내부 오차가 발생하여 클라이언트가 두 값으로 역산한 데드라인이 서로 일치하지 않는 문제가 있었습니다.
A서버 타임스탬프를 기준으로 남은 시간과 서버 시각을 동시에 계산하도록 설계해 내부 시간 오차를 제거하고, 게임 스테이지별 적응형 동기화 주기를 도입해 불필요한 동기화 트래픽을 제거했습니다. TimeSyncScheduler를 구현하여 서버 시각을 주기적으로 각 클라이언트에 개별 전송함으로써 클라이언트가 서버 기준 시각으로 카운트다운을 보정하도록 했습니다.
R모든 클라이언트가 서버 기준 시각으로 카운트다운을 표시하게 되어 플레이어 간 타이머 불일치가 해소되었고, 스테이지 전이 시점과 클라이언트 표시를 일관되게 유지할 수 있었습니다.
인게임 상태 관리 동시성 문제
S방 입장 정원 초과: 정원 6명인 방에 마지막 1자리가 남은 상태에서 2명이 동시에 입장 요청을 보낼 경우, Redis HGETALL → HSET 사이에 경쟁 상태가 발생하여 정원을 초과한 7명이 입장하는 문제가 발생했습니다. 게임 종료 중복 처리: GameStageScheduler가 1초 주기로 폴링하여 finishGame()을 호출하는데, 이전 실행이 종료 처리를 완료하기 전에 다음 실행이 같은 게임의 데드라인 도달을 다시 감지하여 DB에 게임 결과가 이중 저장되고, GAME_FINISHED 이벤트가 2회 전송되는 문제가 발생했습니다. 플레이어 상태 변경 충돌: Redis Hash에 JSON으로 저장된 플레이어 상태를 HGET → 수정 → HSET하는 과정에서 동시 요청이 먼저 쓴 값을 덮어쓰는 갱신 손실 문제가 발생했습니다.
ARedis Lua 스크립트를 도입하여 읽기→판단→쓰기 과정을 원자적 트랜잭션으로 처리했습니다. 방 입장: Lua 스크립트에서 플레이어 확인 → 인원 카운트 → 정원 비교 → 조건부 추가를 단일 EVAL로 처리하여 경쟁 상태를 원천 차단했습니다. 게임 종료: Redis SETNX 기반 분산 락을 획득한 후에만 종료 처리를 진행하고, Lua 스크립트로 소유권 검증 후 락을 삭제하도록 처리했습니다. 상태 변경: Lua 스크립트에서 HGET → JSON 파싱 → 필드 수정 → HSET을 원자적으로 처리하며, KEEPTTL 옵션으로 기존 TTL을 보존하도록 처리했습니다.
R동시 20건 입장 요청 테스트에서 정원 초과 0건. 분산 락으로 게임 종료 중복 처리 원천 차단, 결과 정확히 1회 저장. Lua 스크립트 p95 응답 1ms 이내, Java ↔ Redis 네트워크 왕복 2~3회 → 1회 감소.
실시간 이벤트 중복 전송 및 불필요한 네트워크 부하 문제
S메시지 중복: 네트워크 지연으로 채팅 전송 버튼 응답이 늦어질 경우, 동일 메시지가 연속 클릭으로 전체 구독자에게 여러 번 브로드캐스트되는 문제가 발생했습니다. 과도한 전송: 스크립트를 통해 분당 수백 건의 채팅 전송 시 글로벌 토픽의 모든 구독자에게 그대로 브로드캐스트되어 STOMP 브로커와 클라이언트 렌더링 부하가 동시에 급증했습니다. 불필요한 동기화 트래픽: 시간 동기화 이벤트를 전체 접속자에게 동일 주기로 전송하여, 인게임이 아닌 유저에게도 불필요한 트래픽이 발생했습니다.