Skip to content

feat: 기존 피드 조회 API 4종에 카운트 필드 추가#388

Merged
KoSeonJe merged 7 commits intodevelopfrom
feat/DDING-000-feed-modify-existing
Feb 23, 2026
Merged

feat: 기존 피드 조회 API 4종에 카운트 필드 추가#388
KoSeonJe merged 7 commits intodevelopfrom
feat/DDING-000-feed-modify-existing

Conversation

@KoSeonJe
Copy link
Copy Markdown
Collaborator

@KoSeonJe KoSeonJe commented Feb 23, 2026

🚀 작업 내용

기존 피드 조회 API 4종에 좋아요/댓글/조회수 관련 필드를 추가합니다.

수정 대상 API

Method URL 추가 필드
GET /server/feeds viewCount, likeCount, commentCount
GET /server/feeds/{feedId} likeCount, commentCount, comments[]
GET /server/clubs/{clubId}/feeds viewCount, likeCount, commentCount
GET /server/central/my/feeds feedCount, totalViewCount

주요 변경사항

  • 피드 목록 조회 시 각 피드의 조회수, 좋아요 수, 댓글 수를 함께 반환
  • 피드 상세 조회 시 좋아요/댓글 수와 댓글 목록을 함께 반환
  • 동아리 피드 현황 조회 시 전체 피드 수와 총 조회수를 집계하여 반환
  • 목록 API의 좋아요/댓글 카운트는 벌크 쿼리로 한 번에 조회하여 N+1 문제 방지
  • 피드가 없는 경우에도 집계 값이 null 대신 0을 반환하도록 쿼리 보정
  • 동아리별 피드 목록 응답에서 불필요한 썸네일 파일명 필드 제거
  • 테스트 공통 설정을 상위 클래스로 통합하고, 테스트명을 요구사항 중심으로 개선
  • 피드 집계 쿼리 및 좋아요/댓글/서비스 통합 테스트 추가

🤔 고민했던 내용

  • 목록 API에서 피드마다 좋아요/댓글을 개별 조회하면 N+1이 발생하므로, feedId 리스트 기반 벌크 조회 후 Map으로 변환하여 한 번에 주입하는 방식 채택
  • 커서 기반 페이지네이션에서 현재 페이지가 비어 있어도 전체 집계 데이터는 유지해야 하므로, 빈 리스트 반환 시에도 stat 값을 보존하도록 처리

💬 리뷰 중점사항

  • 벌크 카운트 쿼리에 빈 리스트가 전달될 때의 동작 확인
  • Facade Service에서 Repository를 직접 의존하는 부분 — Facade 패턴 위반 여부 논의 필요
  • 피드 상세 조회 시 댓글 수와 댓글 목록 크기의 일관성

KoSeonJe and others added 4 commits February 23, 2026 16:03
- FeedCountDto: feedId별 카운트 조회용 interface projection
- MyFeedStatDto: 내 피드 통계(feedCount, totalViewCount, imageCount, videoCount) projection
- FeedLikeRepository.countsByFeedIds(): N+1 방지 벌크 좋아요 카운트
- FeedCommentRepository.countsByFeedIds(): N+1 방지 벌크 댓글 카운트 (soft delete 필터링)
- FeedRepository.findMyFeedStat(): clubId 기반 피드 집계 쿼리

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- FeedListQuery: viewCount, likeCount, commentCount 필드 추가 + 미사용 of() 제거
- FeedQuery: likeCount, commentCount, List<FeedCommentQuery> comments 필드 추가
- MyFeedPageQuery: feedCount, totalViewCount, imageCount, videoCount 추가 + @builder 적용
- FeedFileService.extractFeedThumbnailInfo(): 생성자 → builder 패턴 전환, viewCount 포함

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- FacadeFeedService: buildFeedListQueriesWithCounts()로 목록 API 벌크 카운트 주입
- FacadeFeedService.getById(): likeCount, commentCount, comments 조회 추가
- FacadeClubFeedServiceImpl.getMyFeedPage(): findMyFeedStat() 집계 + null 시 stat 유지

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- FeedPageResponse: viewCount, likeCount, commentCount 추가
- ClubFeedPageResponse: viewCount, likeCount, commentCount 추가 + thumbnailFilename 누락 버그 수정
- FeedResponse: likeCount, commentCount, CommentResponse 내부 record + comments 목록 추가
- MyFeedPageResponse: feedCount, totalViewCount, imageCount, videoCount 집계 필드 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 23, 2026

