---
kind: change
title: "W3-2 댓글/리뷰 분리 구현"
session_id: 20260618-104034
created_at: 2026-06-18
status: implemented
related_security_checklist: "../security/security-remediation-checklist.md"
related_work_log: "../work-log/2026-06-17-w3-feature-skeletons.md"
related_design: "../../.atp/work-session/20260618-104034/design.md"
---

# W3-2 댓글/리뷰 분리 구현 변경 이력

상위 골자: [W3 사이트 플랫폼 기능 골자 카탈로그](../work-log/2026-06-17-w3-feature-skeletons.md) §W3-2.

## 개요

기존 `game_comments`(닉네임 자유입력, localStorage 전용)를 서버 영속화 + 로그인 연동으로 전환하고,
별점 5점 + 서술 평가 형태의 `game_reviews` 도메인을 신설했다.

## 변경된 런타임 동작

### 1. 댓글 서버 영속화 전환

- **이전**: 댓글이 `localStorage`에만 저장. `game_comments` 매퍼·테이블은 고아 상태.
- **이후**: 댓글이 서버 DB `game_comments`에 영속. 새로고침·브라우저 변경 후에도 유지.
- 기존 localStorage 댓글은 마이그레이션하지 않음(보안 [hold] 정합 — QG-2 결정).
- `game_comments.user_id` bigint NULL FK 추가(비파괴, 레거시 nickname 레코드 보존).
- content 200자 앱레벨 검증 신규 적용(이전: 길이 제한 없음).

### 2. game_reviews 도메인 신설

- 신규 테이블 `game_reviews`: 게임당 1회 제한(partial UNIQUE WHERE is_delete IS NOT TRUE), rating smallint CHECK(1~5), body text.
- "수정됨" 상태: in-row(updated_at > created_at). 별도 이력 테이블 없음(열람 권한 확정 시 후속).
- 삭제: soft-delete(is_delete, deleted_at).

### 3. 권한 모델

- 댓글/리뷰 수정·삭제: 작성자 본인(`sessionUserId == 리소스.userId`) OR 운영자(`ROLE_ADMIN="ADMIN"`).
- 운영자 role 부여 경로는 W1 RBAC 연결 시 활성(현재 휴면 — 부여자 없음).
- 로그인 없는 쓰기 시도: 401.
- CSRF 토큰 없는 상태 변경: 403.

### 4. 게임 삭제 cascade 확장

- `GameController.deleteGame`에 `softDeleteGameReviews` 추가.
- 게임 삭제 시 관련 리뷰도 soft-delete.

## 신규/변경 파일

### 신규 파일 (백엔드)

| 파일 | 설명 |
|---|---|
| `src/.../data/GameReviewData.java` | 리뷰 도메인 POJO |
| `src/.../mapper/GameReviewsMapper.java` | 리뷰 MyBatis 매퍼 |
| `src/.../controller/api/GameCommentController.java` | 댓글 CRUD API (C1~C4) |
| `src/.../controller/api/GameReviewController.java` | 리뷰 CRUD API (R1~R5) |
| `docs/game-reviews-ddl.sql` | 권위 DDL (멱등 ALTER, 기존 DB 적용용) |
| `src/test/.../GameCommentControllerTest.java` | 댓글 컨트롤러 단위 테스트 12건 |
| `src/test/.../GameReviewControllerTest.java` | 리뷰 컨트롤러 단위 테스트 13건 |

### 변경 파일

| 파일 | 변경 내용 |
|---|---|
| `src/.../data/GameCommentData.java` | userId 필드 추가 |
| `src/.../mapper/GameCommentsMapper.java` | user_id 기반 쿼리 확장 |
| `src/.../mapper/GamesMapper.java` | softDeleteGameReviews 추가 |
| `src/.../controller/api/GameController.java` | deleteGame cascade 확장, currentUserId/userRole 모델 노출 |
| `db/schema.sql` | game_reviews CREATE + game_comments user_id ALTER |
| `src/main/webapp/WEB-INF/views/game-detail.jsp` | 댓글 localStorage→서버 fetch 교체, 리뷰 UI 신규(별점 위젯·수정됨 마커·게임당1회·로그인 게이트) |
| `src/test/.../BibimbapApplicationTests.java` | GameCommentsMapper/GameReviewsMapper @MockBean 2개 추가 |

