← 모든 프로젝트

4명 팀 프로젝트 · 백엔드 · 2026.08 – 2026.10 (10주)

Super-Sub

생활체육 경기 영상으로 실력을 검증하고, 팀에 필요한 용병을 찾아 주는 플랫폼

  • Python
  • FastAPI
  • PostgreSQL
  • Next.js
  • Flutter
  • pgvector
  • ViTPose
  • EXAONE
  • vLLM
  • k3s
  • AWS
  • Cloudflare

데모 — 개인 PC 에서 돌고 있어 PC 가 꺼져 있으면 열리지 않습니다. 「테스트용 로그인」으로 들어갈 수 있습니다.

Super-Sub 첫 화면

어떤 서비스인가

생활체육 팀(풋살·축구 등)은 사람이 모자랄 때 용병을 구하지만, 처음 보는 사람의 실력은 알 길이 없습니다. Super-Sub 는 그 사람이 올린 경기 영상을 근거로 삼습니다.

  • 영상을 올리면 관절(자세)을 뽑아 항목별로 점수를 매기고, 등급과 판정 문장이 담긴 실력 리포트를 만듭니다.
  • 리포트가 쌓인 선수 카드를 보고, 팀장은 포메이션 판의 빈 자리에 들어올 사람을 AI 추천과 지인 찾기로 고릅니다.
  • 팀끼리 경기를 신청하고 수락하는 흐름과 알림까지 한 서비스 안에서 이어집니다.

서비스 구성

4명이 영역을 나눠 맡았습니다 — 박민호(PM · QA), 백성검(웹 · 앱), 정어진(백엔드 · 파이프라인 · 배포), 정상호(AI 분석). 아래 수치는 팀 문서의 2026-09-23 기준입니다.

영상 분석 AI

  • 사람 검출(RT-DETR) → 자세 추정(ViTPose) → 동작 특징 → 규칙(루브릭) 채점으로 이어지는 분석기를 만들었습니다.
  • 등급은 코드가 결정론적으로 매기고, 언어 모델(EXAONE 4.0 1.2B)은 출력 형식이 강제된 채 근거 문장만 씁니다. 그래서 같은 영상을 다섯 번 넣어도 총점 표준편차가 0.00 이었습니다(영상 4편).
  • 분석은 GPU 워커가 큐에서 가져가 처리합니다. 운영용 T4 GPU 서버에 추론 서버(vLLM)를 올렸고, 검증 때 한 편이 S3 입출력까지 약 1분 6초에 끝났습니다.
  • 한계: 채점 기준은 축구 인스텝 슈팅 하나만 쓰고 있고, 임계값은 지도자 검수를 거치지 않은 잠정치입니다.

웹 · 앱

  • 웹(Next.js 16 · React 19) — 분석 리포트(등급 · 레이더 차트), 선수 카드 꾸미기와 공개 카드 링크, 포메이션 판, AI 추천, 팀 매칭(조건 → 후보 → 신청 → 수락). 백엔드 호출은 서버 쪽 중계(BFF)를 거치고 토큰은 httpOnly 쿠키에 둡니다.
  • 앱(Flutter · Riverpod) — 가짜 데이터와 실서버 구현을 한 줄로 갈아 끼우는 구조에 계약 시험을 물렸고, 실서버에 붙였습니다(스토어 배포 전).

백엔드 · 데이터

  • 7개 컨텍스트(사용자 · 카드 · 분석 · 매칭 · 평가 · 과금 · 알림)로 나눈 헥사고날 구조, API 엔드포인트 100개, DB 마이그레이션 48개.

운영 · 협업

  • AWS — API 는 EC2 의 k3s, 영상은 S3, 분석은 필요할 때만 켜는 GPU 서버. 웹은 Vercel, 앞단은 Cloudflare. 이미지가 올라오면 서버가 2분 안에 스스로 갱신하고, 마이그레이션은 파드가 뜰 때 돕니다.
  • GitHub Actions 로 백엔드(실제 DB 포함) · 앱 시험과 이미지 빌드를 돌립니다. 코드에 정의된 시험은 백엔드 1,126 · AI 446 · 웹 1,085 · 앱 419개.
  • 제안서 9장과 부록, 미결 항목 175개(111개 해소), 개발 로그를 문서 사이트로 함께 관리했습니다.

내가 맡은 일 — 백엔드

팀에서 백엔드를 맡았습니다. 팀 저장소에 남긴 커밋 439건 중 175건이 백엔드 코드(fastapi/)이고, 나머지는 대부분 API 계약·요구사항 같은 설계·진행 문서입니다(2026-09-29 기준).

서버의 뼈대와 규칙

  • FastAPI 서버를 바운디드 컨텍스트 × 계층(헥사고날) 구조로 세우고, API 계약 문서와 pytest 를 붙였습니다.
  • PostgreSQL · SQLAlchemy · Alembic 으로 DB 를 붙이고, 스키마 변경을 마이그레이션으로 관리했습니다.

