페이지를 불러오는 중

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

영상

1차 UCC 썸네일. "내게 웃음은 사치일 뿐! 역삼동 포커페이스 달인" 문구가 적힌 홍보 영상 표지.
UCC 1차 · 홍보 영상 ↗
2차 UCC 썸네일. 파란 배경에 데구르르 로고만 놓여 있다.
UCC 2차 · 시연 영상 ↗

화면

데구르르 서비스 아키텍처 다이어그램. 사용자에서 Nginx와 Next를 거쳐 Spring 메인 서버로, 다시 Redis, MySQL, OpenVidu, FastAPI 모델 서버로 연결된다.
서비스 아키텍처. 오른쪽 아래 FastAPI가 얼굴 인식과 표정 분석을 담당한 별도 서버
게임 진행 흐름도. 공격과 수비 표시에서 시작해 웃음 게이지와 얼굴 인식 여부로 갈라진다.
공격과 수비를 번갈아 두고, 웃음 게이지와 얼굴 인식 결과로 판정이 분기
데구르르 로그인 화면. 파란 배경에 로고와 카카오 로그인 버튼 하나만 놓여 있다.
카카오 로그인 하나로 가입 절차 없이 진입
데구르르 로비 화면. 방 만들기, 방 찾기, 랜덤 매칭 세 개의 버튼이 있다.
방 코드 입장과 ELO 기반 랜덤 매칭을 함께 배치
랜덤 매칭 대기 화면. 빵 캐릭터가 구르는 아래에 경과 시간과 "게임을 찾는 중입니다" 문구가 있다.
랜덤 매칭 대기 중 경과 시간을 표시해 연결 과정을 드러냄
매칭 성사 화면. 두 참가자의 프로필과 닉네임이 VS 표시를 사이에 두고 위아래로 놓여 있다.
매칭이 성사되면 양쪽의 닉네임과 상태 메시지를 맞붙여 표시
1라운드 화면. 위쪽 방어 참가자와 아래쪽 공격 참가자의 카메라 사이에 웃음 확률 27% 게이지와 남은 시간 0:23이 표시된다. 얼굴은 모자이크 처리.
1라운드. 방패가 방어, 검이 공격. 가운데 게이지가 현재 웃음 확률
2라운드 화면. 공수가 바뀌어 위쪽이 공격, 아래쪽 방어 참가자가 입을 가리고 웃음을 참고 있다. 게이지 21%, 남은 시간 0:30. 얼굴은 모자이크 처리.
2라운드는 공수 교대. 방어 쪽이 웃음을 참는 동안 게이지가 움직임
대결 결과 화면. LOSS 표시 아래 레이팅 1443과 17점 하락이 적혀 있고, 티어 진행 막대와 상대 정보가 있다.
대결 직후 레이팅 증감과 티어 진행도를 함께 노출
시즌 랭킹 목록. 순위별로 메달, 프로필, 닉네임, 레이팅이 나열되어 있다.
시즌별 랭킹으로 상위권을 확인
브론즈·실버·골드 티어 아이콘 세 개. 원형 배지 안에 왕관이 그려져 있다.
레이팅 구간을 브론즈·실버·골드 세 티어로 나눠 표시
배포 후 사용자 추이 그래프. 총 사용자 410명, 새 사용자 409명, 이벤트 1만 건으로 표시되어 있다.
1차 배포 후 5일간 사용자 410명, 이벤트 1만 건

01 표정 판정을 실시간 대결에 연결 2023.07 — 2023.08

얼굴 인식과 표정 분석 모델은 Python, 게임 로직과 세션 관리는 Java 메인 서버에 위치. 두 서버 사이로 영상 프레임이 지연 없이 오가야 대결이 성립하는 구조.

요구사항

모델 서빙
얼굴 인식과 표정 분석 결과를 실시간으로 제공
서버 간 통신
Java 메인 서버와 Python 모델 서버 사이 이미지 전달
판정 지연
판정이 대결 진행을 끊지 않을 것

