이정수
45fdfb640e
feat(redesign): 빈상태 파편 2종 + 목록 페이지 include
...
fragments/posts-empty.jspf·recruit-empty.jspf 신설(.empty-state).
CTA href 실제 라우트로 교정(/posts/new·/recruit/new 단수).
posts-list·recruit-list 빈 분기에 include. recruit-list는 서버
초기 빈상태=파편 / 클라필터 0건=기존 #recruit-empty div 역할 분리,
필터 JS 무수정.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 16:13:52 +09:00
이정수
0390da6ae8
feat(redesign): 공유 bibimbap.css 도입 + theme-init 단일 link
...
리디자인 산출물 css를 src/main/webapp/css/bibimbap.css로 신설.
폰트는 system fallback 보강(외부 CDN 0, Pretendard 로컬 우선).
다크 오버라이드 블록 추가 — header.jsp data-theme=dark 토큰 차용해
body 컴포넌트와 header/footer 다크모드 정합.
theme-init.jsp(23개 페이지 공통 include)에 link 1줄 추가.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 16:07:25 +09:00
이정수
4f4811eb73
feat(ui): 검색영역 접이식 정리 + 다크모드 입력 대비·터치타깃 개선
...
- index: 고도화검색을 <details> 접이식으로(prefill 시 open) → 검색 진입점 위계 정리
- 다크모드 입력 테두리 대비 강화(border rgba(255,255,255,0.26)): login·signup·index·game-detail
- 체크박스 터치타깃 1.15rem(login·signup), summary 44px
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3FeMrbtxfTScjrwUukyHD
2026-06-29 19:10:42 +09:00
이정수
fa6a301fb5
fix(game): 좋아요 서버 영속화 — 상세 토글이 explore 카운트에 반영되도록
...
좋아요가 서버에 저장되지 않고 브라우저 localStorage 에만 토글되어
explore/상세가 읽는 games.like_count 컬럼이 갱신되지 않던 버그 수정.
- POST /game/{id}/like 토글 엔드포인트 신설 (CSRF→로그인→존재 게이트, @Transactional)
- GameLikesMapper: findByGameAndUser/deleteByGameAndUser 추가 (addGameLike 재사용)
- GamesMapper: incrementLikeCount/decrementLikeCount(GREATEST 0)/getLikeCount 추가
- game_likes row 변경 + games.like_count ±1 단일 트랜잭션 동기화
- 상세 addGameModel 이 로그인 사용자 기존 좋아요 여부(liked) 모델 주입
- game-detail.jsp 좋아요 버튼을 localStorage→fetch POST 로 교체
- explore 5개 조회 쿼리 무변경 (컬럼 갱신으로 자동 반영)
- game_likes UNIQUE(game_id,user_key) schema 반영 + 마이그레이션 작성 (DB 미적용)
- GameLikeControllerTest 7건 (회귀 AC-1 포함)
검증: L1 PASS (컴파일 + 7/7 + 컨트롤러 회귀 219/219). L2(dev DB) skip.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3FeMrbtxfTScjrwUukyHD
2026-06-29 16:09:54 +09:00
이정수
3d10449e67
feat(badge): W4 배지 표시면 배선 — 게임카드 creator 칩 + 프로필 myBadges 주입
...
GameReviewController authorBadges 정본 패턴 답습. UserBadgesQueryMapper
배치조회(listActiveBadgeKeysByUserIds) 재사용 — 신규 SQL 0.
- WebMvcController: UserBadgesQueryMapper 주입. profile→myBadges,
indexModelAndView→creatorBadges(userId 그룹핑) 모델 주입
- SearchController: /games/search→creatorBadges 주입
- index.jsp: 게임카드 creator 배지 칩 렌더(badgeLabelIndex, HtmlUtils.htmlEscape)
- 테스트 5건 신규(그룹핑/빈맵/myBadges). L1 352/352 GREEN, 회귀 0
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3FeMrbtxfTScjrwUukyHD
2026-06-29 14:37:00 +09:00
이정수
305cc73860
feat(badge): W4 유저 배지/평판 — 리뷰어/테크니션 자동·수동 하이브리드 부여 + 평판 이벤트 감사
...
- badges/user_badges/reputation_events 3테이블: docs/badge-ddl.sql 권위 + db/schema.sql 동기, 멱등. badge_type/event_type CHECK + 부분유니크(ux_user_badges_active WHERE revoked_at IS NULL · ux_reputation_events_source WHERE source_ref IS NOT NULL) + FK→users
- BadgeKeys enum 단일정의처(REVIEWER 임계10/REVIEW_WRITTEN, TECHNICIAN 임계3/GAME_UPLOADED). BadgeCatalogSeeder ApplicationRunner ON CONFLICT DO NOTHING(신규 키만 시드)
- 자동부여: ReputationService.record(best-effort 훅, 리뷰작성/게임업로드 후단 try/catch) → BadgeService.evaluateAndSync(countActive>=임계 → insertIgnore 멱등). 임계초과 추가활동·동시성 1회만(ON CONFLICT 부분유니크)
- 수동 부여/회수: BadgeManageController /manage/users/{userId}/badges/{key}/(grant|revoke) — /admin/** 밖 경로 + permissionGate.has(BADGE_MANAGE) 게이트헬퍼(ADMIN 암묵+SUBADMIN 키). CSRF→401→403. revoke=revoked_at+reason, 회수후 재획득 가능
- 신규 권한키 BADGE_MANAGE(PermissionKeys 4번째) — PermissionCatalogVerifier values() 자동 시드(W1 카탈로그 3→4, 회귀 0)
- 표시: 리뷰 작성자 배지 배치 부착(listReviews userId 집합 IN 1쿼리, N+1 차단) + game-detail.jsp 칩(textContent)
- 신규 5매퍼 #{} only(${} 0). @MockBean 6
검증: 컨테이너 ./mvnw -o test 347/347 GREEN(신규 29: BadgeServiceTest 12·BadgeManageControllerTest 14·GameReviewControllerTest 회귀 3), 회귀 0. L2 격리 throwaway DB contract PASS(자동부여 멱등·회수후 재획득 부분유니크·reputation dedupe·countActive·배치 alias·CHECK/FK). W1 카탈로그 회귀 0. 실 dev DB 무접촉.
알려진 잔여(표시 deferral): 프로필 myBadges 컨트롤러 주입 + 게임카드 creator 배지 미구현(JSP 방어적 준비됨, 무렌더 — 설계 graceful-empty 정합). 배지 도메인 코어 + 리뷰작성자 표시는 완성. 두 표시면은 WebMvcController/SearchController(W3-1/3-4) 수정 필요라 별도 후속.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3FeMrbtxfTScjrwUukyHD
2026-06-29 12:34:23 +09:00
이정수
6047a39ca5
feat(board): W3-3 포스팅 보드 — 공지/블로그 + OG 미리보기(SSRF 방어) + 유니티 피드 감시
...
- posts/post_categories/unity_feed_sources/unity_feed_items 4테이블: docs/board-ddl.sql 권위 + db/schema.sql 동기, 멱등(IF NOT EXISTS/DO $$). FK·CHECK·부분 UNIQUE(unity_feed_items source_id,guid)
- 포스팅 CRUD: PostController 공개 목록(keyset 페이징 (created_at,id)) + 상세 + 작성/수정/삭제. POST_WRITE enforcement 연결(W1 PermissionGate, 임시 role 체크 0). 작성자무관 POST_WRITE 보유자 편집
- 마크다운 본문: PostMarkdownService commonmark 0.22.0 → jsoup 1.17.2 Safelist allowlist sanitize, 저장 시 1회 캐시(body_sanitized_html). script/on*/javascript:/iframe/object 제거(adversarial 감사 SOUND)
- ★SsrfSafeFetcher 공용 외부 fetch 관문: scheme allowlist + 전 resolved IP 공인검증(사설/루프백/링크로컬/메타데이터169.254.169.254 + CGNAT 100.64/10 + class-E/benchmarking/NAT64/6to4 차단) + 포트 allowlist{80/443/8080/8443} + hostname-connect(HTTPS SNI 정합) + 매홉 재검증(redirect NEVER, MAX 3) + peer 재검증(rebinding) + size cap + timeout + Content-Type + graceful
- OG 미리보기(OgPreviewService) + 유니티 피드 폴링(UnityFeedPoller @Scheduled, SchedulingConfig @EnableScheduling, FeedParser RSS/Atom XXE 방어 disallow-doctype) + guid dedupe(ON CONFLICT DO NOTHING)
- 카테고리/피드 운영: PostAdminController/UnityFeedAdminController /admin/** ADMIN 인터셉터 + CSRF 전수. 카테고리 삭제 FK 보호
- 신규 6매퍼 #{} only(${} 0). @MockBean 8. JSP 5(scriptlet+HtmlUtils.htmlEscape, JSTL 의존 부재 정합)
검증: 컨테이너 ./mvnw -o test 287/287 GREEN(신규 44), 회귀 0. L2 격리 throwaway DB contract PASS(keyset row-comparison·alias·insertIgnoreDup ON CONFLICT·FK 거부). adversarial SSRF 감사: rebinding/redirect/scheme/IP인코딩/IPv6 차단 실증, XSS sanitize SOUND. 실 dev DB 무접촉.
backward 보정(동일 커밋 통합): (1) Host restricted-header→fetch 전건 empty+테스트 vacuous → hostname-connect 전환(HTTPS SNI 정합, JVM 플래그 불요) (2) CGNAT 등 차단범위 확장 (3) FeedParser RFC1123 요일-날짜 불일치 robust 파싱.
알려진 잔여: option-b hostname-connect 는 connect 시 재resolve 로 rebinding TOCTOU 창 존재(DNS 캐시로 최소화). 위협모델상 LOW(fetch URL 제출자=ADMIN 피드등록/POST_WRITE OG). HTTPS 실 TLS = L3 스모크 권고.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3FeMrbtxfTScjrwUukyHD
2026-06-29 11:46:28 +09:00
이정수
e28fa60ca6
feat(hub): W3-4 메인 허브 keyset 페이징 + 진행중 잼 배너 (단계1+2 통합)
...
- keyset 페이징: GamesMapper.listVisibleKeyset/searchVisibleKeyset 신규 — 커서 (sort_order,created_at,id) 3-튜플 OR 분해(혼합방향 ASC/DESC/DESC), LIMIT #{limit}, #{} only. 기존 getVisibleGames/searchVisibleGames 보존(회귀 0)
- WebMvcController: cursor 파라미터 + nextCursor 산정 + parseCursor/encodeCursor(형식오류→첫페이지 폴백, throw 0) + HubCursor record + HUB_PAGE_SIZE=24. 검색/비검색 동일 커서 규약(공통 헬퍼)
- 진행중 잼 배너(단계2): JamsMapper.listActive/countActive 신규(status IN RECRUIT/DEV/EVAL, is_visible). 최신 1건 + "외 N-1개" 라벨, N=0 미렌더. CTA → W3-1 /games/search?jam={slug}
- index.jsp: 더보기 링크(hasNextPage, q+cursor URLEncoder) + 잼 배너(title HtmlUtils.htmlEscape, slug URLEncoder). 기존 렌더루프+W3-1 태그 UI 보존
- docs/games-hub-ddl.sql: idx_games_visible_keyset 멱등(games 컬럼 0변경) + schema.sql 동기
- BibimbapApplicationTests 무변경(JamsMapper/GamesMapper @MockBean W2-1 기등록 재사용)
검증: 컨테이너 ./mvnw -o test 243/243 GREEN(신규 8: WebMvcControllerTest), 회귀 0. L2 격리 throwaway DB contract PASS(혼합방향 keyset 경계 무중복/무누락·동일 sort_order/created_at tie-break·검색 keyset·잼 status 필터). 정렬키 5곳 동일·${} 0. 실 dev DB 무접촉.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3FeMrbtxfTScjrwUukyHD
2026-06-29 10:59:17 +09:00
이정수
35f1dc3de9
feat(search): W3-1 게임 태그 + 검색 확장 — tags/game_tags/jam_tags/game_views + 다중태그·정렬키·방문수·리뷰 정렬
...
- 신규 4테이블(tags/game_tags/jam_tags/game_views) + games.view_count ALTER: docs/tag-ddl.sql 권위 + db/schema.sql 동기 사본, 멱등(IF NOT EXISTS/DO $$ guard)
- 통합 태그 도메인: tag_type(GAME/JAM/COMMON) + 운영자 사전정의·사용자 pending→승인 하이브리드. slug/name UNIQUE
- 검색 확장 GamesMapper.searchGamesAdvanced(SearchCriteria): 태그 다중필터(AND HAVING==tagCount / OR IN) + 제작자 ILIKE + 정렬키 6(latest/likes/views/reviews/rating/relevance) + 잼 검색 라우트(/games/search?jam={slug}, jam_entries 조인). 기존 searchVisibleGames 보존(회귀 0)
- game_review_stats VIEW LEFT JOIN: avg_rating AS "avgRating"/review_count AS "reviewCount" 케이스폴딩 인용. NULLS LAST. 정렬키/tagMode 는 <choose> 내부 고정문자열
- 방문수: game_views 24h dedupe(interval) + games.view_count 비정규화 증분. GameController view 훅
- 태그 관리 API: TagController(생성/승인/비활성/부착) CSRF 전수 + /admin/tags CONTENT_MODERATE 게이트. TagSanitizer 길이2~20·화이트리스트·금칙어(banned-words.txt)
- 신규 4매퍼 #{} only(${} 0건). GameData nullable 박스필드(viewCount/avgRating/reviewCount) 확장. BibimbapApplicationTests @MockBean 4종
검증: 컨테이너 ./mvnw -o test 235/235 GREEN(신규 45: TagSanitizerTest 16·SearchControllerTest 10·TagControllerTest 19), 회귀 0. L2 격리 throwaway DB contract PASS(alias 케이스폴딩 인용/비인용 대조·NULLS LAST·AND⊆OR·ILIKE·interval dedupe). 집합전수 AC-1/2/4/5 PASS. 실 dev DB 무접촉.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3FeMrbtxfTScjrwUukyHD
2026-06-29 10:44:23 +09:00
이정수
a74bf74d13
feat(jam): W2-6 시상 집계 — 3트랙 개별수상 + 가중 GRAND + CLOSED 확정 멱등
...
- JamAwardService: 3트랙(JUDGE/USER_RATING/POPULAR) 개별수상 + GRAND 가중집계. @Transactional recompute = deleteByJamTrack → 재insert(멱등, 두 번 돌려도 결과 동일)
- NULL/임계 제외: JUDGE weightedTotal NULL 제외, USER_RATING review_count>=3 임계·NULLS LAST, POPULAR 득표>0. GRAND 가용 트랙만 가중 정규화(RankScores)
- 소비: jam_score_stats VIEW(심사) + game_review_stats(유저평점, JamReviewRatingMapper) + jam_votes(인기, JamVotesMapper.listCountsByJam 재사용). 신규 DDL 0(W2-3 jam_awards 소비)
- §33 인용 alias: 집계 매퍼(JamReviewRatingMapper) camelCase AS "..." 인용, JamAwardsMapper 일반 POJO 비인용
- JamAwardAdminController: CLOSED 게이트(422) + CSRF(403) + GAME_JAM_MANAGE. JamAwardController 공개 결과. jam-results.jsp. JamController +생성자 인자(AW-DETAIL)
- BibimbapApplicationTests @MockBean 2매퍼(JamAwardsMapper/JamReviewRatingMapper)
검증: ./mvnw -o test 190/190 GREEN(신규 19: JamAwardServiceTest 7·JamAwardAdminControllerTest 7·JamAwardControllerTest 5, 회귀 0 — JamControllerTest 14/14 6arg 후도 GREEN). L2 contract PASS — 멱등 recompute(DELETE→INSERT 2회 count 불변·md5 동일)·3트랙 임계/NULL 제외·alias camelCase 보존·GRAND 가중. 집합전수 AC-T1~6 PASS.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 12:40:51 +09:00
이정수
5ed06d9942
feat(jam): W2-5 인기투표 — jam_votes 1인1표 UNIQUE + 평가기간 게이트 + 종료후 공개
...
- JamVoteController 4핸들러(POST vote 토글 / DELETE cancelVote / GET mine / GET results)
- 1인1표: jam_votes UNIQUE(jam_id,voter_user_id) + 컨트롤러 토글(findVotedGameId → cast/no-op/update)
- 평가기간 게이트: JamEvalWindow.isOpen(W2-4 재사용) → 422. CSRF 누락 → 403. 미로그인 → 401(results 공개)
- 종료후 공개(밴드왜건 회피): CLOSED || eval_end 경과 시만 게임별 count 노출 — 컨트롤러+JSP 2지점 게이트, 진행 중 은닉
- JamVotesMapper: listCountsByJam Map resultType 집계 → AS "gameId"/"voteCount" 인용(§33 케이스폴딩 가드 — Map resultType 기준, 설계 concern3 정정). 신규 DDL 0(W2-3 jam_votes 소비)
- BibimbapApplicationTests @MockBean JamVotesMapper
검증: ./mvnw -o test 171/171 GREEN(신규 23 JamVoteControllerTest, 회귀 0). L2 contract PASS — ux_jam_votes UNIQUE 거부·토글 update·alias camelCase 보존(비인용 대조군 폴딩 재현). 집합전수 AC-T1~4 PASS.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 12:16:20 +09:00
이정수
09ed6bcaa6
feat(jam): W2-4 심사위원 평가 — criterion UPSERT + 가중집계 + 3중게이트
...
- JamScoringController: 3중게이트 순서(CSRF 403 → 인증 401 → isJudge[W2-2] 403 → 잼 404 → JamEvalWindow 평가기간 422 → 출품작 404 → isOwnEntry 자기출품 422 → criterion 화이트리스트+score 검증 → UPSERT). @Transactional 원자성
- JamScoresMapper: jam_scores (심사위원,기준,평가단위) UNIQUE 상 INSERT...ON CONFLICT UPSERT(멱등). JamCriteriaMapper, JamScoreStatsMapper(jam_score_stats VIEW 소비)
- §33 인용 alias: JamScoreStatsMapper camelCase 5건 전부 AS "..." (gameId/weightedTotal/simpleTotal/scoredCriteria/judgeCount) — Postgres 케이스폴딩(game_review_stats BUG-2) 회피
- JamEvalWindow 평가기간 게이트, jam-scoring.jsp. 신규 DDL 0(W2-3 동결 소비)
- BibimbapApplicationTests @MockBean 3매퍼
검증: ./mvnw -o test 148/148 GREEN(신규 29: JamScoringControllerTest 19·JamEvalWindowTest 10, 회귀 0). L2 contract PASS — UPSERT ON CONFLICT 멱등·VIEW alias camelCase 보존(비인용 대조군 소문자 폴딩 재현으로 BUG-2 회피 입증)·가중집계 손계산 일치. 집합전수 AC-T1~6 PASS(정책응답 401×2/403×4/404×3/422×5/200×5).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 11:57:23 +09:00
이정수
71b6b6f32a
feat(jam): W2-2 심사위원 역할 — jam_judges + JamRoleGate(잼 스코프 게이트) + 자기출품 충돌
...
- 신규 jam_judges 테이블(docs/jam-judge-ddl.sql 권위 + db/schema.sql 동기, 멱등). 전역 RBAC(user_permissions) 무변경 — 잼 스코프 권한은 별도 조인
- JamRoleGate.isJudge(잼별 지정 조회) + isOwnEntry(개인 entrant OR 팀멤버 OR-EXISTS) — 자기출품 충돌 판정 헬퍼/계약 제공(enforce=W2-4)
- JamJudgeAdminController: 지정/해제/조회 + requireJamManage 게이트 + CSRF. W2-1 /admin/jams/** exclude 가 judges 트리 커버(인터셉터 무수정)
- JamEntriesMapper.isOwnEntry 추가(#{} only). admin-jam-list.jsp 심사위원 섹션
- BibimbapApplicationTests @MockBean 2건(JamJudgesMapper/JamRoleGate)
검증: ./mvnw -o test 119/119 GREEN(신규 22: JamRoleGateTest 8·JamJudgeAdminControllerTest 14, 회귀 0). L2 contract PASS(ux_jam_judges UNIQUE 거부·isOwnEntry OR-EXISTS 6/6 정합). 집합전수 AC-T1~7 PASS(전역 RBAC 무변경 단언 포함).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 11:15:22 +09:00
이정수
ccf1e42430
feat(jam): W2-1 게임잼 엔티티/라이프사이클 — jams/entries/teams + GAME_JAM_MANAGE enforcement + 공개 목록·상세
...
- 신규 5테이블(jams/jam_teams/jam_team_members/jam_entries/jam_status_log) DDL: docs/jam-ddl.sql 권위 + db/schema.sql 동기 사본, 멱등(IF NOT EXISTS/DO $$ guard)
- 잼-게임 연결 = 조인테이블 jam_entries(games 무변경). 평가단위 = (jam_id,game_id) 활성 자연키. 잼당 게임 1회 활성 UNIQUE
- 출품 주체 개인/팀 XOR(entrant_type + XOR CHECK). jam_teams/jam_team_members
- 라이프사이클 4상태(RECRUIT/DEV/EVAL/CLOSED) + JamLifecycle 전이 그래프·기간정합 + jam_status_log 감사(수동/자동 공통 코어)
- GAME_JAM_MANAGE enforcement: JamAdminController 진입부 requireJamManage 게이트 + InterceptorConfig /admin/jams/** exclude(SUBADMIN+키 통과)
- 공개 JamController: keyset 페이징 목록(/jams) + 상세(/jams/{slug}) + 출품/팀 액션(CSRF). 3 JSP
- 신규 5매퍼 #{} only, snake→camel alias. BibimbapApplicationTests @MockBean 5건
검증: ./mvnw -o test 97/97 GREEN(신규 32: JamControllerTest 14·JamAdminControllerTest 10·JamLifecycleTest 8 + contextLoads), 회귀 0. L2 dev DB contract PASS(XOR/status CHECK·활성 UNIQUE·keyset row-comparison). 집합전수 AC-T1~6 PASS.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 10:50:12 +09:00
이정수
941f9fb128
feat(rbac): W1 거버넌스/RBAC — 관리자 콘솔·권한 게이트·세션 epoch 전파
...
- role(ADMIN/SUBADMIN/USER) + user_permissions join 권한 모델 도입. ADMIN 암묵 전권, SUBADMIN 부여 키만, USER 0.
- permissions 카탈로그(DB 테이블) + PermissionKeys enum 단일 정의처. 부팅 시 PermissionCatalogVerifier 가 enum→DB 멱등 시드·불일치 경고(코드↔DB 동기화 계약).
- PermissionGate(판정 코어) + RbacInterceptor(/admin/** ADMIN 게이트) + InterceptorConfig. 미인증 401/redirect·미인가 403·CSRF 실패 403 정책 확정.
- AdminConsoleController 4액션(임명/권한토글/강등/운영진 목록) + admin-console.jsp. 상태변경 전부 CsrfTokens.isValid 선검증.
- users.permissions_epoch 스탬프 + 요청당 PK 단일조회 대조로 권한 회수 즉시 반영. Spring Session 부재(톰캣 in-memory)로 타 세션 직접 무효화 불가한 제약을 epoch 대조로 우회 — 회수 우회 차단.
- comment/review 모더레이션 ROLE_ADMIN.equals(role) → PermissionGate.canModerate(ADMIN OR SUBADMIN+CONTENT_MODERATE) 흡수. ADMIN 통과 동작 회귀 보존.
- rbac_audit_log 임명/강등/부여/회수 대칭 기록.
- DDL: docs/rbac-ddl.sql(멱등·비파괴, 기존 전원 role=USER 호환), db/bootstrap-admin.sql(최초 ADMIN 수동 seed — 자동 승격 경로 부재).
검증: ./mvnw -o test 65/65 GREEN (PermissionGateTest 7 · AdminConsoleControllerTest 13 · GameComment/ReviewControllerTest 흡수 회귀 PASS). DDL 로컬 적용·L2(DB 방언 계약)·L3(스모크)는 미수행 — 별도 결정으로 분리.
설계: .atp/work-session/20260622-180054/implementation/W1-design.md
요구: .atp/work-session/20260622-180054/research/W1-requirements.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 10:38:57 +09:00
이정수
b9d836d5f4
feat: W3-2 댓글/리뷰 고도화 — 다축 평점·육각형 레이더·페이지네이션·집계뷰
...
W3-2 코어 위에 FINAL SPEC 전체 구현 (resumed_from 20260618-145152).
- 다축 평점: game_review_axes(6축·리뷰당6행) + overall 자동평균/수동(is_rating_manual)
- 육각형 SVG 레이더(요약+카드, frontend-design 폴리시), 6축 radiogroup(roving tabindex)
- A1 commentView 통일 / A2 하이브리드 작성자명+탈퇴자 마스킹(QG-2) / A3 edited·updated_at
- B1 페이지네이션(limit+1 hasMore) + sort 화이트리스트(@SelectProvider, ${} 미사용)
- B2 본문 최소10자 / B3 TextNormalizer 정규화
- C3 game_review_stats 집계뷰(W3-2 일반 DDL, 잼평가 동결 무관). 클라 평균계산 폐기
- DDL 멱등 ALTER/CREATE (game-reviews-ddl.sql + db/schema.sql 동기화)
검증: ./mvnw test 43/43 GREEN(회귀0). DDL 적용·L3 스모크 = 사용자 환경.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 12:29:21 +09:00
이정수
e98c437765
feat: W3-2 댓글/리뷰 분리 구현
...
기존 game_comments(닉네임 자유입력, localStorage 전용)를 서버 영속화 +
로그인 연동으로 전환하고, 별점 5점 + 서술 평가 형태의 game_reviews 도메인 신설.
- 댓글 서버 영속화: localStorage → DB game_comments. user_id NULL FK 비파괴 추가,
content 200자 앱레벨 검증. 레거시 nickname 레코드 보존(비마이그레이션, QG-2).
- game_reviews 신설: 게임당 1회(partial UNIQUE), rating CHECK(1~5), soft-delete.
- 권한: 작성자 본인 OR ROLE_ADMIN. 비로그인 쓰기 401, CSRF 미검증 403.
- GameController.deleteGame 에 리뷰 cascade soft-delete 추가.
- GameCommentController(C1~C4) / GameReviewController(R1~R5) 신규.
- 컨트롤러 단위 테스트 추가, BibimbapApplicationTests 매퍼 @MockBean 보강.
- 보안 체크리스트 댓글 항목 충족 마킹, 좋아요 항목은 범위 밖 미결 유지.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019tHAb6XYHWDPzb82FNKugo
2026-06-18 14:35:24 +09:00
김판돌
f6f691ca73
fix: harden auth csrf handling
2026-06-16 15:38:44 +09:00
pandoli365
d88cfbf5fb
페이지 개선
2026-06-13 15:05:12 +09:00
pandoli365
2cca6c72a6
비빕밥 팀원 모집 페이지 작업
2026-06-13 14:43:33 +09:00
김판돌
8f6a49cb54
프로젝트가 보이지 않는 문제 수정
2026-05-27 16:47:50 +09:00
pandoli365
40c6c88e9d
삭제시 delete가 아닌 기록이 남도록 변경
2026-05-05 23:10:44 +09:00
pandoli365
4e49fd21ff
게임 등록 버그 수정
2026-05-03 23:55:26 +09:00
pandoli365
b046e01263
사이트 이용 약관 처리
2026-05-03 23:01:42 +09:00
pandoli365
3ed6fcb800
게임 등록 추가
2026-05-03 22:25:18 +09:00
pandoli365
39674c7be7
프로필 개선
2026-05-03 19:34:31 +09:00
pandoli365
49dd1e5a6f
로그인 페이지 개선
2026-05-03 18:07:38 +09:00
pandoli365
3508a4b3bb
로그인 페이지 작업완료
2026-05-03 17:56:40 +09:00
pandoli365
23f594c3c5
Prepare public repository
2026-05-03 16:13:32 +09:00