회원과 보안

  • 가입·로그인(bcrypt, 서명된 JWT)과 구글 로그인(ID 토큰 검증부터 계정 생성까지)을 만들었습니다.
  • 인증 사건 로깅, 인증 엔드포인트 요청 제한, 운영 환경에서 API 문서(/docs) 닫기처럼 보안 요구사항을 하나씩 채웠습니다.

영상 분석 파이프라인

  • 분석을 작업 큐로 돌리고, 워커가 죽어도 큐가 막히지 않도록 멈춘 작업을 회수합니다.
  • 영상의 수명 주기 — 업로드(S3 사전 서명 URL), 프로필 보관, DB·S3 연쇄 삭제, 저장 안 한 임시 영상 정리 — 를 끝까지 이었습니다.
  • 워커가 S3 에 남긴 분석 결과를 DB 에 적재하고, 총점·등급·항목별 수치를 읽는 리포트 API 를 냈습니다. 같은 영상의 재업로드도 감지합니다.

팀과 매칭

  • 팀 초대·수락, 팀↔팀 경기 신청·수락과 알림, 매칭 조건과 상대 후보, 빈 자리 추천 후보, 서로 지인 신청을 만들었습니다.

운영

  • /metrics 엔드포인트와 Prometheus · Grafana 관제 구성, k3s 배포 매니페스트, 시연용 자동 수락 봇을 만들었습니다.

맡은 영역 밖에서 한 일

맡은 영역은 백엔드였지만, 기능이 화면 끝까지 이어지도록 다른 사람의 영역도 필요한 만큼 고쳤습니다. 팀 main 에 들어간 것만 적었습니다.

웹 화면 — 백성검 님 영역

  • 분석 리포트 화면에 오버롤 등급과 레이더 차트를 넣고, 분석 진행 표시가 실제 완료 여부를 따라가게, 분석 실패를 「아직 안 끝남」과 구분해 보여 주게 했습니다.
  • 관리자가 영상과 분석 상태를 한눈에 보는 관리자 페이지(/admin/videos)를 새로 만들었습니다.
  • 팀 매칭 · 경기 화면이 다른 페이지로 옮겨도 이어지게 하고, 손으로 「경기 완료」를 누르는 흐름과 시간 겹침 규칙을 시험과 함께 넣었습니다.
  • 초대를 수락해도 판에 안 서던 것, 판에서 뺀 사람이 새로고침하면 되살아나던 것처럼 화면과 서버가 어긋나던 결함을 양쪽을 함께 고쳐서 풀었습니다.
  • 시연용 심사위원 계정을 브라우저마다 하나씩 배정하고, 번호를 직접 고를 수 있게 했습니다.

협업 도구

  • 백엔드와 Flutter 앱 시험을 함께 도는 CI 를 처음 걸었습니다(백엔드는 실제 PostgreSQL · pgvector 로).
  • 한 사람 PC 에만 있던 문서 사이트의 그림 · ERD 생성기를 저장소로 옮겨 누구나 다시 그릴 수 있게 했습니다.

끝난 뒤에도 돌게 — 개인 사본

팀 서비스는 AWS(EC2 · S3 · GPU 서버)에서 돌았습니다. 프로젝트가 끝나도 링크 하나로 보여 줄 수 있도록, 팀 코드는 한 줄도 고치지 않고 전체를 내 PC 로 옮긴 사본을 따로 운영합니다 — 위 「데모」가 그것입니다.

  • AWS 없이 자가 호스팅 — k3s 위에 PostgreSQL · 백엔드 · 화면을 올리고, S3 는 MinIO 로, 공인 IP 는 Cloudflare 터널로 바꿨습니다. S3 교체는 환경 변수 하나(AWS_ENDPOINT_URL_S3)로 했습니다.
  • Cloudflare 를 지나는 첫 HEAD 요청만 403 이 나던 문제를, 백엔드만 클러스터 안에서 MinIO 에 직결시키는 방식으로 풀었습니다 — 서명(SigV4)에 스킴이 들어가지 않는다는 점을 이용해, 같은 호스트 이름을 파드 안에서만 다르게 풉니다.
  • GPU 분석을 T4 16GB 대신 RTX 3050 8GB 에서 — 추론 서버(vLLM)를 따로 띄우지 않고, 자세 추출과 언어 모델이 GPU 를 차례로 쓰게 했습니다.
  • 백업 하나로 되살리기 — 비밀 값과 데이터를 뜨는 백업과, 새로 클론한 저장소에서 전체를 되살리는 스크립트를 만들고 깨끗한 환경에서 실제로 되살려 확인했습니다(기계 준비물이 있는 상태에서 약 1분 30초).
  • 화면 개인화는 빌드 때만 — 팀 코드를 고치는 대신 이미지 빌드 문맥에 패치를 입힙니다. 홈의 포메이션 판을 「팀분석」 화면으로 옮긴 것도 이 방식입니다.
팀분석 화면 — 팀장·팀원 알약과 포메이션 판
팀분석 화면 — 팀장이 포메이션 판의 빈 자리를 채운다(시연 데이터)