Warning

Rate limit exceeded

@KoSeonJe has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 14 minutes and 37 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between e3c0fe8 and ed2b9f4.

📒 Files selected for processing (6)
  • src/main/java/ddingdong/ddingdongBE/domain/feed/controller/dto/response/ClubFeedPageResponse.java
  • src/main/java/ddingdong/ddingdongBE/domain/feed/controller/dto/response/MyFeedPageResponse.java
  • src/test/java/ddingdong/ddingdongBE/common/support/DataJpaTestSupport.java
  • src/test/java/ddingdong/ddingdongBE/domain/feed/repository/FeedCommentRepositoryTest.java
  • src/test/java/ddingdong/ddingdongBE/domain/feed/repository/FeedLikeRepositoryTest.java
  • src/test/java/ddingdong/ddingdongBE/domain/feed/repository/FeedRepositoryTest.java

Walkthrough

피드 도메인에 조회수·좋아요·댓글 집계가 추가되었습니다. 리포지토리에서 집계 쿼리(피드별/클럽별)를 제공하고, 서비스는 이를 병합해 확장된 FeedList/Feed/마이피드 페이징 쿼리·응답(댓글 목록 포함)을 반환합니다.

Changes

Cohort / File(s) Summary
Repository 계층 - 집계 쿼리
src/main/java/ddingdong/ddingdongBE/domain/feed/repository/FeedLikeRepository.java, src/main/java/ddingdong/ddingdongBE/domain/feed/repository/FeedCommentRepository.java, src/main/java/ddingdong/ddingdongBE/domain/feed/repository/FeedRepository.java
피드별 좋아요·댓글 개수 반환용 countsByFeedIds(...) 네이티브 쿼리 메서드와 클럽별 피드 통계 반환 findMyFeedStat(...) 추가.
Repository DTO 프로젝션
src/main/java/ddingdong/ddingdongBE/domain/feed/repository/dto/FeedCountDto.java, .../MyFeedStatDto.java
페이드별 카운트 및 마이피드 통계용 프로젝션 인터페이스 추가(필드 접근자 메서드 선언).
서비스/도메인 쿼리 DTO 변경
src/main/java/ddingdong/ddingdongBE/domain/feed/service/dto/query/FeedListQuery.java, .../FeedQuery.java, .../MyFeedPageQuery.java
viewCount, likeCount, commentCount 필드 추가. FeedListQuery.of(...) 제거, FeedQuery.of(...)·MyFeedPageQuery.of(...) 시그니처/빌더 업데이트(통계 포함).
서비스 계층 로직 변경
src/main/java/ddingdong/ddingdongBE/domain/feed/service/FacadeFeedService.java, src/main/java/ddingdong/ddingdongBE/domain/feed/service/FacadeClubFeedServiceImpl.java, src/main/java/ddingdong/ddingdongBE/domain/feed/service/FeedFileService.java
FeedLike/FeedComment 리포지토리에서 ID별 카운트를 조회해 FeedListQuery에 병합하는 buildFeedListQueriesWithCounts() 도입 및 MyFeedStatDto 연계로 MyFeedPage 구성 로직 확장. FeedFileService는 FeedListQuery 빌더 사용으로 내부 구성 변경.
컨트롤러 응답 DTO 변경
src/main/java/ddingdong/ddingdongBE/domain/feed/controller/dto/response/ClubFeedPageResponse.java, .../FeedPageResponse.java, .../FeedResponse.java, .../MyFeedPageResponse.java
응답 스키마에 viewCount/likeCount/commentCount 추가. FeedResponseCommentResponse 중첩 레코드 및 댓글 리스트 포함. MyFeedPageResponse 등에 빌더 적용.
테스트 추가/확장
src/test/java/ddingdong/ddingdongBE/domain/feed/repository/FeedCommentRepositoryTest.java, .../FeedLikeRepositoryTest.java, .../FeedRepositoryTest.java, .../FacadeFeedServiceTest.java
리포지토리 집계 메서드 단위/통합 테스트 추가 및 FacadeFeedService 테스트 확장(좋아요·댓글 시나리오).

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client as Client
    participant Ctrl as FeedController
    participant Facade as FacadeFeedService
    participant FeedRepo as FeedRepository/DB
    participant LikeRepo as FeedLikeRepository
    participant CommentRepo as FeedCommentRepository

    Client->>Ctrl: GET /feeds (요청)
    Ctrl->>Facade: getFeedPageByClubId(...)
    Facade->>FeedRepo: fetch feeds & paging
    Facade->>LikeRepo: countsByFeedIds(feedIds)
    Facade->>CommentRepo: countsByFeedIds(feedIds)
    Note right of Facade: 매핑 및 ID별 카운트 병합\n(buildFeedListQueriesWithCounts)
    Facade->>FeedRepo: findMyFeedStat(clubId)
    Facade->>Ctrl: 구성된 FeedPage/MyFeedPage 응답 반환
    Ctrl->>Client: HTTP 200 응답 (view/like/comment 포함)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

