# PRD: 커뮤니티 게시글 좋아요 상태 응답 추가 ## 1. Overview v2 API에서 커뮤니티 게시글 item을 내려줄 때 인증 조회자가 해당 게시글에 좋아요를 누른 상태를 함께 제공한다. --- ## 2. Problem - v2 커뮤니티 게시글 응답은 `likeCount`는 제공하지만, 조회자 본인의 좋아요 여부는 제공하지 않는다. - 앱 클라이언트는 게시글 카드의 좋아요 버튼 초기 상태를 알기 위해 별도 조회를 하거나 화면 진입 후 상태를 추정해야 한다. - 같은 커뮤니티 게시글이 홈 추천, 크리에이터 채널 홈, 크리에이터 채널 커뮤니티 탭에서 각각 다른 DTO로 노출되므로 누락 없이 동일한 의미의 필드를 추가해야 한다. --- ## 3. Goals - v2에서 커뮤니티 게시글 카드로 노출되는 모든 응답 item에 조회자의 좋아요 상태를 추가한다. - 신규 응답 필드 이름은 `isLiked`로 한다. - Kotlin Boolean 직렬화가 `liked`로 바뀌지 않도록 응답 DTO에는 `@JsonProperty("isLiked")`를 사용한다. - 좋아요 상태는 `CreatorCommunityLike.isActive == true`인 행을 기준으로 계산한다. - 인증 회원 기준으로 조회되는 크리에이터 채널 홈/커뮤니티 탭에서는 로그인한 조회자의 좋아요 여부를 반영한다. - 비회원도 호출 가능한 홈 추천 전체 응답에서는 비회원의 `isLiked`를 `false`로 내려준다. - 기존 `likeCount`, `commentCount`, `existOrdered`, 유료 콘텐츠 마스킹, 성인 콘텐츠 필터, 차단 정책은 변경하지 않는다. --- ## 4. Non-Goals - 커뮤니티 게시글 좋아요 생성/취소 API 동작은 변경하지 않는다. - `likeCount` 집계 기준은 변경하지 않는다. - legacy 커뮤니티 API 응답 스키마는 변경하지 않는다. - 팔로잉 뉴스의 `COMMUNITY_POST` news item에는 게시글 카드 필드가 없으므로 이번 범위에서 제외한다. - DB schema, 운영 DDL, 마이그레이션은 포함하지 않는다. - 추천 스냅샷 산정 로직과 인기 커뮤니티 정렬 정책은 변경하지 않는다. --- ## 5. Target Users - 회원: 커뮤니티 게시글 카드에서 본인이 누른 좋아요 상태를 즉시 확인하려는 사용자 - 앱 클라이언트: 게시글 카드 렌더링 시 좋아요 버튼의 초기 active 상태가 필요한 클라이언트 - 서버 개발자: v2 커뮤니티 게시글 응답 간 좋아요 상태 필드 의미를 일관되게 유지해야 하는 개발자 --- ## 6. 조사 결과 ### 대상 응답 item - `HomePopularCommunityPostItem` - 파일: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/home/dto/recommendation/HomeRecommendationResponse.kt` - 노출 API: `GET /api/v2/home/recommendations` - 현재 필드: `postId`, `creatorId`, `creatorNickname`, `creatorProfileImage`, `imageUrl`, `audioUrl`, `content`, `price`, `createdAt`, `likeCount`, `commentCount`, `existOrdered` - 데이터 흐름: `HomeRecommendationFacade`가 `HomePopularCommunityRecommendationRecord`를 변환한다. - `CreatorChannelCommunityPostResponse` in creator channel home - 파일: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/creator/channel/home/dto/CreatorChannelHomeResponse.kt` - 노출 API: `GET /api/v2/creator-channels/{creatorId}/home` - 현재 필드: `postId`, `creatorId`, `creatorNickname`, `creatorProfileUrl`, `imageUrl`, `audioUrl`, `content`, `price`, `dateUtc`, `existOrdered`, `likeCount`, `commentCount` - 데이터 흐름: `CreatorChannelHomeQueryService`가 `CreatorChannelCommunityQueryService.findHomeCommunityPosts(...)`를 호출하고, 공지/커뮤니티 섹션이 같은 domain post를 사용한다. - `CreatorChannelCommunityPostResponse` in creator channel community tab - 파일: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/creator/channel/community/dto/CreatorChannelCommunityTabResponse.kt` - 노출 API: `GET /api/v2/creator-channels/{creatorId}/community` - 현재 필드: `postId`, `creatorId`, `creatorNickname`, `creatorProfileUrl`, `createdAtUtc`, `content`, `imageUrl`, `audioUrl`, `price`, `isCommentAvailable`, `existOrdered`, `likeCount`, `commentCount`, `isPinned` - 데이터 흐름: `CreatorChannelCommunityQueryService.getCommunityTab(...)`가 `CreatorChannelCommunityPost`를 만들고 DTO가 이를 변환한다. ### 대상 하위 모델 - `CreatorChannelCommunityPost` - 파일: `src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/community/domain/CreatorChannelCommunityTab.kt` - 채널 홈과 커뮤니티 탭이 함께 사용하는 domain post다. - `CreatorChannelCommunityPostRecord` - 파일: `src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/community/port/out/CreatorChannelCommunityQueryPort.kt` - repository에서 조회한 게시글 row와 부가 상태를 service로 전달한다. - `HomePopularCommunityRecommendationRecord` - 파일: `src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/port/out/HomeRecommendationQueryPort.kt` - 홈 인기 커뮤니티 상세 조회 결과를 facade로 전달한다. ### 제외 대상 - `HomeFollowingNews` / `FollowingNewsResponse` - `FollowingNewsType.COMMUNITY_POST`와 `targetId`는 있지만 게시글 카드 DTO가 아니며 `likeCount`도 제공하지 않는다. --- ## 7. User Stories - 사용자는 홈 추천의 인기 커뮤니티 게시글을 볼 때 내가 이미 좋아요를 눌렀는지 바로 알고 싶다. - 사용자는 크리에이터 채널 홈의 공지/커뮤니티 게시글 카드에서 좋아요 버튼이 현재 상태대로 표시되기를 원한다. - 사용자는 크리에이터 채널 커뮤니티 탭 목록에서 각 게시글의 좋아요 상태가 정확하게 표시되기를 원한다. - 앱 클라이언트는 게시글 목록 응답만으로 좋아요 버튼의 selected 상태를 렌더링하고 싶다. --- ## 8. Core Features ### Feature A. 홈 인기 커뮤니티 게시글 좋아요 상태 #### Requirements - `HomePopularCommunityPostItem`에 `isLiked: Boolean`을 추가한다. - `HomePopularCommunityRecommendationRecord`에 조회자 좋아요 여부를 전달할 수 있는 Boolean 필드를 추가한다. - `DefaultHomeRecommendationQueryRepository.findPopularCommunityRecommendationDetails(...)`는 `memberId`가 null이면 `isLiked=false`를 반환한다. - `memberId`가 있으면 해당 회원의 활성 좋아요가 있는 게시글만 `isLiked=true`로 반환한다. - 홈 추천 전체 응답은 비회원 호출을 허용하므로 비회원 `isLiked=false`를 명시적으로 보장한다. #### Edge Cases - 비활성 좋아요는 `isLiked=false`다. - 다른 회원이 좋아요한 게시글은 현재 조회자의 `isLiked=true`가 되면 안 된다. - 좋아요 수가 1 이상이어도 현재 조회자가 좋아요하지 않았으면 `isLiked=false`다. ### Feature B. 크리에이터 채널 홈 게시글 좋아요 상태 #### Requirements - 크리에이터 채널 홈의 `CreatorChannelCommunityPostResponse`에 `isLiked: Boolean`을 추가한다. - `CreatorChannelCommunityPost` domain에 조회자 좋아요 여부를 추가한다. - `CreatorChannelCommunityPostRecord`에 조회자 좋아요 여부를 추가한다. - `DefaultCreatorChannelCommunityQueryRepository.findHomeCommunityPosts(...)`는 기존 id 목록 기반 부가 조회 패턴을 유지하고, 조회자 좋아요 게시글 id 목록을 bulk 조회한다. - 공지(`notices`)와 일반 커뮤니티(`communities`) 모두 같은 `isLiked` 의미를 사용한다. #### Edge Cases - 조회자가 게시글 작성자여도 본인이 활성 좋아요를 누른 이력이 없으면 `isLiked=false`다. - 유료 미구매로 이미지/오디오/본문이 마스킹되어도 좋아요 상태는 실제 조회자의 활성 좋아요 기준으로 내려준다. ### Feature C. 크리에이터 채널 커뮤니티 탭 게시글 좋아요 상태 #### Requirements - 커뮤니티 탭의 `CreatorChannelCommunityPostResponse`에 `isLiked: Boolean`을 추가한다. - `DefaultCreatorChannelCommunityQueryRepository.findCommunityPosts(...)`도 홈 조회와 동일한 조회자 좋아요 상태 계산을 적용한다. - page/size와 무관하게 응답에 포함된 각 게시글의 `isLiked`가 정확해야 한다. #### Edge Cases - 현재 page에 포함되지 않은 게시글의 좋아요 상태를 조회하거나 응답에 포함하지 않는다. - 좋아요가 없는 게시글은 `isLiked=false`다. --- ## 9. Technical Constraints - 기존 v2 패키지 경계를 유지한다. - 공개 API 응답 필드 추가 외에 기존 필드명과 의미를 변경하지 않는다. - 커뮤니티 좋아요 상태는 `CreatorCommunityLike`와 `creatorCommunityLike.isActive.isTrue`를 기준으로 한다. - 채널 홈/커뮤니티 탭은 이미 `postIds` 기반으로 `likeCount`, `commentCount`, `existOrdered`를 bulk 조회하므로 같은 패턴의 `likedCommunityPostIds(viewerId, postIds)` helper를 추가하는 방향을 우선 검토한다. - 홈 인기 커뮤니티는 QueryDSL projection으로 record를 생성하므로 `isLiked` 계산식 또는 별도 subquery를 projection에 추가하는 방향을 우선 검토한다. - Boolean 응답 필드는 Jackson 직렬화 이름 보존을 위해 `@JsonProperty("isLiked")`를 명시한다. - 신규 필드 추가에 따른 테스트 fixture와 controller JSON assertion을 갱신한다. --- ## 10. Success Criteria - `GET /api/v2/home/recommendations`의 `popularCommunityPosts[*].isLiked`가 인증 회원 기준 활성 좋아요 여부를 반환한다. - `GET /api/v2/home/recommendations`를 비회원으로 호출하면 `popularCommunityPosts[*].isLiked=false`를 반환한다. - `GET /api/v2/creator-channels/{creatorId}/home`의 `notices[*].isLiked`, `communities[*].isLiked`가 인증 조회자 기준 활성 좋아요 여부를 반환한다. - `GET /api/v2/creator-channels/{creatorId}/community`의 `communityPosts[*].isLiked`가 인증 조회자 기준 활성 좋아요 여부를 반환한다. - 비활성 좋아요, 다른 회원의 좋아요, 좋아요 수와 조회자 좋아요 여부가 다른 케이스를 테스트로 구분한다. - 기존 좋아요 수, 댓글 수, 구매 여부, 유료 콘텐츠 마스킹, 성인 콘텐츠 필터 테스트가 계속 통과한다. --- ## 11. Open Questions - 없음. 필드명은 요청 의미와 기존 Boolean 응답 패턴을 기준으로 `isLiked`로 확정한다.