## 신규 API 9개

### 댓글 (GameCommentController)

| ID | 메서드 | 경로 | 설명 |
|---|---|---|---|
| C1 | GET | `/game/{id}/comments` | 댓글 목록 조회 |
| C2 | POST | `/game/{id}/comments` | 댓글 작성 (CSRF·로그인·200자 검증) |
| C3 | PUT | `/game/{id}/comments/{commentId}` | 댓글 수정 (작성자/운영자) |
| C4 | DELETE | `/game/{id}/comments/{commentId}` | 댓글 삭제 (작성자/운영자) |

### 리뷰 (GameReviewController)

| ID | 메서드 | 경로 | 설명 |
|---|---|---|---|
| R1 | GET | `/game/{id}/reviews` | 리뷰 목록 조회 |
| R2 | GET | `/game/{id}/reviews/{reviewId}` | 리뷰 단건 조회 |
| R3 | POST | `/game/{id}/reviews` | 리뷰 작성 (게임당 1회, CSRF·로그인·rating 검증) |
| R4 | PUT | `/game/{id}/reviews/{reviewId}` | 리뷰 수정 (수정됨 마커, 작성자/운영자) |
| R5 | DELETE | `/game/{id}/reviews/{reviewId}` | 리뷰 삭제 (soft-delete, 작성자/운영자) |

모든 상태 변경 API 공통 시퀀스: `@Transactional → CsrfTokens.isValid (403) → sessionUserId (401) → 검증 (400) → mapper → JSON`.

## 범위 밖 (미변경)

- **좋아요**: 여전히 localStorage 전용. 보안 체크리스트 B3 좋아요 항목 별도 관리.
- **운영자 role 부여**: ROLE_ADMIN 상수 정의만. 실제 부여 경로는 W1 RBAC 연결 시.
- **W2-3 집계 계약**: game_reviews가 평점 공급원(rating 컬럼)까지만. AVG/COUNT 집계·뷰 미생성(W2-6 시상 후속).
- **리뷰 이력 열람**: in-row 수정됨 마커만. 이력 테이블은 열람 권한 확정 시 후속.

## 검증 결과 요약

| 레이어 | 결과 |
|---|---|
| L1 단위 테스트 (31건) | PASS (GameCommentControllerTest 12 + GameReviewControllerTest 13 + UserControllerCsrfTest 5 + BibimbapApplicationTests 1) |
| AGG-1 엔드포인트 수 | PASS (Comment 4, Review 5) |
| AGG-3 CSRF 게이트 | PASS (합산 6개) |
| AGG-2 DDL 3종 | PASS |
| L3 브라우저 스모크 | needs_user_verification |
| DDL 적용 | needs_user_verification (docs/game-reviews-ddl.sql 참조) |

## 이월 항목

1. ~~W2-3 평점 집계 계약 (SELECT AVG/COUNT) — W2-6 시상 후속.~~ → **해소**: C3 재분류 확정(W2-3 동결 무관). game_review_stats 집계뷰 [2026-06-22 고도화에서 신설](./2026-06-22-w3-2-comments-reviews-enhancement.md#c3-재분류--직전-코어-이월-항목-해소-중요).
2. 운영자 role 부여 경로 — W1 RBAC/Interceptor 연결 시 활성.
3. ~~다축(육각형) 평점 — 현재 단일 rating, game_review_axes 분리 여지(주석).~~ → **해소**: [2026-06-22 고도화](./2026-06-22-w3-2-comments-reviews-enhancement.md)에서 game_review_axes 6축 + 육각형 SVG 레이더 구현 완료.
4. 리뷰 이력 열람 권한 — 열람 정책 확정 시 별도 이력 테이블.
5. GameCatalog 정적 폴백 게임 대상 댓글/리뷰 — DB 실제 게임만 기능 동작(폴백은 404).