✨기능, D-3

Suggested reviewers

  • wonjunYou
  • 5uhwann
  • Seooooo24
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 변경 사항의 주요 내용을 명확하고 간결하게 설명합니다. 4종의 피드 조회 API에 카운트 필드를 추가하는 것이 핵심 변경 사항이며, 이를 정확히 반영하고 있습니다.
Description check ✅ Passed PR 설명이 저장소의 필수 템플릿을 충실히 따르고 있습니다. 작업 내용(수정 대상 API 테이블 포함), 주요 변경사항, 고민했던 내용, 리뷰 중점사항이 모두 포함되어 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/DDING-000-feed-modify-existing

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/ddingdong/ddingdongBE/domain/feed/service/FacadeFeedService.java (1)

60-70: ⚠️ Potential issue | 🟡 Minor

상세 조회 시 commentCountcomments.size() 간 불일치 가능성.

commentCountfeedCommentService.countByFeedId()로 별도 조회하고, commentsfeedCommentService.getAllByFeedId()로 조회합니다. 두 쿼리 사이에 댓글이 추가/삭제되면 commentCount != comments.size()가 될 수 있습니다.

commentCount를 별도로 쿼리하지 않고 comments.size()를 사용하는 것이 일관성 측면에서 더 안전합니다.

♻️ 제안
     public FeedQuery getById(Long feedId) {
         feedService.incrementViewCount(feedId);
         Feed feed = feedService.getById(feedId);
         ClubProfileQuery clubProfileQuery = feedFileService.extractClubInfo(feed.getClub());
         FeedFileInfoQuery feedFileInfoQuery = feedFileService.extractFeedFileInfo(feed);
         long likeCount = feedLikeService.countByFeedId(feedId);
-        long commentCount = feedCommentService.countByFeedId(feedId);
         List<FeedCommentQuery> comments = feedCommentService.getAllByFeedId(feedId);
-        return FeedQuery.of(feed, clubProfileQuery, feedFileInfoQuery, likeCount, commentCount, comments);
+        return FeedQuery.of(feed, clubProfileQuery, feedFileInfoQuery, likeCount, comments.size(), comments);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/ddingdong/ddingdongBE/domain/feed/service/FacadeFeedService.java`
around lines 60 - 70, In getById, avoid the race between
feedCommentService.countByFeedId(...) and feedCommentService.getAllByFeedId(...)
by removing the separate count call and deriving commentCount from the retrieved
comments list (i.e., set commentCount = comments.size()); update the code that
builds the return value (FeedQuery.of(...)) to use that computed commentCount
instead of the removed count variable so getById, comments, and FeedQuery.of
remain consistent.
🧹 Nitpick comments (4)
src/main/java/ddingdong/ddingdongBE/domain/feed/service/FacadeFeedService.java (2)

87-98: FeedListQuery 필드 수동 복사는 유지보수에 취약합니다.

FeedListQuery에 새 필드가 추가될 경우 이 빌더 코드에서 누락될 수 있습니다. FeedListQuery에 카운트를 주입하는 withCounts(long likeCount, long commentCount) 같은 팩토리 메서드를 추가하면 복사 로직을 DTO 내부에 캡슐화할 수 있습니다.

♻️ FeedListQuery에 withCounts 메서드 추가 예시

FeedListQuery에 다음 메서드를 추가:

public FeedListQuery withCounts(long likeCount, long commentCount) {
    return FeedListQuery.builder()
            .id(this.id)
            .thumbnailCdnUrl(this.thumbnailCdnUrl)
            .thumbnailOriginUrl(this.thumbnailOriginUrl)
            .thumbnailFileName(this.thumbnailFileName)
            .feedType(this.feedType)
            .viewCount(this.viewCount)
            .likeCount(likeCount)
            .commentCount(commentCount)
            .build();
}

그리고 buildFeedListQueriesWithCounts에서:

         return feedListQueries.stream()
-                .map(q -> FeedListQuery.builder()
-                        .id(q.id())
-                        .thumbnailCdnUrl(q.thumbnailCdnUrl())
-                        .thumbnailOriginUrl(q.thumbnailOriginUrl())
-                        .feedType(q.feedType())
-                        .thumbnailFileName(q.thumbnailFileName())
-                        .viewCount(q.viewCount())
-                        .likeCount(likeCountMap.getOrDefault(q.id(), 0L))
-                        .commentCount(commentCountMap.getOrDefault(q.id(), 0L))
-                        .build())
+                .map(q -> q.withCounts(
+                        likeCountMap.getOrDefault(q.id(), 0L),
+                        commentCountMap.getOrDefault(q.id(), 0L)))
                 .toList();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/ddingdong/ddingdongBE/domain/feed/service/FacadeFeedService.java`
around lines 87 - 98, The manual field-by-field reconstruction of FeedListQuery
in buildFeedListQueriesWithCounts is brittle; add a factory method on
FeedListQuery—e.g., withCounts(long likeCount, long commentCount)—that returns a
new FeedListQuery preserving all existing fields but overriding
likeCount/commentCount, then replace the mapping in
FacadeFeedService.buildFeedListQueriesWithCounts to call
q.withCounts(likeCountMap.getOrDefault(q.id(),0L),
commentCountMap.getOrDefault(q.id(),0L)) so the DTO encapsulates copy logic and
future fields aren’t missed.

15-15: 사용되지 않는 import.

java.util.Collections는 이 파일에서 사용되지 않습니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/ddingdong/ddingdongBE/domain/feed/service/FacadeFeedService.java`
at line 15, Remove the unused import java.util.Collections from the
FacadeFeedService class; locate the import statement near the top of
FacadeFeedService (the line with "import java.util.Collections;") and delete it
so the file no longer contains that unused import.
src/main/java/ddingdong/ddingdongBE/domain/feed/controller/dto/response/ClubFeedPageResponse.java (1)

24-56: FeedPageResponse.FeedListResponse와 구조가 거의 동일합니다.

ClubFeedListResponseFeedPageResponse.FeedListResponse가 같은 필드 구성과 매핑 로직을 갖고 있습니다. 향후 필드 추가 시 양쪽 모두 수정해야 하므로, 공통 응답 레코드를 추출하는 것을 고려해볼 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/ddingdong/ddingdongBE/domain/feed/controller/dto/response/ClubFeedPageResponse.java`
around lines 24 - 56, ClubFeedListResponse duplicates
FeedPageResponse.FeedListResponse fields and mapping; extract a single shared
record (e.g., FeedListResponseRecord) containing id, thumbnailCdnUrl,
thumbnailOriginUrl, thumbnailFilename, feedType, viewCount, likeCount,
commentCount and a static from(FeedListQuery) factory, place it in a common dto
package, then change both ClubFeedListResponse and
FeedPageResponse.FeedListResponse to either reuse that shared record directly or
delegate to it (replace their builder/from logic to call
FeedListResponseRecord.from and map/return that instance) so future field
additions require one change.
src/main/java/ddingdong/ddingdongBE/domain/feed/controller/dto/response/FeedResponse.java (1)

108-124: query.comments() null 체크는 방어적이지만, 상위에서 보장하는 것이 더 좋습니다.

현재 FeedQuery.of()에서 comments를 그대로 전달하므로, 호출자가 null을 넘기면 여기서 빈 리스트로 대체됩니다. 가능하다면 FeedQuery.of() 내부에서 comments == null ? List.of() : comments로 보장하면 하위 코드의 방어 로직이 불필요해집니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/ddingdong/ddingdongBE/domain/feed/controller/dto/response/FeedResponse.java`
around lines 108 - 124, FeedResponse.from currently defensively null-checks
query.comments(), but the construct should be enforced at the source: update
FeedQuery.of (the factory/constructor that builds FeedQuery) to normalize
comments by replacing a null comments parameter with an empty List (e.g.,
comments == null ? List.of() : comments) so callers of FeedResponse.from can
assume query.comments() is non-null and you can remove the defensive
null-handling in FeedResponse.from; ensure the change targets FeedQuery.of and
keep the public API of FeedQuery.comments() unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/main/java/ddingdong/ddingdongBE/domain/feed/repository/FeedRepository.java`:
- Around line 103-112: The aggregation query in findMyFeedStat is missing
COALESCE for imageCount and videoCount so SUM(CASE ...) can return NULL for
clubs with no feeds; update the nativeQuery to wrap both SUM(CASE WHEN
f.feed_type = 'IMAGE' THEN 1 ELSE 0 END) and SUM(CASE WHEN f.feed_type = 'VIDEO'
THEN 1 ELSE 0 END) with COALESCE(..., 0) so MyFeedStatDto getters (and
downstream MyFeedPageQuery which may unbox to primitive long) always receive 0
instead of null.

In
`@src/main/java/ddingdong/ddingdongBE/domain/feed/service/dto/query/MyFeedPageQuery.java`:
- Around line 18-28: MyFeedPageQuery.of currently passes stat.getImageCount()
and stat.getVideoCount() (which can be null) into primitive long fields causing
NPEs; fix by guarding against nulls in the of() method (e.g., use a
null-coalescing default 0L when reading
stat.getImageCount()/stat.getVideoCount()) or alternatively update the SQL to
use COALESCE around the SUMs so stat.getImageCount()/getVideoCount() never
return null; modify the MyFeedPageQuery.of implementation to use the chosen
approach and ensure the builder receives non-null long values for imageCount and
videoCount.

In
`@src/main/java/ddingdong/ddingdongBE/domain/feed/service/FacadeClubFeedServiceImpl.java`:
- Line 42: Replace the direct FeedRepository dependency in
FacadeClubFeedServiceImpl with a FeedService dependency: remove or replace the
private final FeedRepository feedRepository field and constructor parameter with
FeedService feedService, and update all usages of
feedRepository.findMyFeedStat(...) to delegate to
feedService.findMyFeedStat(...). This keeps the facade from depending on the
repository directly and mirrors the fix already used in FacadeFeedService.

In
`@src/main/java/ddingdong/ddingdongBE/domain/feed/service/FacadeFeedService.java`:
- Around line 33-34: FacadeFeedService currently depends directly on
FeedLikeRepository and FeedCommentRepository which breaks DDD layering and is
inconsistent with using FeedLikeService/FeedCommentService elsewhere; remove the
FeedLikeRepository and FeedCommentRepository fields from FacadeFeedService and
delegate the bulk count work to new methods countsByFeedIds(List<Long>) on
FeedLikeService and FeedCommentService (add these service methods and have them
call their repositories internally), then update the FacadeFeedService code
paths that used the repositories (the list retrieval around the existing
repository calls) to call FeedLikeService.countsByFeedIds(...) and
FeedCommentService.countsByFeedIds(...) instead.

---

Outside diff comments:
In
`@src/main/java/ddingdong/ddingdongBE/domain/feed/service/FacadeFeedService.java`:
- Around line 60-70: In getById, avoid the race between
feedCommentService.countByFeedId(...) and feedCommentService.getAllByFeedId(...)
by removing the separate count call and deriving commentCount from the retrieved
comments list (i.e., set commentCount = comments.size()); update the code that
builds the return value (FeedQuery.of(...)) to use that computed commentCount
instead of the removed count variable so getById, comments, and FeedQuery.of
remain consistent.

---

Duplicate comments:
In
`@src/main/java/ddingdong/ddingdongBE/domain/feed/repository/FeedLikeRepository.java`:
- Around line 18-24: The countsByFeedIds method in FeedLikeRepository can fail
when given an empty feedIds list (causing an IN () SQL error); mirror the fix
used in FeedCommentRepository by short-circuiting when feedIds is null or empty
and returning an empty List before invoking the repository query. Locate
FeedLikeRepository.countsByFeedIds and add the defensive check (return
Collections.emptyList() or equivalent) so the native query is never called with
an empty collection.

---

Nitpick comments:
In
`@src/main/java/ddingdong/ddingdongBE/domain/feed/controller/dto/response/ClubFeedPageResponse.java`:
- Around line 24-56: ClubFeedListResponse duplicates
FeedPageResponse.FeedListResponse fields and mapping; extract a single shared
record (e.g., FeedListResponseRecord) containing id, thumbnailCdnUrl,
thumbnailOriginUrl, thumbnailFilename, feedType, viewCount, likeCount,
commentCount and a static from(FeedListQuery) factory, place it in a common dto
package, then change both ClubFeedListResponse and
FeedPageResponse.FeedListResponse to either reuse that shared record directly or
delegate to it (replace their builder/from logic to call
FeedListResponseRecord.from and map/return that instance) so future field
additions require one change.

In
`@src/main/java/ddingdong/ddingdongBE/domain/feed/controller/dto/response/FeedResponse.java`:
- Around line 108-124: FeedResponse.from currently defensively null-checks
query.comments(), but the construct should be enforced at the source: update
FeedQuery.of (the factory/constructor that builds FeedQuery) to normalize
comments by replacing a null comments parameter with an empty List (e.g.,
comments == null ? List.of() : comments) so callers of FeedResponse.from can
assume query.comments() is non-null and you can remove the defensive
null-handling in FeedResponse.from; ensure the change targets FeedQuery.of and
keep the public API of FeedQuery.comments() unchanged.

In
`@src/main/java/ddingdong/ddingdongBE/domain/feed/service/FacadeFeedService.java`:
- Around line 87-98: The manual field-by-field reconstruction of FeedListQuery
in buildFeedListQueriesWithCounts is brittle; add a factory method on
FeedListQuery—e.g., withCounts(long likeCount, long commentCount)—that returns a
new FeedListQuery preserving all existing fields but overriding
likeCount/commentCount, then replace the mapping in
FacadeFeedService.buildFeedListQueriesWithCounts to call
q.withCounts(likeCountMap.getOrDefault(q.id(),0L),
commentCountMap.getOrDefault(q.id(),0L)) so the DTO encapsulates copy logic and
future fields aren’t missed.
- Line 15: Remove the unused import java.util.Collections from the
FacadeFeedService class; locate the import statement near the top of
FacadeFeedService (the line with "import java.util.Collections;") and delete it
so the file no longer contains that unused import.

private final VodProcessingJobService vodProcessingJobService;
private final SseConnectionService sseConnectionService;
private final FeedFileService feedFileService;
private final FeedRepository feedRepository;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

FeedRepository 직접 의존 — FeedService를 통해 접근해야 합니다.

FacadeFeedService와 동일한 문제입니다. findMyFeedStat()FeedService에 위임하면 Facade가 Repository에 직접 의존하지 않게 됩니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/ddingdong/ddingdongBE/domain/feed/service/FacadeClubFeedServiceImpl.java`
at line 42, Replace the direct FeedRepository dependency in
FacadeClubFeedServiceImpl with a FeedService dependency: remove or replace the
private final FeedRepository feedRepository field and constructor parameter with
FeedService feedService, and update all usages of
feedRepository.findMyFeedStat(...) to delegate to
feedService.findMyFeedStat(...). This keeps the facade from depending on the
repository directly and mirrors the fix already used in FacadeFeedService.

Comment on lines +33 to +34
private final FeedLikeRepository feedLikeRepository;
private final FeedCommentRepository feedCommentRepository;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Facade에서 Repository 직접 의존은 DDD 레이어링 위반입니다.

FacadeFeedServiceFeedLikeRepositoryFeedCommentRepository를 직접 주입받고 있습니다. Line 66-68의 상세 조회에서는 FeedLikeService/FeedCommentService를 통해 접근하면서, Line 82-85의 목록 조회에서는 Repository를 직접 사용하는 것은 일관성이 없습니다.

벌크 카운트 메서드(countsByFeedIds)를 각 도메인 서비스(FeedLikeService, FeedCommentService)로 위임하면 Repository 의존을 제거할 수 있습니다.

♻️ 제안
 public class FacadeFeedService {

     private final FeedService feedService;
     private final FeedFileService feedFileService;
     private final FeedLikeService feedLikeService;
     private final FeedCommentService feedCommentService;
-    private final FeedLikeRepository feedLikeRepository;
-    private final FeedCommentRepository feedCommentRepository;

그리고 FeedLikeService/FeedCommentService에 벌크 카운트 메서드를 추가:

// FeedLikeService
List<FeedCountDto> countsByFeedIds(List<Long> feedIds);

// FeedCommentService
List<FeedCountDto> countsByFeedIds(List<Long> feedIds);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/ddingdong/ddingdongBE/domain/feed/service/FacadeFeedService.java`
around lines 33 - 34, FacadeFeedService currently depends directly on
FeedLikeRepository and FeedCommentRepository which breaks DDD layering and is
inconsistent with using FeedLikeService/FeedCommentService elsewhere; remove the
FeedLikeRepository and FeedCommentRepository fields from FacadeFeedService and
delegate the bulk count work to new methods countsByFeedIds(List<Long>) on
FeedLikeService and FeedCommentService (add these service methods and have them
call their repositories internally), then update the FacadeFeedService code
paths that used the repositories (the list retrieval around the existing
repository calls) to call FeedLikeService.countsByFeedIds(...) and
FeedCommentService.countsByFeedIds(...) instead.

@KoSeonJe KoSeonJe changed the title feat: [DDING-000] 기존 피드 조회 API 4종에 카운트 필드 추가 feat: 기존 피드 조회 API 4종에 카운트 필드 추가 Feb 23, 2026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/test/java/ddingdong/ddingdongBE/domain/feed/service/FacadeFeedServiceTest.java (1)

67-73: ⚠️ Potential issue | 🟡 Minor

setUp()에서 feedLikeRepositoryfeedCommentRepository 정리가 누락되었습니다.

새 테스트(getFeedById_WithLikesAndComments)에서 좋아요와 댓글을 저장하지만, setUp()에서 이들을 삭제하지 않습니다. 테스트 실행 순서에 따라 잔여 데이터가 다른 테스트에 영향을 줄 수 있습니다. FK 제약조건 순서를 고려하여 피드 삭제 전에 좋아요/댓글을 먼저 삭제해야 합니다.

🐛 수정 제안
 `@BeforeEach`
 void setUp() {
+    feedCommentRepository.deleteAll();
+    feedCommentRepository.flush();
+    feedLikeRepository.deleteAll();
+    feedLikeRepository.flush();
     feedRepository.deleteAll();
     feedRepository.flush();
     clubRepository.deleteAll();
     clubRepository.flush();
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/ddingdong/ddingdongBE/domain/feed/service/FacadeFeedServiceTest.java`
around lines 67 - 73, Update the setUp() method to also clear feedLikeRepository
and feedCommentRepository and ensure they are deleted before feedRepository to
respect FK constraints: call feedLikeRepository.deleteAll() and
feedCommentRepository.deleteAll() prior to feedRepository.deleteAll()/flush()
inside setUp() so likes/comments from previous tests cannot leak into new tests
like getFeedById_WithLikesAndComments.
🧹 Nitpick comments (1)
src/test/java/ddingdong/ddingdongBE/domain/feed/service/FacadeFeedServiceTest.java (1)

187-189: FeedFixture 헬퍼 메서드 사용을 권장합니다.

다른 테스트 파일(FeedLikeRepositoryTest, FeedCommentRepositoryTest)에서는 FeedFixture.createFeedLike() / FeedFixture.createFeedComment()를 일관되게 사용하고 있습니다. 여기서도 동일하게 사용하면 테스트 코드의 일관성이 높아집니다.

♻️ 수정 제안
-        feedLikeRepository.save(FeedLike.builder().feed(savedFeed).uuid("uuid-1").build());
-        feedLikeRepository.save(FeedLike.builder().feed(savedFeed).uuid("uuid-2").build());
-        feedCommentRepository.save(FeedComment.builder().feed(savedFeed).uuid("uuid-3").anonymousNumber(1).content("댓글 1").build());
+        feedLikeRepository.save(FeedFixture.createFeedLike(savedFeed, "uuid-1"));
+        feedLikeRepository.save(FeedFixture.createFeedLike(savedFeed, "uuid-2"));
+        feedCommentRepository.save(FeedFixture.createFeedComment(savedFeed, "uuid-3", 1, "댓글 1"));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/ddingdong/ddingdongBE/domain/feed/service/FacadeFeedServiceTest.java`
around lines 187 - 189, 현재 테스트에서 직접 FeedLike/FeedComment 엔티티를 생성해 저장하고 있는데, 동일한
패턴을 다른 테스트들(FeedLikeRepositoryTest, FeedCommentRepositoryTest)에서 사용 중인 헬퍼 메서드로
통일하세요: savedFeed를 인자로 FeedFixture.createFeedLike(savedFeed, "uuid-1") /
FeedFixture.createFeedLike(savedFeed, "uuid-2") 및
FeedFixture.createFeedComment(savedFeed, "uuid-3", 1, "댓글 1") 호출로 교체하고 반환된 엔티티를
feedLikeRepository.save(...) / feedCommentRepository.save(...) 대신 바로 저장하거나 필요시
저장 호출로 일관되게 사용하도록 수정하세요.
ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0fd363b and 78b0fe9.

📒 Files selected for processing (5)
  • src/main/java/ddingdong/ddingdongBE/domain/feed/repository/FeedRepository.java
  • src/test/java/ddingdong/ddingdongBE/domain/feed/repository/FeedCommentRepositoryTest.java
  • src/test/java/ddingdong/ddingdongBE/domain/feed/repository/FeedLikeRepositoryTest.java
  • src/test/java/ddingdong/ddingdongBE/domain/feed/repository/FeedRepositoryTest.java
  • src/test/java/ddingdong/ddingdongBE/domain/feed/service/FacadeFeedServiceTest.java
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@src/test/java/ddingdong/ddingdongBE/domain/feed/service/FacadeFeedServiceTest.java`:
- Around line 67-73: Update the setUp() method to also clear feedLikeRepository
and feedCommentRepository and ensure they are deleted before feedRepository to
respect FK constraints: call feedLikeRepository.deleteAll() and
feedCommentRepository.deleteAll() prior to feedRepository.deleteAll()/flush()
inside setUp() so likes/comments from previous tests cannot leak into new tests
like getFeedById_WithLikesAndComments.

---

Nitpick comments:
In
`@src/test/java/ddingdong/ddingdongBE/domain/feed/service/FacadeFeedServiceTest.java`:
- Around line 187-189: 현재 테스트에서 직접 FeedLike/FeedComment 엔티티를 생성해 저장하고 있는데, 동일한
패턴을 다른 테스트들(FeedLikeRepositoryTest, FeedCommentRepositoryTest)에서 사용 중인 헬퍼 메서드로
통일하세요: savedFeed를 인자로 FeedFixture.createFeedLike(savedFeed, "uuid-1") /
FeedFixture.createFeedLike(savedFeed, "uuid-2") 및
FeedFixture.createFeedComment(savedFeed, "uuid-3", 1, "댓글 1") 호출로 교체하고 반환된 엔티티를
feedLikeRepository.save(...) / feedCommentRepository.save(...) 대신 바로 저장하거나 필요시
저장 호출로 일관되게 사용하도록 수정하세요.

.id(feedListQuery.id())
.thumbnailCdnUrl(feedListQuery.thumbnailCdnUrl())
.thumbnailOriginUrl(feedListQuery.thumbnailOriginUrl())
.thumbnailFilename(feedListQuery.thumbnailFileName())
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현 api 명세에 해당 필드는 제거해도 될 것 같아

Comment on lines +16 to +19
@Schema(description = "이미지 피드 수", example = "10")
long imageCount,
@Schema(description = "영상 피드 수", example = "5")
long videoCount,
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요구사항이 변경되어 해당 필드는 명세에서 삭제해도 될 것 같아

Comment on lines +25 to +26
@Import(JpaAuditingConfig.class)
class FeedCommentRepositoryTest extends DataJpaTestSupport {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import DataJpaTestSupport 내부에 넣어도 될 것 같아요

@KoSeonJe KoSeonJe merged commit c7dfa5a into develop Feb 23, 2026
2 checks passed
@KoSeonJe KoSeonJe deleted the feat/DDING-000-feed-modify-existing branch February 23, 2026 07:50
@KoSeonJe KoSeonJe self-assigned this Feb 23, 2026
KoSeonJe added a commit that referenced this pull request Feb 28, 2026
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit c7dfa5a)
@KoSeonJe KoSeonJe mentioned this pull request Feb 28, 2026
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