bibimbap/docs/work-log/2026-06-23-w2-w4-feature-sk...

225 lines
22 KiB
Markdown

---
kind: work-log
title: W2(게임잼) + W4(유저 배지/평판) — 기능별 골자 카탈로그
description: W2(게임잼 워크스트림) 6개 서브기능(W2-1 잼 엔티티/라이프사이클 / W2-2 심사위원 역할권한 / W2-3 잼 평가 통합설계·스키마 동결 / W2-4 심사위원 평가 / W2-5 인기투표 / W2-6 시상 집계) + W4(유저 배지/평판)의 골자. W3 skeletons 포맷과 동일 — 기능당 목적·핵심동작·결합/의존(code-fact vs 해석 태깅)·미결질문(비해소)·후속진입점 + 결합표 + 착수순서 + 전체수준 미결(QG). 윤곽 카탈로그 깊이 — 미결질문 해소 안 하고 남김. 당장 구현 아님, 풀설계 착수 전 굳혀두는 기획 메모.
status: draft
source_roadmap: 2026-06-17-jam-platform-roadmap.md
source_grounding: ../../.atp/work-session/20260623-104307/research/W2-W4-grounding.md
source_session: 20260623-104307
created_at: 2026-06-23
owner: art
---
# W2(게임잼) + W4(유저 배지/평판) — 기능별 골자 카탈로그
상위 로드맵: [2026-06-17-jam-platform-roadmap.md](./2026-06-17-jam-platform-roadmap.md) §W2·§W4.
자매 골자(W3): [2026-06-17-w3-feature-skeletons.md](./2026-06-17-w3-feature-skeletons.md).
**깊이**: 윤곽 카탈로그. 기능당 목적·핵심동작·결합/의존·미결질문·후속진입점만 굳혀둔다. DDL/API 계약/시퀀스/AC매핑은 기능별 착수(풀설계) 단계에서. **미결질문은 해소하지 않고 남긴다** — 골자의 목적은 "무엇을/왜 + 무엇이 미결인지"를 굳히는 것. 당장 구현 아님.
> 결합/의존의 각 항목은 `code-fact`(파일:라인 근거) / `해석`(설계 판단) 으로 태깅한다. 근거는 grounding 문서: [research/W2-W4-grounding.md](../../.atp/work-session/20260623-104307/research/W2-W4-grounding.md).
---
## ★ W3 skeletons "코드 현황" 절 stale 정정 (2건)
W3 skeletons(2026-06-17 작성)의 "코드 현황" 절은 작성 이후 **W1 착지(커밋 941f9fb) + W3-2 구현 완료**로 2건이 뒤집혔다. 이를 명시하지 않으면 후속 설계가 옛 전제로 진행된다.
1. **skeleton 코드현황 #3 "권한 인프라 — Interceptor 클래스 없음 … W3-3 포스터 게이트 얹힐 자리 미비"****정정: 실재(code-fact)**.
- `security/PermissionGate`(has/canModerate/isAdmin/require) · `RbacInterceptor`(401 vs 403 분기) · `PermissionKeys`(GAME_JAM_MANAGE/POST_WRITE/CONTENT_MODERATE) · `Roles`(ADMIN/SUBADMIN/USER) · `config/InterceptorConfig` · `config/PermissionCatalogVerifier` · `UserPermissionsMapper` 전부 착지. users 에 `role`·`permissions_epoch` 컬럼 실재, 세션 epoch 전파(`refreshIfStale`) 동작.
- **단 enforcement 갭(code-fact)**: 인터셉터는 `/admin/**` + `isAdmin` 만 검사. 권한 키 중 `CONTENT_MODERATE` 만 소비(canModerate, GameCommentController:203 / GameReviewController:422). **`POST_WRITE`·`GAME_JAM_MANAGE` 는 enum 선언만 있고 소비처 0건**.
- → W3-3 POST_WRITE·W2-1 GAME_JAM_MANAGE 는 "자리 미비"가 아니라 **"키 실재 + enforcement 연결이 신규 작업"** 으로 정정. (참고: 기존 `RecruitController` POST 도 CSRF+세션userId만, POST_WRITE 미적용 — 현재 모든 로그인 유저 개방. W3-3 은 여기에 게이트를 *추가*하는 것.)
2. **skeleton 코드현황 #2 "review 테이블·tag 테이블 모두 없음 → W3-1·W3-2 구조 전부 신규"****리뷰 부분 정정: 실재(code-fact)**.
- `game_reviews`(rating smallint CHECK 1~5 + body + nickname 스냅샷 + `is_rating_manual`) + `game_review_axes`(6축: immersion/creativity/controls/completeness/sound/visual, 리뷰당 6행) + `game_review_stats`(**테이블 아닌 VIEW**, 9컬럼 집계) + 4매퍼(GameReviewsMapper/GameReviewStatsMapper/GameReviewAxesMapper/GameCommentsMapper) 전부 착지. (tag 부분은 여전히 미존재 — W3-1 태그 구조는 신규 유효.)
- → W3-1 리뷰수/점수 정렬 · W2-6 유저평점 트랙 · W4 리뷰어 배지는 이 **하이브리드(단일 overall + 6축) + VIEW 집계**를 소비. **단일 평균 가정 금지.** axes 0행 리뷰는 6축평균 NULL(백필 미적용).
> 안 뒤집힌 항목: 코드현황 #1(games 태그 컬럼 없음 — W3-1 태그 신규 유효), #4(index.jsp 잼 노출 없음 — W3-4 유효)는 그대로. **#5 부분 정정(2026-06-23 W3-5 조사)**: WebGL 업로드 부분구현은 유효하나 "`/game/**` 핸들러 미등록 → 서빙 미보장"은 **틀림** — `GameAssetController`(@GetMapping `/game/{uuid}/**`)가 정상 서빙(의도된 설계). W3-5 실 결함은 zip-slip 심링크 구멍·업로드 권한게이트 전무. (W3 skeletons QG-3 해소.)
---
## 코드 현황 (W2/W4 착수 전 확인된 결합점)
골자의 "결합/의존" 정확도를 위해 현 코드베이스를 대조했다(grounding 문서). W2/W4 가 얹히는 핵심 사실:
1. **권한 인프라(W1 산출)**`user_permissions(user_id, permission_key)` **전역 권한 모델**. `scope/resource_id/jam_id` 컬럼 부재(schema.sql:326-347). `PermissionGate.has(session, key)` 리소스 인자 없음(PermissionGate.java:22). → **잼 회차별 역할은 그대로 안 얹힘(W2-2 스코프 갭)**. `GAME_JAM_MANAGE`·`POST_WRITE` 키는 선언만·소비처 0건.
2. **리뷰/평점(W3-2 산출)** — 하이브리드: `game_reviews.rating`(1~5) + `game_review_axes`(6축, 리뷰당 6행). `game_review_stats`**VIEW**(avg_rating/review_count/6축평균 9컬럼). 게임당 유저 1리뷰 활성유니크(game_id,user_id). axes 0행 → 6축평균 NULL. (W2-3/W2-6/W4 가 소비.)
3. **games 엔티티** — 13컬럼(id/user_id FK/name/creator_note/git_url/webgl_path/thumbnail_url/like_count/is_visible/sort_order/created_at/updated_at/is_delete). **`jam_id`·방문수·`team_id` 모두 부재**(schema.sql:88-100). user_id 단일 FK = 1게임 1유저. (W2-1 이 재사용+확장.)
4. **jams 테이블 전무**`jam_id`·`CREATE TABLE jam` 0 hit. → W2 잼 엔티티 전부 신규.
5. **game_likes**`GameLikesMapper`(get/add/update, DELETE·토글·조회 없음) + `game_likes`(id/game_id FK/`user_key varchar(200)`/created_at, PK(id)만). **1인1표 UNIQUE(game_id,user_key) 부재 — 단 schema.sql 비권위 복원본이라 운영 DB 제약 미확인(추정)**. (W2-5 가 별개로 신설.)
6. **배지/평판 구조 전무** — badge/reputation/trust_level 0 hit. → W4 전부 신규. 단 평판 신호 원천(리뷰 6축·좋아요·role·업로드)은 전부 실재.
7. **관례** — 게시판=`RecruitController`(읽기=JSP 뷰이름 반환, 쓰기=ResponseEntity JSON + `CsrfTokens.isValid` + 세션 userId + 화이트리스트), RecruitPostsMapper **페이징 없음**(전건). 신규 DDL 권위 = **`docs/*-ddl.sql`**(apply-local-ddl.sh 멱등 적용), schema.sql 은 동기 사본. 매퍼 전부 `#{}`. 표시명=`users.display_name` 단일 출처. 세션 attr 에 `permissions(Set)`·`permsEpoch(long)` 포함.
---
## 기능별 골자
### W2-1 — 게임잼 엔티티 + 라이프사이클 ★크리티컬 패스 기점
- **목적**: 게임잼 운영·평가·시상의 토대 엔티티. 회차 독립(다중 인스턴스). 출품작 = 기존 `games` 재사용 + 잼 연결.
- **핵심 동작**: `jams` 테이블(상태전이 모집→개발→평가→종료, 개발기간/평가기간 필드) + 관리자 잼 CRUD + 잼 목록/상세 페이지. 출품작 이중 노출(잼 전용 뷰 + 일반 게임 허브). 운영 표시 필드(Discord 링크/상금/후원사 — 표시만, 실지급 수동).
- **결합/의존**:
- C1: `jams` 전무 → 전부 신규 DDL. `docs/*-ddl.sql` 권위 선례(W1 의 docs/rbac-ddl.sql) 따름. — 코드사실(부재) + 해석(배치)
- C2: games↔jam 연결 신규 — games 13컬럼에 `jam_id` 부재(schema.sql:88-100). 연결 방식(games.jam_id nullable 컬럼 vs 조인테이블 `jam_entries`)은 미결. — 코드사실 + 해석
- C3: 관리자 잼 CRUD = `GAME_JAM_MANAGE` 게이트 소비. **키는 enum 선언만·소비처 0건(enforcement 갭, PermissionKeys.java:4-6)** → 이 키를 enforcement 에 연결하는 게 신규. 임시 role 직접 체크 금지(W1 게이트 위에 얹음). — 코드사실
- C4: 잼 목록/상세 = `RecruitController` 패턴(읽기 JSP뷰, 쓰기 JSON+CSRF) 선례 따름(RecruitController.java:36,65,145). 단 RecruitPostsMapper 페이징 없음 → 잼/출품작 목록 페이징은 신규. — 코드사실 + 해석
- C5: `games.team_id` 부재 = 1게임 1유저(schema.sql:89). 로드맵 "개인·팀 모두" 와 갭 — 팀 출품 지원 시 신규. — 코드사실
- **미결질문** (윤곽 — 비해소):
- Q1 games↔jam 연결: `games.jam_id` nullable 컬럼 추가 vs 조인테이블. 1게임 다중잼 출품 허용 여부.
- Q2 팀 출품 범위: 개인만 우선 vs 팀 동시. 로드맵 "개인·팀 모두" 의 1차 구현 범위.
- Q3 상태전이: 일정 기반 자동 전이 vs 관리자 수동. 기간 필드와 평가기간 게이트(W2-3·W2-5) 관계.
- Q4 회차 동시성: 동시 진행 잼 복수 허용? (메인 허브 W3-4 "진행중 잼" 노출과 연동.)
- Q5 출품 자격: 잼 기간 중 신규 출품만 vs 기존 게임 사후 연결 허용.
- **후속 진입점**: design-advisor
### W2-2 — 심사위원 역할 권한 ★보안(권한)
- **목적**: W1 RBAC 위에 얹히는 잼 전용 역할. 심사위원만 심사 점수 입력 가능(W2-4 게이트).
- **핵심 동작**: 잼 회차별 심사위원 지정(주체=잼 관리자), 심사위원 점수입력 게이트.
- **결합/의존**:
- C1: **★스코프 갭** — 현 권한모델 `user_permissions=(user_id, permission_key)` 전역, scope/jam_id 부재(schema.sql:326-347). `PermissionGate.has(session, key)` 리소스 인자 없음(PermissionGate.java:22). → "잼 회차별 역할"이 전역 모델에 그대로 안 얹힘. 스코프 확장 vs 별도 잼-역할 테이블 결정 선행. — 코드사실(전역모델) + 해석(스코프 갭)
- C2: W2-1 잼 엔티티 선행(심사위원은 잼에 종속). — 해석
- C3: 심사위원 지정 = `GAME_JAM_MANAGE`(잼 관리자) 게이트 위. — 해석
- C4: enforcement 패턴은 `CONTENT_MODERATE`(canModerate) 선례 참고 가능하나 그건 전역 판정(GameCommentController:203) — 잼 스코프 판정은 신규 경로. — 코드사실 + 해석
- **미결질문** (윤곽 — 비해소):
- Q1 **★스코프 모델**: (a) user_permissions 에 scope/jam_id 컬럼 추가 + 게이트 시그니처 확장 (b) 별도 `jam_judges(jam_id, user_id)` 테이블 (c) 하이브리드. → 전체수준 QG 승격(아래 QG-W2-A).
- Q2 심사위원 수/자격: 잼당 인원 제한? 일반유저/SUBADMIN/외부 누구나 지정 가능?
- Q3 자기출품 충돌: 심사위원이 같은 잼 출품 가능? 충돌 회피 규칙.
- Q4 역할 만료: 잼 종료 후 자동 회수 vs 잔존.
- **후속 진입점**: design-advisor (W2-1 선행 + 스코프 결정)
### W2-3 — 잼 평가 통합설계 (스키마 동결) ⚠️결합 클러스터
> 범위 = **잼 평가만**(심사/투표/시상 스키마). 댓글/리뷰 스키마는 W3-2(이미 구현) — 동결 묶음 아님.
- **목적**: 심사 점수(W2-4)·잼 투표(W2-5)·시상 집계(W2-6) 스키마를 확정·동결. "W3 리뷰 평점 → 시상 유저평점 트랙" 단방향 집계 계약 + 평가기간 게이트 계약.
- **핵심 동작**: 하류 3기능이 의존할 스키마·계약을 먼저 굳혀 재작업 차단(forward phase-gate §2.7 — 결합 클러스터).
- **결합/의존**:
- C1: W3-2 리뷰 집계가 **하이브리드**(overall rating 1~5 + 6축) + `game_review_stats` **VIEW**(9컬럼). → 시상 유저평점 트랙이 단일평균인지 6축 중 무엇인지 동결 계약이 정의해야 함. **단일평균 가정 금지**. — 코드사실(하이브리드/VIEW) + 해석
- C2: axes 0행 리뷰 = 6축평균 NULL, 백필 미적용 → 시상 집계 NULL 처리 계약 필요. — 코드사실
- C3: W2-4/W2-5/W2-6 전부 이 동결에 의존 → **동결 전 하류 착수 금지**. — 해석(phase-gate)
- C4: 평가기간 게이트 = W2-1 잼 상태(평가기간 필드) 의존. — 해석
- C5: 단방향 계약 — 시상이 `game_review_stats` VIEW 를 읽기만, 리뷰 도메인은 잼 무관(game_reviews FK 에 jam 없음). W3-2 골자 ③과 정합. — 코드사실
- **미결질문** (윤곽 — 비해소):
- Q1 **유저평점 트랙 소스**: avg_rating(overall) vs 6축 평균 vs 특정 축. 동결 계약의 핵심. → QG-W2-B.
- Q2 평가기간 한정: 시상 대상 리뷰가 평가기간 작성분만 vs 전체. (리뷰는 잼 무관 상시 작성이므로 기간 필터 방식 결정.)
- Q3 동결 범위: 심사점수·투표·시상 3스키마 + 단방향계약 + 기간게이트 중 이번 동결 경계.
- Q4 NULL/미달 처리: 리뷰 0개·axes 0행 출품작의 유저평점 트랙 산정.
- **후속 진입점**: design-advisor (W2-1 후, W2-4/5/6 선행 게이트)
### W2-4 — 심사위원 평가
- **목적**: 권한자(심사위원) 점수 입력 → 심사 트랙 집계.
- **핵심 동작**: 심사위원이 출품작 점수 입력, 심사위원 대상 집계.
- **결합/의존**:
- C1: W2-3 동결 완료 + W2-2 심사위원 권한 의존(둘 다 선행). — 해석(phase-gate)
- C2: 점수입력 게이트 = W2-2 스코프 역할(전역 아닌 잼별) → 스코프 결정(QG-W2-A) 종속. — 해석
- C3: 집계는 `game_review_stats` VIEW 패턴 재사용 vs 잼 심사용 별도 집계 신규. — 해석
- **미결질문** (윤곽 — 비해소):
- Q1 점수 척도: 6축(리뷰 axes 재사용) vs 잼 전용 축 vs 단일. 리뷰 6축과 통합/분리.
- Q2 집계 방식: 심사위원 평균 vs 가중. 동률 처리.
- Q3 수정/재입력: 평가기간 내 수정 허용? 이력.
- **후속 진입점**: design-advisor (W2-3 동결 후)
### W2-5 — 인기투표
- **목적**: 잼 전용 1인 1표, 평가기간 한정, `game_likes` 와 별개.
- **핵심 동작**: 잼 출품작 투표(평가기간만), 1인 1표.
- **결합/의존**:
- C1: `game_likes` 와 별개(로드맵 명시). game_likes 자체가 1인1표 미보장(UNIQUE 부재 추정)·`user_key varchar`·토글 메서드 부재(GameLikesMapper.java:13-43) → **재활용으로 1인1표 자동 충족 안 됨**. 잼 투표 테이블 신규. — 코드사실(매퍼) + 추정(UNIQUE) + 해석
- C2: ⚠️**추정 검증 필요** — game_likes 운영 DB UNIQUE 제약은 schema.sql 비권위 근거뿐(grounding concern). 잼 투표 1인1표는 신규 테이블에 UNIQUE 명시로 보장(설계 시). — 추정
- C3: W2-3 동결 의존 + 평가기간 게이트(W2-1 상태). — 해석
- C4: 투표 식별자 — 로그인 user_id 기반 1인1표(game_likes user_key varchar 모델과 다를 수 있음). — 해석
- **미결질문** (윤곽 — 비해소):
- Q1 투표 단위: 출품작당 1표(여러 작품 가능) vs 잼 전체 1표(최애 1개).
- Q2 투표 자격: 로그인 유저만 vs 익명. 1인1표 식별자(user_id 가정).
- Q3 결과 노출: 실시간 vs 평가기간 종료 후.
- Q4 변경/취소: 투표 변경 허용?
- **후속 진입점**: design-advisor (W2-3 동결 후)
### W2-6 — 시상 집계 / 결과 ★크리티컬 패스 종점
- **목적**: 3트랙(심사 / 유저평점 / 인기) 산정 → 수상 표시.
- **핵심 동작**: 심사(W2-4)·유저평점(W3 리뷰 집계)·인기(W2-5) 3트랙 집계, 수상 표시.
- **결합/의존**:
- C1: 3트랙 = W2-4(심사) + `game_review_stats` VIEW(유저평점) + W2-5(인기) — 전부 의존. — 해석 + 코드사실(VIEW)
- C2: 유저평점 트랙이 하이브리드 6축+overall 소비(W2-3 동결 Q1 종속). **단일평균 가정 금지**. — 코드사실(6축) + 해석
- C3: NULL 처리 — 리뷰 0/axes 0행 출품작(백필 미적용). — 코드사실
- C4: W2-3 동결 계약이 트랙 가중/합산 규칙 정의 선행. — 해석(phase-gate)
- **미결질문** (윤곽 — 비해소):
- Q1 트랙 가중: 3트랙 가중치 합산 vs 트랙별 개별 수상.
- Q2 수상 카테고리: 대상/최우수/인기상 등 구조.
- Q3 동점 처리.
- Q4 결과 확정 시점·표시 위치(잼 상세 vs 별도 시상 페이지).
- **후속 진입점**: design-advisor (W2-4/5 + W2-3 동결 후)
### W4 — 유저 배지 / 평판
- **목적**: 커뮤니티 기여 인정(권한 아님 — 평판/배지). 게임잼·RBAC 와 별개 도메인.
- **핵심 동작**: 리뷰어 배지(리뷰 활동/품질 기반) + 기술자 배지(개발정보 공유·적극 업로더). 배지 부여·표시.
- **결합/의존**:
- C1: 배지/평판 스토리지 전무(0 hit) → 전부 신규 DDL. — 코드사실(부재)
- C2: 평판 신호 원천 전부 실재 — 리뷰(6축+overall, game_reviews/stats VIEW) · 좋아요(game_likes) · 역할(users.role) · 업로드(games). 신규 배지 테이블이 이들을 집계 소비. — 코드사실 + 해석
- C3: 리뷰어 배지 = game_reviews/game_review_stats 활동·품질 소비. "품질" 정의 미결. — 코드사실 + 해석
- C4: 표시명 = `users.display_name` 단일 출처(schema.sql:32). 배지 표시는 유저 표면(프로필/리뷰 작성자/게임 카드)에 부착. — 코드사실
- C5: 로드맵상 "활동 소스 존재 후" 착수 — 단 소스 이미 실재 → **W2 무관 독립 착수 가능**. — 해석
- **미결질문** (윤곽 — 비해소):
- Q1 배지 종류·기준: 리뷰어/기술자 외 추가? 부여 임계(리뷰 N개·품질 점수·업로드 M개).
- Q2 부여 방식: 자동(임계 도달) vs 수동(운영자) vs 혼합. → QG-W4-A.
- Q3 평판 점수: 배지(이산)만 vs 연속 평판 점수 병행.
- Q4 "기술자" 명칭(임시) 확정.
- Q5 회수/강등: 배지 회수 조건(부정 리뷰 등).
- Q6 표시 위치: 프로필·리뷰 작성자·게임 카드 어디에.
- **후속 진입점**: design-advisor
---
## 워크스트림 결합 요약
| 기능 | 결합 대상 | 결합 방식 | 신규/확장 | 독립 출시 |
|---|---|---|---|---|
| W2-1 잼 엔티티 | W1(GAME_JAM_MANAGE 미연결 키) + games(jam_id 부재) | 게이트 enforcement 연결 + games 확장 | jams 신규 + games 연결 | W1 후 |
| W2-2 심사위원 역할 | W1(전역 권한모델) + W2-1 | **스코프 갭** — 모델 확장/별도테이블 결정 | 신규(스코프 모델) | W2-1 후 |
| W2-3 평가 동결 | W3-2(하이브리드 리뷰+VIEW) + W2-4/5/6 | 동결 계약 + 단방향 집계 | 신규(동결) | 결합 클러스터(동결 게이트) |
| W2-4 심사위원 평가 | W2-3 동결 + W2-2 권한 | 동결 후 + 스코프 게이트 | 신규 | W2-3 후 |
| W2-5 인기투표 | W2-3 동결 + game_likes(별개, 1인1표 추정 갭) | 별개 신규 테이블 | 신규 | W2-3 후 |
| W2-6 시상 집계 | W2-4/5 + game_review_stats VIEW | 3트랙 집계 | 신규 | W2-4/5 + 동결 후 |
| W4 배지/평판 | 리뷰·좋아요·역할·업로드 소스(전부 실재) | 신규 배지가 소스 집계 소비 | 신규(소스 실재) | 독립(소스 실재) |
---
## 착수 순서 제언 (단정 아님 — 의존 사슬 기반)
> 크리티컬 패스(로드맵): W1(완료) → **W2-1 잼 엔티티 → W2-3 평가 동결 → W2-6 시상 집계**.
1. **W2-1 게임잼 엔티티** — jams 신규 + games 연결 + GAME_JAM_MANAGE enforcement 연결. 크리티컬 패스 기점. 다른 W2 전부의 선행.
2. **W2-2 심사위원 역할** — W2-1 후. **스코프 모델 결정(QG-W2-A) 선행 필수**.
3. **W2-3 잼 평가 동결** — ⚠️결합 클러스터 게이트. W2-4/5/6 착수 전 동결 필수(phase-gate). 유저평점 트랙 소스(QG-W2-B) 확정 동반.
4. **W2-4 심사위원 평가 / W2-5 인기투표** — 동결 후 **병렬 가능**(독립 자원: 심사 vs 투표).
5. **W2-6 시상 집계** — W2-4/5 + 동결 후. 크리티컬 패스 종점.
6. **W4 배지/평판** — 평판 소스(리뷰/좋아요/역할/업로드) 전부 실재 → **W2 무관 독립 병렬 가능**. 단 배지 기준이 리뷰/잼 활동 정의에 의존하면 그 부분만 후순위.
---
## 전체 수준 미결질문 (풀설계 착수 전 확정 — QG)
> 이 QG 들은 골자 단계에서 해소하지 않는다. **풀설계 진입 시 AskUserQuestion 으로 확정**(사용자 제약).
- **QG-W2-A (★최우선 — 보안/권한)**: W2-2 잼 스코프 권한 모델 — (a) `user_permissions` scope/jam_id 컬럼 추가 + 게이트 시그니처 확장 (b) 별도 `jam_judges(jam_id, user_id)` 테이블 (c) 하이브리드. **W2-2·W2-4 전부 종속.** 현 권한 모델이 전역이라(code-fact) 잼별 역할이 그대로 안 얹힘.
- **QG-W2-B**: W2-3 동결 — 시상 유저평점 트랙 소스(overall avg vs 6축 평균 vs 특정 축) + 평가기간 필터 방식. **W2-6 종속.** 리뷰가 하이브리드라(code-fact) 단일평균 가정 불가.
- **QG-W2-C**: W2-1 games↔jam 연결 방식(games.jam_id 컬럼 vs 조인테이블) + 팀 출품 범위(games team_id 부재). **W2-1 핵심.**
- **QG-W2-D (⚠️추정 검증)**: game_likes 운영 DB UNIQUE(game_id,user_key) 제약 — schema.sql 비권위라 미확인(grounding concern). W2-5 가 별개 신규 테이블이면 game_likes 제약과 무관하나, "별개" 판정의 근거가 되므로 운영 DB 확인 권장.
- **QG-W4-A**: W4 배지 부여 기준·방식(자동 임계 vs 수동 vs 혼합) + "기술자" 명칭 확정.
> W3 측 기존 QG(QG-1 W1 게이트 착수 / QG-2 닉네임 레거시 / QG-3 `/game/**` 핸들러)는 W3 skeletons 에 잔존 — 본 W2/W4 와 직접 충돌 없음.
---
## 다음 단계
1. **골자 합의** (현재) — 본 W2·W4 골자 + stale 정정 2건 합의.
2. **각 W 미결질문 확정** — 풀설계 착수하는 기능의 *잔여* 미결 + 관련 QG 를 AskUserQuestion 으로 확정.
3. **풀설계** — design-advisor 로 진입, W1-design.md 깊이(DDL/파일영향맵/API계약/시퀀스/AC매핑). 착수 순서대로(크리티컬 패스 우선) 또는 사용자 지정 기능부터.
**보안 명시점**(풀설계에서 전수 적용): W2-2 권한(잼 스코프 게이트) / W3-3 SSRF(OG 미리보기·유니티블로그 피드 — W3 측) / W3-5 zip-slip(W3 측). W2 상태변경 전수 CSRF + `#{}` 바인딩.