Folio 04 · 삼성 청년 SW 아카데미
데구르르
1:1 웃음 참기 화상 대결
- 기간
- 2023.07.10 — 2023.11.17
- 역할
- 모델 서버 · 실시간 통신 · 프론트엔드 · 인프라
- 배포 후 5일
- 사용자 410명
코로나 거리두기로 웃을 일이 줄어든 시기에 웃음을 되찾자는 취지로 시작한 게임. 웃음을 참아야 하는 자리에서는 웃기지 않아도 웃음이 새어 나온다는 역설을 규칙으로 옮겨, 공격과 방어를 번갈아 두고 상대를 웃기는 1:1 화상 대결로 구성. 카메라로 들어오는 얼굴에서 표정을 읽어 웃음 여부를 판정하고, 그 판정이 대결 도중 끊기지 않아야 하는 조건.
Python · FastAPI · OpenCV · Keras · Java · STOMP · WebSocket · React · OpenVidu · Docker · Jenkins
영상
화면
01 표정 판정을 실시간 대결에 연결 2023.07 — 2023.08
얼굴 인식과 표정 분석 모델은 Python, 게임 로직과 세션 관리는 Java 메인 서버에 위치. 두 서버 사이로 영상 프레임이 지연 없이 오가야 대결이 성립하는 구조.
요구사항
- 모델 서빙
- 얼굴 인식과 표정 분석 결과를 실시간으로 제공
- 서버 간 통신
- Java 메인 서버와 Python 모델 서버 사이 이미지 전달
- 판정 지연
- 판정이 대결 진행을 끊지 않을 것
구현
-
모델 서버 구성
- 실시간 판정에 맞는 경량 구성으로, Haar cascade로 얼굴을 찾고 잘라낸 64×64 흑백 영역을 사전학습된 7종 표정 분류 모델(FER 계열, Keras)에 넣어 확률을 얻는 FastAPI 서버 구성
- 프레임마다 최상위 표정과 웃음 확률을 반환하고, 얼굴이 검출되지 않으면 별도 상태로 돌려 진행 로직이 구분하도록 처리
- 카메라 프레임을 주기적으로 올려 판정을 바로 확인하는 HTTP 업로드 경로와 테스트 화면 구성
-
실시간 통신
- Python 서버가 STOMP over WebSocket 클라이언트로 Java 메인 서버에 접속해 프레임을 구독하고, 판정 결과를 같은 연결로 되돌려 보내는 구조
- 모델 서버는 들어오는 연결을 열지 않고 메인 서버 하나만 외부에 노출
- 라운드 종료 판정은 메인 서버가 맡아 최상위 표정이 웃음이고 확률이 0.5 이상인 프레임에서 즉시 종료. 화면에는 현재 웃음 확률을 게이지로 표시
- STOMP로 대결 진행 상황을 양쪽에 실시간 전달
-
추론 부하 분리 설계 판단
- 모델 추론이 메인 서버의 게임 로직과 자원을 공유하면 판정이 지연되는 문제 확인
- Python 서버를 별도 클라우드 서버로 분리 배포해 추론 부하 격리
- Jenkins 빌드를 위한 Docker 이미지 구성
까다로웠던 지점
판정 지연이 곧 게임 붕괴
웃음 판정이 밀리면 이미 웃은 뒤에 결과가 나와 대결의 긴장이 사라지는 구조. 한 서버에서 게임 로직과 모델 추론을 함께 처리하는 한 부하가 겹치는 순간 지연이 발생.
해결모델 서버를 물리적으로 분리해 자원 경합 제거. 게임 로직은 메인 서버, 추론은 전용 서버로 책임 분리
남긴 것
- 표정 판정이 대결 흐름을 끊지 않는 수준까지 확보
- 패배자의 웃는 순간을 캡처해 남기는 하이라이트 기능까지 확장
02 대결을 서비스로 묶기 2023.08 — 2023.11
판정이 동작해도 그것만으로는 게임이 아님. 상대를 만나고, 결과가 기록으로 남고, 다시 들어올 이유가 생겨야 서비스로 성립.
요구사항
- 상대 연결
- 지인과 붙는 경로와 모르는 상대를 만나는 경로를 모두 제공
- 실력 균형
- 비슷한 수준끼리 매칭되어 대결이 일방적으로 끝나지 않을 것
- 재방문 동기
- 한 판으로 끝나지 않고 다시 들어올 이유를 남길 것
- 진입 장벽
- 가입 절차가 게임 시작을 가로막지 않을 것
구현
-
두 갈래 입장 경로
- 방 코드를 입력해 지인과 만나는 경로 구현
- ELO 기반 랜덤 매칭으로 모르는 상대와 연결되는 경로 구현
- 대기와 매칭 상태를 실시간으로 표시해 연결 과정을 드러냄
-
대결 화면 구현
- 게임 대기·매칭·플레이 화면의 UI 설계와 구현
- 하이라이트 캡처의 업로드 경로와 튜토리얼 화면 구현
-
레이팅과 티어
- 대결 결과에 따라 레이팅이 오르내리는 ELO 방식 채택
- 레이팅 구간을 티어로 묶어 실력대를 시각적으로 구분
- 시즌별 랭킹 조회를 두어 상위권을 확인 가능하도록 구성
-
결과를 남기는 장치 설계 판단
- 패배자의 웃는 순간을 이미지로 포착해 결과 화면에 노출
- 점수만 남기면 한 판으로 끝나지만 장면이 남으면 공유와 재대결로 이어진다는 판단
- 마이페이지에 최근 전적과 하이라이트를 모아 다시 볼 수 있게 구성
-
가입 마찰 축소
- 카카오 로그인을 도입해 별도 가입 절차 없이 진입 가능하도록 처리
까다로웠던 지점
초기 사용자가 없으면 매칭도 없음
실력 기반 매칭은 대기열에 사람이 충분할 때 동작하는 구조. 배포 초기에는 동시 접속자가 적어 비슷한 레이팅의 상대를 찾지 못하고 대기만 길어지는 상황.
해결방 코드 입장을 랜덤 매칭과 동등한 위치에 배치. 아는 사람끼리 먼저 플레이해 대기열을 채우는 경로를 열어 초기 공백을 우회
남긴 것
- 1차 배포 후 5일간 사용자 410명, 이벤트 약 1만 건 기록
- 대결 결과가 레이팅·티어·하이라이트로 남아 재대결 동기를 형성
- 2차 차수에서 이 구조 위에 통계와 랭킹 화면을 확장