1) 기능 요구사항
서비스가 무엇을 하는가를 적는다. 어떤 조건에서 동작해야 하는가는 아래 2)절이다.
번호는 새로 매기지 않는다. 부록 D가 SFR-001~010을 이미 근거로 인용하고 있어 (각 도메인 테이블 설명), 여기서 번호를 바꾸면 그 인용이 통째로 끊긴다. 아래 항목은 새로 정의한 것이 아니라 3장·부록 D에 흩어져 있던 정의를 한자리에 모은 것이다.
2)절과 같은 형식으로 확인 방법을 함께 적는다. 상태 열은 2026-09-04 기준이며 반드시 낡으므로, 믿을 것은 상태가 아니라 확인 방법이다.
아래는 요약이다. 전문·확인 방법·근거는 표 바로 아래 「상세」를 펼쳐서 본다. 상태 아이콘(✅ 구현됨 · 🟡 부분 · ❌ 미구현)도 요약일 뿐이라, 실제로 무엇이 됐고 안 됐는지는 상세의 괄호 안 서술이 정본이다.
| ID | 요구사항 (요약) | 상태 |
|---|---|---|
| SFR-001 | 경기·훈련 클립 업로드 — 규격 검사·반려 사유 기록 | ✅ |
| SFR-002 | 결정론적 코드로 지표 측정·적재 (언어 모델은 수치를 생성하지 않는다) | 🟡 |
| SFR-003 | 측정값 근거의 판정 코멘트 (점수 숫자 없이) | 🟡 |
| SFR-004 | 지표 기반 호칭 부여 (절대 기준, 상대 비교 아님) | ❌ |
| SFR-005 | 성향 벡터화·유사도 비교 (점수화하지 않는다) | ❌ |
| SFR-006 | 지원 건별 적합도 3축(수준·역할·성향) 산출 | 🟡 |
| SFR-007 | 모집 팀에 후보 추천 + 사유 기록 | ❌ |
| SFR-008 | 경기 후 상호 평가 (선택형, 제재는 별도 기록) | 🟡 |
| SFR-009 | 카드 공개 공유 (인증 없이, 수치 비노출) | 🟡 |
| SFR-010 | 경기 등록 + 필요 포지션·인원 | ✅ |
상세 — 요구사항 전문 · 확인 방법 · 근거
| ID | 요구사항 | 확인 방법 | 상태 |
|---|---|---|---|
| SFR-001 | 사용자는 자신의 경기·훈련 클립을 업로드한다. 업로드는 저장 위치와 메타를 video에 남기고, 규격 검사를 거쳐 반려 사유를 값으로 기록한다(video_validation) — 사유가 남지 않으면 왜 반려됐는지 사후에 확인할 수 없다. 촬영 가이드(측면 촬영·전신 포함·최소 해상도)는 업로드 화면에서 제시한다 | 업로드 1건에 video 1행과 video_validation 1행이 생기는지. 규격 미달 클립의 반려 사유가 비어 있지 않은지 | 구현됨 (업로드 3단계 — POST /videos/upload-url(사전 서명) · POST /videos(등록) · GET /videos, 2026.09.03. 등록 1건에 video 와 video_validation 이 함께 생기고, 규격 미달은 사유 문장으로 남는다(용량 200MB · 길이 60초 · 1920x1080). 🔴 촬영 가이드 문구는 업로드 화면에 아직 없다) |
| SFR-002 | 업로드된 클립에서 결정론적 코드가 관절 각도·각속도·무게중심 궤적을 측정해 지표 항목별로 적재한다(analysis_metric_value). 지표 항목은 종목·동작마다 다르므로 컬럼으로 고정하지 않고 metric_definition에 정의한다. 언어 모델은 수치를 생성하지 않는다(3장 3) | 같은 클립을 두 번 분석해 지표 값이 일치하는지(QUA-001). 지표 항목이 코드가 아니라 metric_definition에 정의되어 있는지 | 부분 (agent/에 측정·등급 판정이 있고 활성 루브릭은 축구 인스텝 슈팅·야구 투구·농구 점프슛 3종. DB 적재 경로는 없다) |
| SFR-003 | 측정값과 채점 기준을 대조한 판정 결과를 선수가 읽을 문장으로 남긴다(analysis_report). 근거가 된 측정값과 프레임 번호를 함께 기록한다 — 근거 없는 문장은 실력 검증의 산출물이 될 수 없다. 코멘트는 두 문장 이내이며 총점·등급 숫자를 넣지 않는다(3장 4) | 리포트 1건에 근거 측정값과 프레임 번호가 함께 남는지. 코멘트에 점수 숫자가 들어가지 않는지 | 부분 (agent/judge.py가 근거 문장을 만든다. 적재 경로 없음 · 모델 라이선스는 미결 1번) |
| SFR-004 | 지표를 근거로 호칭을 부여한다. 호칭 기준은 절대 기준이며 항목·비교연산자·임계값을 행으로 나누어 정의한다(title_definition · title_criteria). 부여 이력은 user_title에 남긴다. 사용자 간 상대 비교로 부여하지 않는다 | 같은 지표에 같은 호칭이 부여되는지. 호칭 조건이 코드가 아니라 title_criteria 행으로 표현되어 있는지 | 미구현 |
| SFR-005 | 플레이 성향을 특징 벡터로 만들어 유사도 비교에 쓴다(player_vector, pgvector 색인). 성향은 우열이 아니라 유형이므로 점수로 환산하지 않는다 | 벡터 색인이 걸려 있는지. 유사도 조회가 P95 500ms 안에 드는지(PER-003, 부록 D.4가 4단 조인을 위험 지점으로 지목한다) | 미구현 |
| SFR-006 | 경기 지원 건마다 적합도를 수준·역할·성향 세 축으로 각각 산출한다(fitness_score). 실력 점수와 적합도는 다른 값이다 — 점수는 클립 1건의 절대 평가, 적합도는 지원 1건의 상대 평가다. 축을 하나로 합친 단일 점수를 내지 않는다 | 축별로 개별 반환되는지(부록 D.4의 검수 기준). 적합도가 지원 건과 무관하게 단독 조회되지 않는지 | 부분 (지원·제안 경로 구현 — match_application, 2026.09.02. 적합도 산출(fitness_score) 자체는 미구현이고 분석 지표 적재에 걸려 있다) |
| SFR-007 | 모집 팀에 후보를 추천하고 추천 사유를 함께 남긴다(recommendation). 사유가 없으면 팀이 왜 이 사람인지 판단할 수 없고, 추천 품질을 사후에 검증할 수도 없다 | 추천 1건에 사유가 비어 있지 않은지 | 미구현 (선행인 지원 경로는 2026.09.02에 생겼다) |
| SFR-008 | 경기 후 상호 평가를 받는다. 평가는 선택형이며 선택지 정의(review_option)와 선택 결과(review_selection)를 나누어 담는다. 불참·신고 등 제재 기록은 평가 점수로 환산하지 않고 별도로 둔다(no_show · report) | 평가 결과가 단일 점수로 합산되지 않는지. 제재 기록이 review와 직접 이어지지 않는지 | 부분 (테이블 5종 — review_option · review · review_selection · report · no_show — 과 review_option 초기 목록이 2026.09.03 마이그레이션으로 들어왔다. 적재·조회 API 는 아직 없다) |
| SFR-009 | 사용자는 자신의 카드를 공개 링크로 공유한다(player_card). 공개 조회는 인증 없이 되고 경로에는 내부 식별자 대신 추측 불가능한 슬러그를 쓴다(SEC-005). 카드에 수치 능력치를 노출하지 않는다 — 수치는 리포트 경로로만 조회된다 | 토큰 없이 GET /cards/{slug} → 200. 응답에 점수·등급 수치가 없는지. 슬러그가 이름에서 유도되지 않는지 | 부분 (조회·생성 구현 — /me/card GET·POST · /cards/{slug}. 생성 시점은 2026.09.02에 「사용자가 요청할 때」로 정했다. 카드에 붙는 호칭은 SFR-004에 걸려 있다) |
| SFR-010 | 팀은 경기를 등록하고 필요한 포지션과 인원을 함께 적는다(match · match_position_need). 포지션은 둘 이상일 수 있으므로 행으로 나눈다. 종목은 team이 결정하므로 경기에 종목 컬럼을 두지 않는다 | 경기 1건에 포지션 요구가 여러 행으로 들어가는지. match에 종목 컬럼이 없는지 | 구현됨 (팀 생성·가입·탈퇴 + 경기 등록·조회·지원, 2026.09.02. position 은 종목별 기본 포지션으로 채웠다. 경기 탐색(GET /matches)과 수정·취소(PATCH·DELETE /matches/{id})를 2026.09.03 에 더했다. 지원이 붙은 경기도 취소할 수 있다 — 지원을 무르고 거절하는 경로(DELETE /matches/{id}/applications/{application_id})가 2026.09.04 에 생기며 409 가 풀렸다(미결 jin 16번 해소). 적합도·추천(SFR-006·007)은 다음 단계) |
순위표를 만들지 않는다. SFR-004·005·006·008은 모두 “다른 사람보다 낫다”가 아니라 “이 사람이 이 경기에 맞는다”를 답한다. 사용자 간 비교 점수를 저장하는 테이블을 두지 않는다는 부록 D의 설계 원칙과 같은 이야기이며, 이는 판단이지 누락이 아니다.
SFR-002·003은 미결 항목에 걸려 있다. 채점 품질을 측정할 골든셋(미결 2번)이 없으면 개선 여부를 판별할 수 없고, 판정 모델의 상업 이용 가능 여부(미결 1번)가 정해지지 않았다. 두 항목의 “구현됨”은 그 둘이 풀린 뒤에야 판정할 수 있다.
이 절에 넣지 않은 것
- 가입·로그인·프로필 — 서비스 기능이 아니라 모든 기능의 전제다. 요구사항으로는 SEC-003·004·011에서 다루고, 스키마는 부록 D 사용자·팀 도메인에 있다.
- 결제·구독 — 부록 D에 과금 도메인이 있으나 10주 범위 밖이다. 4장 수익 모델이 확정된 뒤에 적는다.
- 팀 스쿼드 편성(
squad) — 카드 묶음이라 SFR-009의 연장이지만, 화면 설계가 정해지지 않아 별도 항목으로 세우지 않는다.
2) 비기능 요구사항
기능 요구사항(SFR)이 “무엇을 하는가”라면 이 절은 “어떤 조건에서 동작해야 하는가”다.
부록 D와 3장·6장이 이미 이 절의 항목을 근거로 인용하고 있다(SEC-003 · SEC-006 · PER-001 · PER-003 · QUA-002). 따라서 번호는 기존 참조에 맞추어 고정한다 — 여기서 번호를 바꾸면 다른 문서의 근거가 통째로 끊긴다.
각 항목에는 확인 방법을 같이 적는다. 확인할 수 없는 요구사항은 지켜졌는지 판별할 방법이 없어 문서에만 남기 때문이다. 상태 열은 2026-08-28 저녁 기준이며(QUA-001·002 는 2026-09-23 에 실물과 대조해 고쳤다) 반드시 낡으므로, 믿을 것은 상태가 아니라 확인 방법이다.
아래 세 절도 요약 → 상세(펼치기) 구조다. 아이콘은 ✅ 구현됨 · 🟡 부분 · ❌ 미구현 · ⚪ 미측정이며, 실제로 무엇이 됐고 안 됐는지는 상세의 괄호 안 서술이 정본이다.
보안 (SEC)
| ID | 요구사항 (요약) | 상태 |
|---|---|---|
| SEC-001 | 통신 구간 암호화 (TLS + DB 인증서 검증까지) | ❌ |
| SEC-002 | 비밀번호 해시 저장, 알고리즘 한계 초과 입력 거부 | ✅ |
| SEC-003 | 계정·신원 분리 식별 (이메일로 사람 식별 안 함) | ✅ |
| SEC-004 | 만료형 토큰, 변경·탈퇴 시 즉시 무효화 | ✅ |
| SEC-005 | 자기 자원만 접근, 추측 불가능한 슬러그 | 🟡 |
| SEC-006 | 삭제 시 DB 행 + 객체 저장소 원본까지 함께 삭제 | 🟡 |
| SEC-007 | 영상 속 제3자 동의 절차·삭제 요청 경로 | ❌ |
| SEC-008 | 업로드 파일을 실제 컨테이너 형식으로 검증 | 🟡 |
| SEC-009 | 인증 엔드포인트 남용 방지 제한 (계정 잠금 방식은 안 씀) | 🟡 |
| SEC-010 | 인증 이벤트 로깅 (비밀번호·토큰은 기록 안 함) | ✅ |
| SEC-011 | 시크릿은 배포 환경 주입, 기본값 없이 실패 | ✅ |
| SEC-012 | 계정 존재 여부 비노출 (가입 중복 안내는 예외) | 🟡 |
상세 — 요구사항 전문 · 확인 방법 · 근거
| ID | 요구사항 | 확인 방법 | 상태 |
|---|---|---|---|
| SEC-001 | 모든 통신 구간을 암호화한다. 클라이언트–서버는 TLS, 애플리케이션–DB는 인증서 검증까지 수행한다. 검증 없는 암호화(sslmode=require)는 중간자 공격을 막지 못한다 | 접속 문자열에 sslmode=verify-full과 CA 번들 경로가 있는지 | 미구현 |
| SEC-002 | 비밀번호는 평문으로 저장·기록·전송하지 않는다. 해시(bcrypt)만 보관하며, 알고리즘 한계(72바이트)를 넘는 입력은 잘라내지 않고 거부한다 — 자동으로 잘리면 앞부분만 같아도 통과한다 | user_credential.password_hash가 $2b$로 시작. app/core/password.py | 구현됨 |
| SEC-003 | 계정과 신원을 분리해 식별한다. 계정은 user.email 유일 제약으로, 외부 로그인은 제공자가 준 subject로 식별한다 — 이메일로 사람을 식별하지 않는다. 자격증명은 user와 분리된 테이블에 둔다 | 부록 D.7의 유일 제약 3건(user.email, user_identity(provider, subject), user_identity(user_id, provider)) | 구현됨 |
| SEC-004 | 인증은 만료가 있는 Bearer 토큰으로 한다. 비밀번호 변경·탈퇴 시 기존 토큰이 즉시 무효가 되어야 한다 | 비밀번호 변경 후 옛 토큰으로 GET /me → 401 | 구현됨 |
| SEC-005 | 자기 자원에만 접근할 수 있다. 공개 링크는 추측 불가능한 슬러그로만 열고, 내부 식별자(uuid)를 공개 경로에 노출하지 않는다 | 남의 식별자로 요청 → 404. 슬러그가 이름에서 유도되지 않고 무작위인지 | 부분 (본인 조회는 토큰 기준. 슬러그 생성 규칙은 2026.09.02에 정했다 — 96비트 무작위, 닉네임에서 유도하지 않는다) |
| SEC-006 | 삭제 요청 시 원본과 모든 파생물을 함께 삭제한다. DB 행뿐 아니라 객체 저장소의 원본·썸네일·추출 프레임·임베딩을 포함한다 — 외래키 연쇄만으로는 저장소에 원본이 남는다 | 부록 D.6의 연쇄 경로. 삭제 후 해당 객체 키 조회 → 404 | 부분 (DB 연쇄는 외래키로 걸었다 — DELETE /me. 저장소는 S3 로 정했지만(2026.09.03) 탈퇴가 객체까지 지우지는 않아 원본이 남는다) |
| SEC-007 | 영상에는 업로더 외의 인물(상대 팀·관중)이 함께 촬영된다. 업로드 시 동의 절차를 두고, 촬영된 제3자의 삭제 요청 경로를 제공한다 | 업로드 화면의 동의 항목, 제3자 삭제 요청 접수 경로의 존재 | 미구현 |
| SEC-008 | 업로드 파일은 확장자가 아니라 실제 컨테이너 형식으로 검증한다. 용량·길이·해상도 상한을 두고, 디코딩·메타 추출은 애플리케이션 프로세스 밖에서 수행한다 | video_validation의 반려 사유. 확장자만 바꾼 파일이 반려되는지 | 부분 (Content-Type 화이트리스트(mp4 · mov)와 용량·길이·해상도 상한이 있고 반려 사유가 video_validation 에 남는다. 실제 컨테이너 형식은 검사하지 않는다 — 길이·해상도는 클라이언트가 잰 값이고, 서버가 다시 재려면 원본을 내려받아야 해 PER-002 와 부딪힌다) |
| SEC-009 | 인증 엔드포인트에 남용 방지 제한을 둔다. 해싱은 의도적으로 느리므로 인증 요청 자체가 자원 소모 수단이 된다. 계정 잠금 방식은 쓰지 않는다 — 남의 이메일을 아는 것만으로 그 계정을 잠글 수 있어 그 자체가 서비스 거부 수단이 된다 | 동일 출처에서 임계값 초과 요청 → 429 | 부분 (프로세스 안에서만 센다 — 서버를 여러 대로 늘리면 공유 저장소가 필요하다) |
| SEC-010 | 인증 관련 사건(로그인 성공·실패, 토큰 거부)을 사후에 추적 가능한 형태로 기록한다. 비밀번호·토큰·Authorization 헤더는 기록하지 않는다 | 로그에 password/Bearer 문자열이 남지 않는지 검사 | 구현됨 |
| SEC-011 | 시크릿은 저장소에 두지 않고 배포 환경에서 주입한다. 값이 없으면 조용한 기본값 대신 실패한다 — 기본값이 있으면 그 값으로 서명한 토큰을 누구나 만들 수 있다 | JWT_SECRET 없이 로그인 → 503(AUTH_NOT_CONFIGURED) | 구현됨 |
| SEC-012 | 실패 응답과 공개 인터페이스가 계정 존재 여부를 드러내지 않는다. 로그인은 “없는 이메일”과 “틀린 비밀번호”를 구분하지 않는다. API 문서와 데모 자격증명은 개발 환경에서만 노출한다 | 미가입 이메일과 가입된 이메일의 로그인 실패 응답이 동일한지. 운영 환경에서 /docs 접근 → 404 | 부분 (/docs는 닫았고 로그인은 구분하지 않으나, 가입은 중복을 알려준다 — 아래 예외) |
SEC-012의 예외를 명시해 둔다. 가입은 “이미 가입된 이메일입니다”를 알려주어야 사용자가 다음 행동을 정할 수 있으므로, 계정 열거를 감수하고 안내하는 쪽을 택한다. 대신 SEC-009의 요청 제한으로 대량 열거의 비용을 올린다. 이는 판단이지 누락이 아니다.
성능 (PER)
| ID | 요구사항 (요약) | 상태 |
|---|---|---|
| PER-001 | 영상 분석 비동기 처리, 작업 상태·소요시간 기록 | 🟡 |
| PER-002 | 업로드·재생이 앱 서버를 경유하지 않음 (사전 서명 URL) | 🟡 |
| PER-003 | 조회 API 응답 P95 500ms 이내 | ⚪ |
상세 — 요구사항 전문 · 확인 방법 · 근거
| ID | 요구사항 | 확인 방법 | 상태 |
|---|---|---|---|
| PER-001 | 영상 분석은 비동기로 처리한다. 업로드 응답이 분석 완료를 기다리지 않으며, 작업의 상태와 소요 시간을 analysis_job에 기록한다. 목표 소요 시간은 6장 4)의 실측으로 확정한다 | 업로드 응답 시각과 분석 완료 시각이 분리되어 기록되는지 | 부분 (analysis_job 에 status 와 created_at·started_at·finished_at 세 시각이 따로 있고, 등록 응답은 분석을 기다리지 않는다. 워커가 집고 끝내는 경로도 열렸다 — POST /internal/analysis-jobs/claim · PATCH /internal/analysis-jobs/{job_id}, 2026.09.04. 실제 폴링 워커는 아직 안 붙었다(미결 jin 18번)) |
| PER-002 | 영상 업로드·재생은 애플리케이션 서버를 경유하지 않는다(객체 저장소 사전 서명 URL). 앱 서버가 대용량 전송을 떠안으면 무관한 API 응답까지 함께 느려진다 | 업로드 트래픽이 API 서버를 통과하지 않는지 | 부분 (업로드는 사전 서명 URL 로 S3 에 직접 올라가 앱 서버를 지나지 않는다(2026.09.03). 재생 경로는 아직 없다 — 내려받을 URL 을 내주는 자리가 없다) |
| PER-003 | 조회 API 응답은 P95 500ms 이내 | 부하 시험. 부록 D.4가 벡터 검색의 4단 조인을 가장 위험한 지점으로 지목한다 | 미측정 |
품질 (QUA)
| ID | 요구사항 (요약) | 상태 |
|---|---|---|
| QUA-001 | 측정 결정론적 재현성 (같은 영상 → 같은 수치) | ✅ |
| QUA-002 | 지표 산출 버전 기록 | ✅ |
| QUA-003 | 자동 검증 통과한 변경만 병합 (skip을 통과로 안 봄) | ✅ |
상세 — 요구사항 전문 · 확인 방법 · 근거
| ID | 요구사항 | 확인 방법 | 상태 |
|---|---|---|---|
| QUA-001 | 측정은 결정론적이다. 같은 영상은 언제나 같은 수치를 낸다. 판단(언어 모델)만 비결정적이며, 그 결과에도 근거가 된 측정값과 프레임 번호를 함께 남긴다 | 같은 영상을 두 번 분석해 analysis_metric_value가 일치하는지 | 구현됨 (2026-09-09 실측 — 같은 영상을 5번 분석해 4편 모두 지표값이 소수 끝자리까지 같고 총점 표준편차 0.00. 분석 결과로 쟀고, 백엔드는 그 지표값을 그대로 적재한다. 같은 실행 환경에서 잰 값이다) |
| QUA-002 | 지표 산출에는 산출 버전을 기록한다. 채점 기준이 바뀌어도 과거 결과가 어느 버전으로 산출된 것인지 판별할 수 있어야 한다 | analysis_metric의 버전 값이 비어 있지 않은지 | 구현됨 (분석 결과마다 채점 기준 버전과 파이프라인 버전을 함께 저장한다. 보고서에 버전이 빠지면 「unknown」으로 남는다) |
| QUA-003 | 자동 검증을 통과한 변경만 병합한다. 특히 DB 통합 테스트가 건너뛴 채 초록으로 끝나지 않아야 한다 — 통과와 “한 줄도 실행하지 않음”이 구별되지 않으면 검증이 아니다 | .github/workflows/backend-tests.yml의 마지막 단계가 skipped를 exit 1로 잡는다 | 구현됨 |
이 절에 넣지 않은 것
- 백업·재해복구(RPO·RTO) — 운영 인프라가 확정된 뒤에 적는다. 지금 숫자를 적으면 근거 없는 값이 된다.
- 다중 인증(MFA)·단말 관리 — 10주 개발 범위 밖이다.
- 수치 능력치·순위표 미노출(3.5) — 성능이나 보안이 아니라 서비스 설계 원칙이므로 아래 3) 제약사항에서 다룬다.
3) 제약사항 및 전제조건
1)절이 “무엇을 하는가”, 2)절이 “어떤 조건에서 동작해야 하는가”라면 이 절은 “무엇을 할 수 없는가”와 “무엇을 참으로 놓고 설계했는가”다.
둘을 나누어 적는다. 제약(CON)은 우리가 바꿀 수 없는 조건이고, 전제(ASM)는 참이라고 가정한 것이다. 전제는 깨질 수 있으므로 깨지면 무엇이 무너지는지를 함께 적는다. 적어 두지 않으면 전제가 사실로 굳어, 틀렸을 때 어디부터 다시 봐야 하는지 알 수 없다.
번호는 이 절에서 처음 매긴다 — 다른 문서가 아직 인용하지 않는다.
제약사항 (CON)
| ID | 제약 | 영향 | 대응 |
|---|---|---|---|
| CON-001 | 개발 기간 10주(2026.08.20~10.27), 인원 4명 | 서비스 전 범위를 만들 수 없다 | 과금 도메인·MFA·백업(RPO·RTO)을 범위 밖으로 두고, 2)절에 “넣지 않은 것”으로 명시한다 |
| CON-002 | 필수 투입자원 목록에 MediaPipe·Ollama·llama.cpp가 없다 | 포즈 추정과 모델 서빙을 통상적인 조합으로 짤 수 없다 | OpenCV + Transformers로 구성한다(6장 2)·4)). ViTPose로 COCO-17 키포인트를 뽑는다 |
| CON-003 | 개발 GPU가 RTX 3050 8GB(상시 점유분 제외 약 7.4GB) | 포즈 추정기와 판정 모델을 동시에 올릴 수 없다 | 추출과 판정을 순차로 적재한다. 32B급은 24GB 이상이 필요하므로 조달 여부는 미결 4번 |
| CON-004 | 판정 모델(EXAONE)의 라이선스가 NC(비상업) | 수익 모델을 전제한 서비스와 충돌한다 | 상업 라이선스 확인, 불가 시 Apache-2.0/MIT 계열로 교체. 교체 범위는 판정 백엔드 한 곳이라 루브릭·스키마·파이프라인은 재사용된다(미결 1번) |
| CON-005 | 지도자가 라벨링한 골든셋이 아직 없다 — 섭외 여건이 안 됨을 확인(2026.09.09) | QWK·MAE는 정의 자체가 안 됨(사람 라벨과 비교하는 지표라 대체 불가) | 골든셋 없이 잴 수 있는 재현성·fps 불변성·지표 타당성을 1차 검증 지표로 쓴다(3장 「검증 기준」, 미결 2·34번). 등급 경계 근거는 지도자 검수 대신 문헌 인용으로 낮춘다. 골든셋 확보는 여건이 되는 대로 진행하는 별도 트랙 |
| CON-006 | 루브릭은 (종목, 동작) 단위로만 성립한다 — 같은 지표가 동작에 따라 반대로 채점된다 | 종목 단위로 열 수 없다 | 축구·야구·농구 종목당 1동작만 연다. 나머지는 검수 대기(미결 3번, 08.26 해소) |
| CON-007 | 던지는 팔·차는 발의 자동 판별이 팔 종목에서 신뢰할 수 없다 | 잘못 고른 쪽으로 채점하면 지표 전체가 무의미해진다 | 업로드 시 사람이 지정할 수 있게 열고, 지정이 없을 때만 자동 판별을 쓴다(미결 6번) |
| CON-008 | 임팩트 탐색 범위가 클립 전체다 — 신전 각속도의 전역 최댓값을 쓰므로 동작과 무관한 구간(와인드업·드리블 주행)이 실제 임팩트를 이긴다 | 야구·농구뿐 아니라 축구도 채점 기준이 확정되지 않았다. 접촉 지점의 신호는 언제나 상위권인데 근소한 차이로 진다 | 탐색 범위를 좁히는 것이 선행이다. 검증에 팔 종목 릴리스 정답 25~60클립이 필요해 확보 전까지 보류(미결 5번, 2026.08.31 재정의) |
| CON-009 | 영상은 사용자가 직접 촬영해 올린다 — 촬영 환경을 통제할 수 없다 | 카메라 거리·각도가 다르면 같은 자세도 다른 수치가 된다 | 어깨 너비 기준 스케일 정규화와 골반 중심 이동, 그리고 촬영 가이드 + video_validation 규격 검사(3장 3)) |
전제조건 (ASM)
| ID | 전제 | 깨지면 | 확인 시점 |
|---|---|---|---|
| ASM-001 | 사용자가 촬영 가이드(측면·전신·최소 해상도)를 따른다 | 키포인트 품질이 낮아 신뢰도 “낮음” 표기가 대량 발생한다. 정규화로 흡수되지 않는 편차다 | 실클립 검증(스프린트 2) |
| ASM-002 | 저장소는 PostgreSQL + pgvector다 | SFR-005의 성향 벡터 검색을 다시 설계해야 한다 | 인프라 확정 시 |
| ASM-003 | 영상 원본은 객체 저장소에 두고 업로드·재생이 앱 서버를 경유하지 않는다(PER-002) | 앱 서버가 대용량 전송을 떠안아 무관한 API까지 느려진다. SEC-006의 삭제 연쇄도 저장소 객체까지 넓혀야 한다 | 확정 (2026.09.03) — 버킷 supersub-ai. 업로드는 앱 서버를 안 지난다. 재생과 SEC-006의 객체 삭제는 남았다 |
| ASM-004 | 라벨링·검수를 맡을 지도자를 섭외할 수 있다 | CON-005와 물려 채점 품질을 아예 검증할 수 없다. 루브릭 임계값도 임시값으로 남는다 | 깨짐 — 섭외 불가로 확인 (2026.09.09). 대응은 CON-005 갱신 내용 참고(미결 34번) |
| ASM-005 | 외부 로그인은 구글 하나만 지원하며, 배포 전에는 등록된 테스트 계정으로만 검증된다 | 일반 사용자가 구글 로그인을 쓸 수 없다 — 동의 화면 게시가 배포의 선행 조건이 된다 | 앱 배포 전 |
| ASM-006 | 판정 모델을 자체 호스팅한다 — 분석 1회당 외부 API 비용이 없다 | 호출량에 비례하는 비용이 생긴다. 4장은 아직 원가를 다루지 않으므로(수익 항목만 있다) 그쪽을 채울 때 이 전제가 출발점이 된다 | 모델 교체 검토 시 — CON-004(EXAONE NC 라이선스)가 외부 API 로 가는 순간 깨진다(미결 1번) |
| ASM-007 | 운영 배포 환경은 아직 없다 | 지금은 확정할 수 없는 항목이 있다는 뜻이다 — SEC-001(sslmode=verify-full), PER-003 실측, SEC-009 요청 제한의 공유 저장소가 여기에 걸려 있다 | EC2 에 올랐다 (2026.09.03, 미결 jin 8번) — SEC-001의 verify-full 은 배포 문서가 지시하지만 코드 기본값은 아직 require 다 |
서비스 설계 원칙으로 두는 제약
성능이나 보안이 아니라 무엇을 일부러 만들지 않는가에 대한 결정이라 이 절에 둔다. 2)절에서 넘겨받은 항목이다. 전부 부록 D의 D.5에서 스키마 구조로 강제되어 있다 — 코드에만 두면 지켜지지 않기 때문이다.
| 원칙 | 스키마로 어떻게 막는가 |
|---|---|
| 카드에 수치 능력치를 노출하지 않는다 | player_card에 능력치 컬럼을 두지 않는다. 수치는 리포트 경로로만 조회된다 |
| 전체 순위표를 두지 않는다 | 사용자 간 비교 점수를 저장하는 테이블이 없다. fitness_score는 지원 건에 종속된다 |
| 호칭은 미부여 방식으로만 작동한다 | user_title에는 부여된 행만 있다. 미달을 false로 저장하면 그 자체가 부정 표식이 된다 |
| 제재는 평가가 아니라 기록으로 처리한다 | report·no_show를 review와 분리한다 |
| 매칭 확정은 사람이 한다 | match_application이 팀·본인의 수락 시각을 각각 갖는다 |
이 절에 넣지 않은 것
- 팀 구성과 일정 — 7장 개발 구현 계획에서 다룬다.
- 수익 모델의 전제(가격·전환율) — 4장의 몫이다. 여기서는 원가에 영향을 주는 ASM-006만 적는다.
- 미결 항목의 진행 상황 — 미결 항목 페이지가 담당자·기한과 함께 관리한다. 이 절은 그것이 요구사항에 어떤 제약으로 걸리는지만 적는다.