Skip to content

feat(social): 댓글/대댓글, 좋아요 기능 및 신고, 소셜 정지 기능 구현#167

Merged
coldsunn merged 11 commits into
developfrom
feat/social
May 18, 2026
Merged

feat(social): 댓글/대댓글, 좋아요 기능 및 신고, 소셜 정지 기능 구현#167
coldsunn merged 11 commits into
developfrom
feat/social

Conversation

@coldsunn
Copy link
Copy Markdown
Collaborator

@coldsunn coldsunn commented May 18, 2026

📝 요약(Summary)

친구 피드 기반 소셜 기능(댓글/대댓글·좋아요·신고 개편)을 구현했습니다.

  1. 댓글/대댓글
  • comments 테이블: daily_report_id, author_id, parent_comment_id(null=댓글, non-null=대댓글), content, is_secret, deleted_at
  • 2레벨 구조(댓글-대댓글)만 지원. 대댓글의 부모는 반드시 top-level 댓글이어야하며 아닐 경우 COMMENT_NOT_TOP_LEVEL 반환

API

Method: GET
Path: /api/v1/comments?dailyReportId={id}
설명: 댓글 목록 (커서 기반, 최신순)
────────────────────────────────────────
Method: POST
Path: /api/v1/comments
설명: 댓글 작성
────────────────────────────────────────
Method: GET
Path: /api/v1/comments/{commentId}/sub-comments
설명: 대댓글 목록 (커서 기반)
────────────────────────────────────────
Method: POST
Path: /api/v1/comments/{commentId}/sub-comments
설명: 대댓글 작성
────────────────────────────────────────
Method: PATCH
Path: /api/v1/comments/{commentId}
설명: 내용 수정 (작성자 본인만, isSecret 변경 불가)
────────────────────────────────────────
Method: DELETE
Path: /api/v1/comments/{commentId}
설명: 삭제 (작성자 본인 또는 리포트 당사자)

접근 제어

  • 본인 리포트: 항상 허용
  • 타인 리포트: isShared=true AND 친구 관계(ACCEPTED) 두 조건 모두 충족 필요

비밀 댓글

  • 열람 권한: 댓글 작성자 + 리포트 당사자. 대댓글은 부모 댓글 작성자도 추가 허용
  • 권한 없는 경우 canViewContent=false 플래그로 내려줌 (프로필, 닉네임, 내용 마스킹은 프론트 처리)
  • 비밀 댓글 하위 대댓글은 무조건 비밀 강제
  • 비밀 부모 댓글: 열람 권한 없으면 대댓글 목록 조회/작성 403 반환

조회 필터링 (getExcludedUserIds)

  • 내가 차단한 / 나를 차단한 사용자
  • 소셜 정지 중인 사용자
  • 탈퇴한 사용자 (author.deletedAt is null 쿼리 조건)
  • 내가 신고한 댓글 (NOT EXISTS ContentReport 서브쿼리)

visibleSubCommentCount
전체 대댓글 수에서 위 필터 대상 + 열람 권한 없는 비밀 대댓글을 제외한 값. 단일 집계 쿼리(countVisibleSubCommentsByParentIds)로 처리

소프트딜리트

  • 댓글 삭제 시 대댓글 Modifying(clearAutomatically = true) bulk UPDATE로 일괄 소프트딜리트
  • 신고 이력 남겨놓기 위해서 소프트딜리트 채택

알림 (즉시 FCM)

  • 내 리포트에 댓글 달림 → COMMENT_ON_MY_REPORT
  • 내 댓글에 대댓글 달림 → REPLY_ON_MY_COMMENT
  • 내가 참여한 댓글에 다른 대댓글 달림 → REPLY_ON_PARTICIPATED_COMMENT
  • targetId로 dailyReportId 전달

피드 응답 변경
GET /api/v1/feed 응답에 myReport(오늘 내 공유 리포트, 미공유 시 null) 필드 추가


  1. 좋아요

API

Method: POST
Path: /api/v1/feed/{dailyReportId}/likes
설명: 게시글 좋아요
────────────────────────────────────────
Method: DELETE
Path: /api/v1/feed/{dailyReportId}/likes
설명: 게시글 좋아요 취소
────────────────────────────────────────
Method: GET
Path: /api/v1/feed/{dailyReportId}/likes
설명: 게시글 좋아요 리스트 (리포트 당사자만)
────────────────────────────────────────
Method: POST
Path: /api/v1/comments/{commentId}/likes
설명: 댓글/대댓글 좋아요
────────────────────────────────────────
Method: DELETE
Path: /api/v1/comments/{commentId}/likes
설명: 댓글/대댓글 좋아요 취소
────────────────────────────────────────
Method: GET
Path: /api/v1/comments/{commentId}/likes
설명: 댓글/대댓글 좋아요 리스트

제약 조건

  • 본인 게시글/댓글/대댓글 좋아요 불가 → CANNOT_LIKE_OWN_CONTENT(400)
  • 중복 좋아요 멱등 처리 (이미 좋아요한 경우 에러 없이 204 반환)
  • 비밀 대댓글 좋아요/좋아요 리스트: 열람 권한자(작성자·리포트 당사자·부모 댓글 작성자)만 허용
  • unlike는 정지 체크 포함

