목표
스팟(포스트 위 아이템 핫스팟)에 대해, 유저가 「소유 자가 신고」 후 사진(필수에 가깝게) 및 선택 텍스트로 리뷰를 남기고, 다른 유저가 스팟 단위로 리뷰 목록을 볼 수 있게 한다. (솔루션 링크 UGC와 별도 도메인.)
제품 (1차 확정)
- 소유 검증: 자가 신고만 — 체크/토글 후 작성 가능. 신고·모더이션·검수는 후속 이슈.
현재 스팟 구현
packages/api-server/src/domains/spots/ — CRUD, GET /api/v1/spots/{spot_id} 등
- 리뷰 전용 테이블/API 없음
DB (제안)
테이블 예: spot_reviews
id (UUID PK)
spot_id (FK → spots, ON DELETE CASCADE 권장)
user_id (FK → users)
body (text, nullable) — 길이 상한 서버 검증
image_urls (jsonb) — 1~N장, 상한(예: 5) 정책
created_at, updated_at
UNIQUE(spot_id, user_id) — 유저당 스팟당 1리뷰(수정은 PATCH로)
Rust (api-server)
웹
수용 기준
- 동일 스팟에 대해 리뷰 작성 후 목록 API로 조회 가능하다.
- 미로그인은 작성 불가, 목록은 (정책대로) 읽기 가능.
- 마이그레이션·Entity·OpenAPI·웹이 한 PR 세트로 일관된다.
참고 파일
packages/api-server/src/domains/spots/handlers.rs, service.rs
packages/api-server/src/domains/posts/service.rs (스토리지 업로드)
- 포스트 상세:
packages/web/lib/components/detail/ImageDetailContent.tsx
후속(별도 이슈로 쪼개기 권장)
- 신고 API, 운영 승인(draft), 솔루션 채택자만 작성 허용 등
목표
스팟(포스트 위 아이템 핫스팟)에 대해, 유저가 「소유 자가 신고」 후 사진(필수에 가깝게) 및 선택 텍스트로 리뷰를 남기고, 다른 유저가 스팟 단위로 리뷰 목록을 볼 수 있게 한다. (솔루션 링크 UGC와 별도 도메인.)
제품 (1차 확정)
현재 스팟 구현
packages/api-server/src/domains/spots/— CRUD,GET /api/v1/spots/{spot_id}등DB (제안)
테이블 예:
spot_reviewsid(UUID PK)spot_id(FK → spots, ON DELETE CASCADE 권장)user_id(FK → users)body(text, nullable) — 길이 상한 서버 검증image_urls(jsonb) — 1~N장, 상한(예: 5) 정책created_at,updated_atUNIQUE(spot_id, user_id)— 유저당 스팟당 1리뷰(수정은 PATCH로)Rust (api-server)
spot_reviews(또는spots하위 서브라우트) — 패턴:dto→service→handlersGET /api/v1/spots/{spot_id}/reviews?page&per_page— 공개 목록, 작성자 프로필 필드 포함POST /api/v1/spots/{spot_id}/reviews— 인증; body + 이미지 URL 목록(이미 업로드된 R2 URL) 또는 멀티파트PATCH/DELETE본인 리뷰만posts의 R2 업로드(packages/api-server/src/domains/posts/service.rsupload_image등) 재사용 — 클라이언트가 먼저 이미지 업로드 후 URL만 POST하는 2단계가 가장 단순할 수 있음웹
ImageDetailContent, 스팟 시트/모달)에 리뷰 리스트 섹션openapi.json갱신 →bun run generate:apidocs/agent/api-v1-routes.md표 추가수용 기준
참고 파일
packages/api-server/src/domains/spots/handlers.rs,service.rspackages/api-server/src/domains/posts/service.rs(스토리지 업로드)packages/web/lib/components/detail/ImageDetailContent.tsx후속(별도 이슈로 쪼개기 권장)