구현

  1. 모델 서버 구성

    • 실시간 판정에 맞는 경량 구성으로, Haar cascade로 얼굴을 찾고 잘라낸 64×64 흑백 영역을 사전학습된 7종 표정 분류 모델(FER 계열, Keras)에 넣어 확률을 얻는 FastAPI 서버 구성
    • 프레임마다 최상위 표정과 웃음 확률을 반환하고, 얼굴이 검출되지 않으면 별도 상태로 돌려 진행 로직이 구분하도록 처리
    • 카메라 프레임을 주기적으로 올려 판정을 바로 확인하는 HTTP 업로드 경로와 테스트 화면 구성
  2. 실시간 통신

    • Python 서버가 STOMP over WebSocket 클라이언트로 Java 메인 서버에 접속해 프레임을 구독하고, 판정 결과를 같은 연결로 되돌려 보내는 구조
    • 모델 서버는 들어오는 연결을 열지 않고 메인 서버 하나만 외부에 노출
    • 라운드 종료 판정은 메인 서버가 맡아 최상위 표정이 웃음이고 확률이 0.5 이상인 프레임에서 즉시 종료. 화면에는 현재 웃음 확률을 게이지로 표시
    • STOMP로 대결 진행 상황을 양쪽에 실시간 전달
  3. 추론 부하 분리 설계 판단

    • 모델 추론이 메인 서버의 게임 로직과 자원을 공유하면 판정이 지연되는 문제 확인
    • Python 서버를 별도 클라우드 서버로 분리 배포해 추론 부하 격리
    • Jenkins 빌드를 위한 Docker 이미지 구성

까다로웠던 지점

판정 지연이 곧 게임 붕괴

웃음 판정이 밀리면 이미 웃은 뒤에 결과가 나와 대결의 긴장이 사라지는 구조. 한 서버에서 게임 로직과 모델 추론을 함께 처리하는 한 부하가 겹치는 순간 지연이 발생.

해결모델 서버를 물리적으로 분리해 자원 경합 제거. 게임 로직은 메인 서버, 추론은 전용 서버로 책임 분리

남긴 것

  • 표정 판정이 대결 흐름을 끊지 않는 수준까지 확보
  • 패배자의 웃는 순간을 캡처해 남기는 하이라이트 기능까지 확장

02 대결을 서비스로 묶기 2023.08 — 2023.11

판정이 동작해도 그것만으로는 게임이 아님. 상대를 만나고, 결과가 기록으로 남고, 다시 들어올 이유가 생겨야 서비스로 성립.

요구사항

상대 연결
지인과 붙는 경로와 모르는 상대를 만나는 경로를 모두 제공
실력 균형
비슷한 수준끼리 매칭되어 대결이 일방적으로 끝나지 않을 것
재방문 동기
한 판으로 끝나지 않고 다시 들어올 이유를 남길 것
진입 장벽
가입 절차가 게임 시작을 가로막지 않을 것

구현

  1. 두 갈래 입장 경로

    • 방 코드를 입력해 지인과 만나는 경로 구현
    • ELO 기반 랜덤 매칭으로 모르는 상대와 연결되는 경로 구현
    • 대기와 매칭 상태를 실시간으로 표시해 연결 과정을 드러냄
  2. 대결 화면 구현

    • 게임 대기·매칭·플레이 화면의 UI 설계와 구현
    • 하이라이트 캡처의 업로드 경로와 튜토리얼 화면 구현
  3. 레이팅과 티어

    • 대결 결과에 따라 레이팅이 오르내리는 ELO 방식 채택
    • 레이팅 구간을 티어로 묶어 실력대를 시각적으로 구분
    • 시즌별 랭킹 조회를 두어 상위권을 확인 가능하도록 구성
  4. 결과를 남기는 장치 설계 판단

    • 패배자의 웃는 순간을 이미지로 포착해 결과 화면에 노출
    • 점수만 남기면 한 판으로 끝나지만 장면이 남으면 공유와 재대결로 이어진다는 판단
    • 마이페이지에 최근 전적과 하이라이트를 모아 다시 볼 수 있게 구성
  5. 가입 마찰 축소

    • 카카오 로그인을 도입해 별도 가입 절차 없이 진입 가능하도록 처리

까다로웠던 지점

초기 사용자가 없으면 매칭도 없음

실력 기반 매칭은 대기열에 사람이 충분할 때 동작하는 구조. 배포 초기에는 동시 접속자가 적어 비슷한 레이팅의 상대를 찾지 못하고 대기만 길어지는 상황.

해결방 코드 입장을 랜덤 매칭과 동등한 위치에 배치. 아는 사람끼리 먼저 플레이해 대기열을 채우는 경로를 열어 초기 공백을 우회

남긴 것

  • 1차 배포 후 5일간 사용자 410명, 이벤트 약 1만 건 기록
  • 대결 결과가 레이팅·티어·하이라이트로 남아 재대결 동기를 형성
  • 2차 차수에서 이 구조 위에 통계와 랭킹 화면을 확장

이미지를 불러오지 못했습니다.