좋아요 리스트 응답
최신순, 차단 관계 양방향 + 정지 유저 제외. isFriend 플래그로 친구 여부 전달(친구면 삭제/차단, 아니면 친구 신청 버튼)

피드·댓글 응답 변경

  • 피드 항목, 댓글/대댓글 응답에 isLiked, hasLikes 필드 추가

  1. 신고 개편 + 소셜 정지

DB 변경

  • content_reports: comment_id 컬럼 추가(nullable), daily_report_id NOT NULL → nullable, fk_content_reports_daily_report ON DELETE CASCADE → ON DELETE SET NULL (게시글 삭제 후에도 신고 이력 보존), partial unique index 2개로 중복 신고 방지
  • social_suspensions 신규 테이블: user_id, started_at, expires_at

신고 API 변경
POST /api/v1/moderation/reports에 commentId 추가 (nullable). dailyReportId / commentId 중 하나 필수. 게시글·댓글 신고 단일 테이블 통합

소셜 정지 자동 발동

  • 조건: 가장 최근 정지 만료 이후 신고 20건 이상 + 신고자 3명 이상
  • 발동 시 단일 트랜잭션: 정지 레코드 생성 + 오늘 공유 게시글 일괄 비공개
  • 만료 체크 Lazy (API 호출 시 expiresAt > NOW() 확인). 레코드 삭제 금지 (다음 정지 집계 기준점으로 사용)

정지 중 차단 API
댓글 작성·수정·삭제, 게시글/댓글 좋아요·취소 → SOCIAL_SUSPENDED(400)

신규 API
GET /api/v1/moderation/suspension/status — 정지 여부 및 해제일 반환 (팝업용)

🔗 Related Issue

  • Closes:

💬 공유사항

  • isLiked / hasLikes / myReport 신규 필드: 구버전 앱은 JSON unknown field를 무시하므로 하위 호환 문제 없습니다.
  • 이외에도 다른 API 변경점들에 대해서도 호환 문제는 발생하지 않을 것 같습니다.
  • 리포트 상세보기 API에는 isLiked / hasLikes 필드 추가 안했습니다. 어짜피 내 예전 리포트 보는데 좋아요 리스트 조회하면 되니까 굳이 좋아요가 1개 이상인지와 같은 정보는 필요 없다고 생각했습니다.

✅ PR Checklist

PR이 다음 요구 사항을 충족하는지 확인하세요.

  • PR 제목을 커밋 메시지 컨벤션에 맞게 작성했습니다.

coldsunn added 11 commits May 18, 2026 12:54
daily_report_id, author_id, parent_comment_id FK 구성, 댓글/대댓글 구조, 리포지토리 구현
댓글/대댓글 CRUD API 구현, 비밀 댓글 열람 권한 및 마스킹 처리, 차단·탈퇴 유저 필터링, visibleSubCommentCount 계산
피드 응답에 내 공유 리포트(myReport) 필드 추가
COMMENT_ON_MY_REPORT, REPLY_ON_MY_COMMENT, REPLY_ON_PARTICIPATED_COMMENT 알림 타입 추가, 알림 대상 탈퇴 유저 필터링,
FriendNotificationEventListener 패키지 friend → social 이동
삭제된 댓글도 유지하는 것이 추후 확장 시에 유리할 것이라 생각해 물리 삭제에서 소프트 딜리트로 전환
daily_report_likes, comment_likes 테이블 마이그레이션 추가, 게시글·댓글/대댓글 좋아요·취소·리스트 API 구현, CommentResponse,
FeedResponse에 isLiked·hasLikes 필드 추가, FeedListResponse에 myReport 필드 추가
social_suspensions 테이블 추가 및 content_reports 테이블에 comment_id 컬럼 추가, daily_report_id FK를 ON DELETE SET
NULL로 변경해 게시글 삭제 후에도 신고 이력 보존, 기존 SharingSuspensionService를 social_suspensions 기반으로 재구현, 신고 누적 조건
만족시 정지 자동 발동, 오늘 공유 게시글 일괄 비공개 처리, 정지 상태 조회 API 추가
CommentCommandService·LikeCommandService의 write 메서드 전체에 소셜 정지 상태인지 체크하는 메서드 추가,
CommentQueryService·LikeQueryService의 getExcludedUserIds에 지 중인 유저 ID를 포함해 조회 시 자동
필터링, CommentRepository 댓글·대댓글·서브카운트 쿼리 3개에 NOT EXISTS 서브쿼리 추가하여 내가 신고한 댓글 미노출
countAllReports / countAllDistinctReporters 새 메서드 추가, @Modifying(clearAutomatically = true) 추가,
deleteComment Swagger에 409 COMMENT_DELETED 추가
@coldsunn coldsunn merged commit f860c0d into develop May 18, 2026
1 check passed
@coldsunn coldsunn deleted the feat/social branch May 18, 2026 15:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant