Skip to content

[hotfix] 전체 피드 조회 화면 상단의, 내 띱 목록 조회 api 로직 수정#248

Merged
seongjunnoh merged 10 commits into
developfrom
hotfix/#213-user-show-following-in-feed-view
Aug 17, 2025
Merged

[hotfix] 전체 피드 조회 화면 상단의, 내 띱 목록 조회 api 로직 수정#248
seongjunnoh merged 10 commits into
developfrom
hotfix/#213-user-show-following-in-feed-view

Conversation

@seongjunnoh

@seongjunnoh seongjunnoh commented Aug 17, 2025

Copy link
Copy Markdown
Collaborator

#️⃣ 연관된 이슈

closes #213

📝 작업 내용

이슈 참고해 주시면 됩니다.
기존 코드가 요구사항을 잘못 구현하고 있어서 로직을 수정하였습니다

  • 기존 api 로직
    • 유저가 팔로잉하는 사람들 중, 최근에 공개 피드를 작성한 사람들을 정렬하여 반환
    • 이때 팔로잉하는 사람들 중, 공개 피드를 작성한 적이 없는 사람은 반환하지 않음

그런데 해당 api의 요구사항은

  1. 유저가 팔로잉하는 사람의 목록을 반환한다
  2. 이때 팔로잉하는 사람들 중, 최근 피드를 작성한 사람이 있는 경우, 이 사람을 우선으로 반환한다(정렬 우선순위가 높다)
  3. 2에서의 우선순위가 동일하다면, 최근 팔로잉을 맺은 유저의 정보를 반환한다
  4. 최대 10명의 팔로잉 유저 정보를 반환한다

@heeeeyong 참고해주시면 됩니다!

이므로,

  • 수정된 api 로직

    1. 유저가 팔로잉하는 전체 사람들을 팔로잉 맺은 시각 기준 내림차순 정렬하여 조회
    2. 이 중, 최근에 피드를 작성한 사람들을 피드 작성 시각 기준 내림차순 정렬하여 최대 10명 조회
    3. 2에서 조회한 사람이 10명이라면 그대로 반환
    4. 2에서 조회한 사람이 10명 보다 작다면, 1에서 조회한 사람들의 정보를 추가하여 반환

    위 과정을 service 에서 수행하도록 수정하였습니다

    무한 스크롤 기능이 없는 조회이므로, QueryDSL 에서 복잡한 비즈니스 로직 + 피드를 작성하지 않은 팔로잉한 유저의 존재때문에 발생하는 feed와 user의 left join 문제를 해결하기 위해 service 로직을 대폭 수정하였습니다

📸 스크린샷

💬 리뷰 요구사항

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

📌 PR 진행 시 이러한 점들을 참고해 주세요

* P1 : 꼭 반영해 주세요 (Request Changes) - 이슈가 발생하거나 취약점이 발견되는 케이스 등
* P2 : 반영을 적극적으로 고려해 주시면 좋을 것 같아요 (Comment)
* P3 : 이런 방법도 있을 것 같아요~ 등의 사소한 의견입니다 (Chore)

@coderabbitai

coderabbitai Bot commented Aug 17, 2025

Copy link
Copy Markdown

Walkthrough

팔로잉 기반 피드 상단 노출 로직을 전면 교체. 기존 “최근 글쓴 팔로잉” 흐름을 제거하고, 팔로잉 목록을 팔로우 시점 역순으로 가져온 뒤, 최근 공개 피드 작성자를 우선하여 최대 10명 정렬해 반환. 이를 위해 새로운 포트/리포지토리/DTO/서비스/컨트롤러/테스트를 추가·변경.

Changes

Cohort / File(s) Summary
Feed 쿼리 확장
src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java, .../repository/FeedQueryRepository.java, .../repository/FeedQueryRepositoryImpl.java, src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java
최신 공개 피드 작성자 ID 집합을 조회하는 메서드 추가: findLatestPublicFeedCreatorsIn(Set, int). 어댑터/포트/리포지토리/구현에 일관 반영.
Following 쿼리 신설
src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java, .../repository/following/FollowingQueryRepository.java, .../repository/following/FollowingQueryRepositoryImpl.java, src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java, src/main/java/konkuk/thip/user/application/port/out/dto/FollowingQueryDto.java
팔로잉 목록을 followedAt 내림차순으로 조회하는 쿼리/포트/어댑터 추가 및 DTO(FollowingQueryDto) 도입.
사용자 쿼리(기존 흐름 제거)
src/main/java/konkuk/thip/user/adapter/out/persistence/UserQueryPersistenceAdapter.java, .../repository/UserQueryRepository.java, .../repository/UserQueryRepositoryImpl.java, src/main/java/konkuk/thip/user/application/port/out/UserQueryPort.java, src/main/java/konkuk/thip/user/application/port/out/dto/UserQueryDto.java
“내 팔로잉 중 최근 글쓴 유저” 조회 메서드 및 구현 제거. UserQueryDto 보조 생성자 삭제 및 검증 강화.
API 컨트롤러/DTO 전환
src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java, .../response/UserFollowingRecentWritersResponse.java(삭제), .../response/UserShowFollowingsInFeedViewResponse.java(신규)
엔드포인트는 동일 경로로 유지하되 응답·핸들러 명세를 “myFollowingsInFeedView”로 교체. 구 응답 DTO 삭제, 신 응답 DTO 추가.
서비스/유스케이스 교체
src/main/java/konkuk/thip/user/application/port/in/UserShowFollowingRecentWritersUseCase.java(삭제), .../UserShowFollowingsInFeedViewUseCase.java(신규), .../service/UserShowFollowingRecentWritersService.java(삭제), .../service/UserShowFollowingsInFeedViewService.java(신규)
새 서비스가 팔로잉 목록 + 최근 공개 피드 작성자 우선 로직을 구현. 기존 유스케이스/서비스 제거.
매퍼 갱신
src/main/java/konkuk/thip/user/application/mapper/UserQueryMapper.java
기존 RecentWriter 매핑 제거. FollowingQueryDto → FeedView DTO 매핑 추가.
JPA 제약 강화
src/main/java/konkuk/thip/user/adapter/out/jpa/FollowingJpaEntity.java
외래키 컬럼에 nullable=false 추가.
테스트 업데이트
src/test/java/konkuk/thip/user/adapter/in/web/UserShowFollowingsInFeedViewApiTest.java
시나리오/명세를 새 두단계 정렬 로직에 맞춰 전면 수정 및 케이스 확장.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant Controller as UserQueryController
  participant Service as UserShowFollowingsInFeedViewService
  participant FollowRepo as FollowingQueryPort
  participant FeedRepo as FeedQueryPort
  participant Mapper as UserQueryMapper

  User->>Controller: GET /users/my-followings/recent-feeds
  Controller->>Service: showMyFollowingsInFeedView(userId)
  Service->>FollowRepo: findAllFollowingUsersOrderByFollowedAtDesc(userId)
  alt followings.isEmpty()
    Service-->>Controller: UserShowFollowingsInFeedViewResponse(empty)
  else
    Service->>FeedRepo: findLatestPublicFeedCreatorsIn(followingUserIds, SIZE)
    Service->>Service: assemble ordered IDs (recent public first, then recent follows)
    Service->>Mapper: toFollowingFeedViewDtos(selectedDtos)
    Mapper-->>Service: List<FeedViewDto>
    Service-->>Controller: UserShowFollowingsInFeedViewResponse(dtos)
  end
  Controller-->>User: 200 OK (data.myFollowingUsers)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
내 팔로잉 반환 + 최근 공개 피드 작성자가 앞서 오도록 정렬 (#213)
동일 엔드포인트 경로 유지하되 응답/유즈케이스 전환 (#213)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Following 엔티티 FK에 nullable=false 추가 (src/main/java/konkuk/thip/user/adapter/out/jpa/FollowingJpaEntity.java; exact lines not provided) 정렬/조회 로직 수정 목표와 직접 관련 없는 스키마 제약 변경으로 보임.
UserQueryDto 생성자/검증 로직 변경 (src/main/java/konkuk/thip/user/application/port/out/dto/UserQueryDto.java; exact lines not provided) 제거된 흐름과 관련된 DTO이지만, 본 이슈의 새 로직 구현과 직접적 연관이 불명확함.

Possibly related PRs

Suggested labels

👻 성준, 🔥 hotfix

Suggested reviewers

  • hd0rable
  • buzz0331

Poem

토닥토닥, 피드 꼭대기 풀잎 위,
누굴 먼저 부를까 귀를 세우리.
최근 글 쓴 벗 먼저, 그다음 새로 맺은 닻—
열 발자국까지만, 질서 정연히 맞.
뛰는 심장처럼 정렬된 이름들에
깡총! 오늘도 피드는 산뜻히 열리네. 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hotfix/#213-user-show-following-in-feed-view

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions

Copy link
Copy Markdown

Test Results

407 tests   407 ✅  30s ⏱️
121 suites    0 💤
121 files      0 ❌

Results for commit fa0a681.

@seongjunnoh seongjunnoh requested review from buzz0331 and hd0rable and removed request for buzz0331 August 17, 2025 19:23

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (20)
src/main/java/konkuk/thip/user/application/port/out/dto/FollowingQueryDto.java (1)

7-16: DTO 필수값 검증 추가 제안(레코드 compact ctor 활용)

QueryDSL @QueryProjection을 유지한 채로 필수 필드(userId/followingTargetUserId/followedAt 등)에 대한 방어적 검증을 추가하는 것을 권장합니다. 조기 실패로 데이터 무결성을 확보할 수 있습니다.

다음 패치를 고려해 주세요:

 package konkuk.thip.user.application.port.out.dto;

 import com.querydsl.core.annotations.QueryProjection;
 
 import java.time.LocalDateTime;
+import org.springframework.util.Assert;

 public record FollowingQueryDto(
         Long userId,
         Long followingTargetUserId,
         String followingUserNickname,
         String followingUserProfileImageUrl,
         LocalDateTime followedAt
 ) {
     @QueryProjection
-    public FollowingQueryDto {}
+    public FollowingQueryDto {
+        Assert.notNull(userId, "userId(follower) must not be null");
+        Assert.notNull(followingTargetUserId, "followingTargetUserId must not be null");
+        Assert.notNull(followedAt, "followedAt must not be null");
+        // 닉네임/프로필 이미지의 null 허용 여부는 도메인 정책에 맞게 선택:
+        // Assert.hasText(followingUserNickname, "followingUserNickname must not be blank");
+        // Assert.notNull(followingUserProfileImageUrl, "followingUserProfileImageUrl must not be null");
+    }
 }

추가로, 필드 의미가 혼동될 수 있어 Javadoc으로 follower(=userId) vs following target(=followingTargetUserId)를 명확히 표기하는 것을 권장합니다.

src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepository.java (1)

27-27: 새 메서드의 반환 정렬/중복/빈 입력 처리 계약을 명시하세요

계약을 명확히 하면 구현/테스트 일관성이 좋아집니다. 특히 userIds가 비었을 때 빈 리스트를 반환하는지, createdAt desc 정렬을 보장하는지, 중복 제거 여부를 문서화해 주세요.

권장 Javadoc 추가:

-    List<Long> findLatestPublicFeedCreatorsIn(Set<Long> userIds, int size);
+    /**
+     * 주어진 userIds 중 공개(비공개 제외) & 활성 피드를 가진 사용자들 중
+     * 가장 최근에 피드를 작성한 사용자 ID를 createdAt 내림차순으로 최대 {@code size}명 반환합니다.
+     * - 반환 목록은 중복이 없습니다.
+     * - {@code userIds}가 비어 있으면 빈 리스트를 반환해야 합니다.
+     */
+    List<Long> findLatestPublicFeedCreatorsIn(Set<Long> userIds, int size);

구현 측면에서는 IN 절에 빈 Set이 전달될 때 불필요한 쿼리를 피하고 즉시 Collections.emptyList()를 반환하도록 방어 로직을 권장합니다.

src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java (1)

19-22: Optional: 팔로잉 유저 전체 페치 대신 상위 N명만 조회하도록 오버로드 및 DB-limit 적용 고려

현재 UserShowFollowingsInFeedViewService#showMyFollowingsInFeedView 에서

List<FollowingQueryDto> followingQueryDtos =
    followingQueryPort.findAllFollowingUsersOrderByFollowedAtDesc(userId);

를 호출해 모든 팔로잉을 가져온 뒤, 화면 상단에 노출할 최대 10명(SIZE)에 대한 잘라내기를 하지 않고 있습니다. 팔로잉 수가 많을 경우 불필요한 I/O/메모리 비용이 발생할 수 있습니다.

제안:

  • FollowingQueryPort에 기본 구현 오버로드 메서드 추가
    default List<FollowingQueryDto> findTopFollowingUsersOrderByFollowedAtDesc(Long userId, int size) {
        List<FollowingQueryDto> all = findAllFollowingUsersOrderByFollowedAtDesc(userId);
        return all.size() <= size ? all : all.subList(0, size);
    }
  • 서비스 레이어에서 findAll… 대신 findTop…(userId, SIZE) 호출로 교체
  • persistence 어댑터 및 QueryDSL 구현(RepositoryImpl)에 .limit(size) 로 DB-레벨 최적화 추가 검토

위 단계로 점진 적용하면, 상단 노출 용도일 때 불필요한 전체 조회 비용을 줄일 수 있습니다.

src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java (1)

51-53: 계약 문서화로 정렬/중복/엣지케이스 명확화

Repository/Adapter/Service 전층에서 동일한 기대를 공유할 수 있도록 Javadoc으로 계약을 명확히 해 주세요.

권장 패치:

-    List<Long> findLatestPublicFeedCreatorsIn(Set<Long> userIds, int size);
+    /**
+     * 주어진 userIds 중에서 '최근 공개 피드' 작성자 ID를 createdAt 내림차순으로 최대 {@code size}명 반환합니다.
+     * - 반환 리스트는 중복이 없습니다.
+     * - userIds가 비어 있으면 빈 리스트를 반환합니다.
+     */
+    List<Long> findLatestPublicFeedCreatorsIn(Set<Long> userIds, int size);

추가로, 상위 N 추출 후 나머지를 팔로우 시점 역순으로 채우는 서비스 조합 단계에서
LinkedHashSet으로 우선순위 집합(최근 공개 피드 작성자) → 잔여 팔로잉 순 삽입을 추천합니다.
이 방식은 중복 제거와 순서 보장을 동시에 달성합니다(기억된 선호도 반영).

src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepository.java (1)

19-19: 상단 노출 목적이라면 DB 레벨에서 제한/페이지네이션을 지원하세요.

현재 “전체”를 모두 반환하면 팔로잉 수가 많은 사용자의 경우 메모리/네트워크 비용이 큽니다. 상단 노출은 보통 최대 N(예: 10)명만 필요하므로, DB에서 정렬+limit를 적용하는 API를 함께 노출하는 편이 성능상 유리합니다. 또한 followedAt이 동일할 때 결정적 순서를 위해 2차 정렬 키(예: targetUserId ASC)를 추가하는 것을 권장합니다.

적용 예시(오버로드 추가):

 public interface FollowingQueryRepository {
@@
-    List<FollowingQueryDto> findAllFollowingUsersOrderByFollowedAtDesc(Long userId);
+    List<FollowingQueryDto> findAllFollowingUsersOrderByFollowedAtDesc(Long userId);
+    List<FollowingQueryDto> findFollowingUsersOrderByFollowedAtDesc(Long userId, int size);
 }
src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java (1)

184-187: 빈 userIds 입력 시 불필요한 쿼리를 방지하는 early return 권장.

QueryDSL의 in(emptySet) 처리가 DB별로 다를 수 있어, 어댑터 단에서 안전하게 빈 리스트를 즉시 반환하면 낭비를 줄일 수 있습니다. size <= 0인 케이스도 함께 방어해두면 좋습니다.

     @Override
     public List<Long> findLatestPublicFeedCreatorsIn(Set<Long> userIds, int size) {
-        return feedJpaRepository.findLatestPublicFeedCreatorsIn(userIds, size);
+        if (userIds == null || userIds.isEmpty() || size <= 0) {
+            return List.of();
+        }
+        return feedJpaRepository.findLatestPublicFeedCreatorsIn(userIds, size);
     }
src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java (1)

61-63: 전체 조회는 비용 큼 — 상단 노출 N명만 필요하면 size 인자를 추가해 DB에서 제한하세요.

상단 “내 구독” 영역은 보통 소수만 노출됩니다. 현재 어댑터는 전체를 모두 조회하므로, JPA/QueryDSL 쿼리 단계에서 정렬+limit 적용이 성능상 유리합니다. 또한 userId null/size <= 0 방어 로직도 고려하세요.

-    public List<FollowingQueryDto> findAllFollowingUsersOrderByFollowedAtDesc(Long userId) {
-        return followingJpaRepository.findAllFollowingUsersOrderByFollowedAtDesc(userId);
+    public List<FollowingQueryDto> findAllFollowingUsersOrderByFollowedAtDesc(Long userId, int size) {
+        if (userId == null || size <= 0) {
+            return List.of();
+        }
+        return followingJpaRepository.findAllFollowingUsersOrderByFollowedAtDesc(userId, size);
     }

관련 Repository/Port 시그니처도 함께 확장 필요합니다. 원하시면 전체 변경 분에 대한 일괄 패치 제안 드리겠습니다.

src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java (1)

387-401: 정렬 결정성 보강(+tie-breaker)과 빈 Set 가드, 인덱스 권고.

  • 동일한 max(createdAt)을 가진 사용자가 다수일 때 결과 순서가 비결정적일 수 있습니다. 2차 정렬키로 userId ASC 등을 추가해 안정성을 높이세요.
  • userIds가 비어 있으면 즉시 반환하는 것이 안전/효율적입니다.
  • 성능을 위해 feed(user_id, is_public, status, created_at) 또는 (user_id, created_at) 복합 인덱스를 고려하세요. 그룹핑+max에 큰 이점이 있습니다.
     @Override
     public List<Long> findLatestPublicFeedCreatorsIn(Set<Long> userIds, int size) {
-        return jpaQueryFactory
+        if (userIds == null || userIds.isEmpty() || size <= 0) {
+            return List.of();
+        }
+        return jpaQueryFactory
                 .select(feed.userJpaEntity.userId)
                 .from(feed)
                 .where(
                         feed.userJpaEntity.userId.in(userIds),
                         feed.isPublic.isTrue(),
                         feed.status.eq(StatusType.ACTIVE)
                 )
                 .groupBy(feed.userJpaEntity.userId)
-                .orderBy(feed.createdAt.max().desc())
+                .orderBy(
+                    feed.createdAt.max().desc(),
+                    feed.userJpaEntity.userId.asc()  // tie-breaker for deterministic ordering
+                )
                 .limit(size)
                 .fetch();
     }
src/main/java/konkuk/thip/user/application/port/in/UserShowFollowingsInFeedViewUseCase.java (1)

5-8: 서비스 트랜잭션 및 API 계약 명확화 제안

  • UserShowFollowingsInFeedViewUseCaseuserId 파라미터는 null을 허용하지 않을 경우 Longlong으로 변경하거나 @NotNull 애노테이션 추가로 null 경로 제거
  • UseCase 인터페이스 및 메서드에 JavaDoc으로 “상단 노출 최대 10명” 정책( SIZE = 10 ) 명시
  • UserShowFollowingsInFeedViewService 클래스 또는 showMyFollowingsInFeedView 메서드에 @Transactional(readOnly = true) 애노테이션 적용 검토
  • 이미 LinkedHashSet을 활용해 중복 제거 및 순서 보존 로직이 적용되어 있으므로, 해당 부분은 현재 구조를 유지해도 무방
src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepositoryImpl.java (3)

119-126: 중복 팔로잉 행(데이터 품질 이슈) 대비: distinct 추가 권장

히스토리나 데이터 오염으로 (user_id, following_user_id) 중복 행이 존재할 경우 결과가 중복될 수 있습니다. 소비 계층에서 LinkedHashSet으로 걸러도 되지만, 조회 단계에서 distinct를 추가하면 I/O 비용과 매핑 비용을 줄일 수 있습니다.

-        return jpaQueryFactory.select(new QFollowingQueryDto(
+        return jpaQueryFactory.select(new QFollowingQueryDto(
                 following.userJpaEntity.userId,
                 followingTargetUser.userId,
                 followingTargetUser.nickname,
                 alias.imageUrl,
                 following.createdAt
-                ))
+                ))
+                .distinct()

114-115: 메서드 이름이 의도와 다소 상이해 보입니다 (네이밍 니트픽)

서비스 레벨에서 “최근 피드 작성자 우선 정렬”이 추가로 적용된다면, 이 메서드는 “팔로우 시점 역순의 베이스 셋 제공” 성격입니다. 혼동 방지를 위해 findFollowingsOrderByFollowedAtDesc 또는 findFollowingsBaseOrderForFeedView 같은 이름을 고려해볼 수 있습니다.


113-136: 대규모 팔로잉 사용자를 위한 성능 최적화 제안

요구사항(최근 피드 작성자 우선 + 부족분은 팔로우 시점 역순으로 보충, 최대 10명)을 효율적으로 달성하려면 “전체 팔로잉 전량을 메모리로 불러와 재정렬” 대신 2-Phase 접근을 고려해볼 수 있습니다.

  • Phase 1: 내 팔로잉 중 “최근 공개 피드 작성자”의 최신 피드 시각 max(created_at) 기준 상위 N명 조회
  • Phase 2: 남은 슬롯(<= 10-N)은 팔로우 시점 역순으로 보충(자기 자신 제외/중복 제거)

이렇게 하면 불필요한 전량 스캔을 피할 수 있습니다. 인덱스가 받쳐주면 DB 정렬 비용도 훨씬 낮습니다. 구현은 Repository 또는 PersistenceAdapter에서 서브쿼리/그룹핑(QueryDSL의 groupBy/서브쿼리)로 가능하고, 서비스에서 두 결과를 합쳐 최대 10명만 반환하면 됩니다.

src/main/java/konkuk/thip/user/adapter/in/web/response/UserShowFollowingsInFeedViewResponse.java (1)

9-22: Swagger 가독성 보완(예시 값 추가) 제안

API 소비자 입장에서 미리보기 품질을 높이려면 @Schema(example = "...")를 필드/레코드 단위로 일부 추가하는 것을 권장합니다. 서드파티/프론트 팀 협업에 도움이 됩니다. 적용은 선택사항입니다.

src/main/java/konkuk/thip/user/application/mapper/UserQueryMapper.java (1)

23-25: 리스트 매핑 메서드는 생략 가능(MapStruct 자동 지원)

MapStruct는 단일 매핑(toFollowingFeedViewDto)이 있으면 동일 시그니처의 리스트 매핑을 자동 생성합니다. 명시 메서드를 제거해도 동작 동일하며, 인터페이스 표면적을 줄여 유지보수성이 좋아집니다. 선택사항입니다.

-    List<UserShowFollowingsInFeedViewResponse.UserShowFollowingsInFeedViewDto> toFollowingFeedViewDtos(
-            List<FollowingQueryDto> dtos
-    );
+    // 선택: 생략 가능(MapStruct가 toFollowingFeedViewDto 기반으로 자동 생성)
src/main/java/konkuk/thip/user/application/service/UserShowFollowingsInFeedViewService.java (3)

31-37: 대량 팔로잉 사용자의 성능 병목 가능성: 전체 팔로잉 전량 로딩 회피를 검토하세요

현재는 전체 팔로잉을 전부 메모리로 읽은 뒤(Set 변환 포함) 처리합니다. 팔로잉 수가 매우 큰 사용자에서:

  • DB IN 절이 과도하게 커지고(네트워크/플랜 비용 상승),
  • 애플리케이션 메모리/GC 부하가 커질 수 있습니다.

핵심 개선 아이디어(선호 순서):

  • DB로 로직을 푸시: “내 팔로잉 중 최신 공개 피드 작성자 TOP N”을 팔로잉 테이블과 포스트를 조인 + MAX(created_at)로 그룹/정렬/LIMIT 하여 한 번에 구합니다.
  • 부족분은 “내 팔로잉 중(제외 목록 제외) 최근 팔로잉 맺은 순 TOP K”만 추가 조회해 채웁니다. 즉, 2개의 제한 쿼리로 전체 팔로잉 전량 로딩을 피합니다.

이렇게 하면 항상 최대 2회, 각 N(=10) 크기의 소량 결과만 다뤄 성능이 안정적입니다.

원하시면 포트/리포지토리 시그니처와 JPQL/SQL 샘플을 제안드리겠습니다.


46-53: 잠재적 중복 키로 인한 IllegalStateException 방지

toMap 수집 시 중복 키가 발생하면 IllegalStateException이 납니다. 정상이라면 중복이 없어야 하지만, 방어적 차원에서 merge 함수를 지정하는 편이 안전합니다.

다음과 같이 변경을 제안합니다:

-        Map<Long, FollowingQueryDto> followingMap = followingQueryDtos.stream()
-                .collect(Collectors.toMap(
-                        FollowingQueryDto::followingTargetUserId,
-                        dto -> dto
-                ));
+        Map<Long, FollowingQueryDto> followingMap = followingQueryDtos.stream()
+                .collect(Collectors.toMap(
+                        FollowingQueryDto::followingTargetUserId,
+                        dto -> dto,
+                        (existing, ignored) -> existing
+                ));

28-56: (선택) 전체 로직을 2회 제한 쿼리로 축약하는 방안 제안

핫픽스 이후 개선 아이디어입니다. 전체 팔로잉 로딩 없이도 동일 결과를 만들 수 있습니다.

  • 포트 제안:

    • feedQueryPort.findTopLatestPublicFeedCreatorsAmongMyFollowings(userId, size)
    • followingQueryPort.findTopFollowingsOrderByFollowedAtDescExcluding(userId, excludedUserIds, size)
  • 서비스 로직 스케치:

    1. latest = feedQueryPort.findTopLatestPublicFeedCreatorsAmongMyFollowings(userId, SIZE)
    2. 부족분이 있으면 fill = followingQueryPort.findTopFollowingsOrderByFollowedAtDescExcluding(userId, latest, SIZE - latest.size)
    3. orderedIds = latest ∪ fill
    4. 필요한 DTO만 조회(또는 기존 DTO 리턴 형태로 포트를 설계) 후 매핑

장점: 항상 소량 결과만 다뤄 안정적인 성능/메모리 사용을 보장합니다.

src/test/java/konkuk/thip/user/adapter/in/web/UserShowFollowingsInFeedViewApiTest.java (3)

37-39: 네이밍 정합성: 메서드명에 남아있는 recent_writers 접미사 정리 권장

클래스/DisplayName은 새 요구사항을 반영하지만, 테스트 메서드명에는 여전히 recent_writers가 남아 있습니다. 검색/가독성 측면에서 메서드명을 feed_view/followings 중심으로 정리하면 더 명확합니다.


248-266: 직접 SQL로 created_at 조정: 안정성/유지보수 관점의 보완 제안

  • 장점: 정렬 시나리오를 결정적으로 통제 가능.
  • 고려사항: 테이블/컬럼명 변경에 취약하고, DB 방언 차이에 민감합니다.

대안:

  • TestEntityFactory에서 createdAt을 주입 가능한 팩토리/빌더 제공(가능하다면 Auditing 우회).
  • 또는 EntityManager를 통한 update 쿼리를 모듈화해 재사용성/가독성을 높임.

현 구현도 충분히 실용적이므로, 구조 변경 시점에만 고려해도 됩니다.

Also applies to: 267-304


329-373: 두 가지 보강 테스트 제안: (1) 팔로잉 0명, (2) 비공개 피드 배제

  • 팔로잉이 없는 경우: 빈 배열([]) 반환을 보장하는 단위/통합 테스트를 추가하면 경계조건 신뢰도가 올라갑니다.
  • 비공개 피드만 가진 팔로잉: 해당 사용자가 “최신 공개 피드 작성자” 목록에 포함되지 않음을 명시적으로 검증하면 가시성이 좋아집니다.

원하시면 해당 테스트 케이스 코드를 초안으로 드리겠습니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 93d182a and fa0a681.

📒 Files selected for processing (24)
  • src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java (1 hunks)
  • src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepository.java (1 hunks)
  • src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java (1 hunks)
  • src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java (1 hunks)
  • src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java (2 hunks)
  • src/main/java/konkuk/thip/user/adapter/in/web/response/UserFollowingRecentWritersResponse.java (0 hunks)
  • src/main/java/konkuk/thip/user/adapter/in/web/response/UserShowFollowingsInFeedViewResponse.java (1 hunks)
  • src/main/java/konkuk/thip/user/adapter/out/jpa/FollowingJpaEntity.java (1 hunks)
  • src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java (2 hunks)
  • src/main/java/konkuk/thip/user/adapter/out/persistence/UserQueryPersistenceAdapter.java (0 hunks)
  • src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepository.java (0 hunks)
  • src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepositoryImpl.java (0 hunks)
  • src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepository.java (2 hunks)
  • src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepositoryImpl.java (2 hunks)
  • src/main/java/konkuk/thip/user/application/mapper/UserQueryMapper.java (2 hunks)
  • src/main/java/konkuk/thip/user/application/port/in/UserShowFollowingRecentWritersUseCase.java (0 hunks)
  • src/main/java/konkuk/thip/user/application/port/in/UserShowFollowingsInFeedViewUseCase.java (1 hunks)
  • src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java (2 hunks)
  • src/main/java/konkuk/thip/user/application/port/out/UserQueryPort.java (0 hunks)
  • src/main/java/konkuk/thip/user/application/port/out/dto/FollowingQueryDto.java (1 hunks)
  • src/main/java/konkuk/thip/user/application/port/out/dto/UserQueryDto.java (1 hunks)
  • src/main/java/konkuk/thip/user/application/service/UserShowFollowingRecentWritersService.java (0 hunks)
  • src/main/java/konkuk/thip/user/application/service/UserShowFollowingsInFeedViewService.java (1 hunks)
  • src/test/java/konkuk/thip/user/adapter/in/web/UserShowFollowingsInFeedViewApiTest.java (6 hunks)
💤 Files with no reviewable changes (7)
  • src/main/java/konkuk/thip/user/application/port/in/UserShowFollowingRecentWritersUseCase.java
  • src/main/java/konkuk/thip/user/adapter/out/persistence/UserQueryPersistenceAdapter.java
  • src/main/java/konkuk/thip/user/application/service/UserShowFollowingRecentWritersService.java
  • src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepository.java
  • src/main/java/konkuk/thip/user/application/port/out/UserQueryPort.java
  • src/main/java/konkuk/thip/user/adapter/in/web/response/UserFollowingRecentWritersResponse.java
  • src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepositoryImpl.java
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#113
File: src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchCommandPersistenceAdapter.java:38-44
Timestamp: 2025-07-30T14:05:04.945Z
Learning: seongjunnoh는 코드 최적화 제안에 대해 구체적인 기술적 근거와 효율성 차이를 이해하고 싶어하며, 성능 개선 방식에 대한 상세한 설명을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#93
File: src/main/java/konkuk/thip/room/adapter/out/persistence/RoomQueryPersistenceAdapter.java:49-114
Timestamp: 2025-07-28T16:44:31.224Z
Learning: seongjunnoh는 코드 중복 문제에 대한 리팩토링 제안을 적극적으로 수용하고 함수형 인터페이스를 활용한 해결책을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#195
File: src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.java:0-0
Timestamp: 2025-08-13T05:22:32.287Z
Learning: seongjunnoh는 데이터 무결성과 중복 방지에 대한 고민이 깊으며, LinkedHashSet을 활용한 중복 제거와 순서 보장을 동시에 달성하는 솔루션을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#180
File: src/main/java/konkuk/thip/room/application/service/AttendanceCheckCreateService.java:22-40
Timestamp: 2025-08-14T09:15:31.332Z
Learning: seongjunnoh는 기술적 완벽성보다 실제 비즈니스 시나리오와 사용자 행동 패턴을 우선 고려하며, 발생 가능성이 낮은 엣지 케이스보다는 실용적인 구현을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#112
File: src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java:272-272
Timestamp: 2025-07-30T10:44:34.115Z
Learning: seongjunnoh는 피드 커서 페이지네이션에서 LocalDateTime 단일 커서 방식을 선호하며, 복합 키 기반 커서보다 구현 단순성과 성능을 우선시한다.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (14)
src/main/java/konkuk/thip/user/application/port/out/dto/UserQueryDto.java (1)

21-25: aliasName·aliasColor·createdAt non-null 보장 검증 완료

  • QUserQueryDto를 생성하는 모든 프로젝션(UserQueryRepositoryImpl, FollowingQueryRepositoryImpl)에서 alias.value, alias.color, user.createdAt을 명시적으로 넘겨줌
  • UserJpaEntity.aliasForUserJpaEntity 매핑은 DB 제약(nullable = false)로 모든 User에 반드시 Alias가 존재
  • BaseJpaEntity.createdAt에 @column(nullable = false) 적용으로 생성 시 null 불가

따라서 Assert.notNull에 의한 런타임 예외 발생 가능성은 없습니다.

src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepository.java (1)

4-4: LGTM: 새 DTO 의존성 추가가 포트 계층과 일관적입니다.

기존 UserQueryDto 기반 질의들과 결을 맞추며, 읽기용 DTO 반환 방향성도 명확합니다.

src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java (1)

5-5: LGTM: DTO import 추가 적절.

레이어 경계를 넘어서는 반환 타입 일관성 유지에 도움됩니다.

src/main/java/konkuk/thip/user/adapter/out/jpa/FollowingJpaEntity.java (1)

21-26: FK 컬럼 NOT NULL 전환 전 마이그레이션·데이터 정합성 검증 필수
두 FK 컬럼(@joincolumn name="user_id", nullable=false / name="following_user_id", nullable=false)을 NOT NULL로 변경하면, 기존에 NULL 값이 있는 레코드가 있거나 스키마가 아직 NOT NULL으로 설정되지 않은 경우 런타임에서 예외가 발생합니다. 운영 DB에 배포하기 전에 반드시 다음을 확인해주세요:

  • 데이터 백필 및 NULL 값 정리
  • DDL 마이그레이션 적용 여부
    • ALTER TABLE followings ALTER COLUMN user_id SET NOT NULL;
    • ALTER TABLE followings ALTER COLUMN following_user_id SET NOT NULL;
    • FK 제약조건(ADD CONSTRAINT … FOREIGN KEY)
    • UNIQUE 제약(UNIQUE(user_id, following_user_id))
    • 필요한 인덱스 생성

현재 저장소에서 자동 검색된 마이그레이션 파일이 없습니다. 아래 경로 및 확장자(.sql, .yaml, .yml 등)에 직접 마이그레이션 스크립트가 있는지 검토 부탁드립니다:

  • src/main/resources/db/migration
  • src/main/resources/db/changelog
  • 기타 별도 스크립트 디렉터리

추가로, JPA 엔티티 레벨에서 스키마 동기화 및 문서화를 위해 @table에 UNIQUE·INDEX를 선언하는 것도 고려해 보세요(선택 사항).

@Table(
  name = "followings",
  uniqueConstraints = {
    @UniqueConstraint(name = "uk_followings_user_target", columnNames = {"user_id", "following_user_id"})
  },
  indexes = {
    @Index(name = "idx_followings_user_created_at", columnList = "user_id, created_at"),
    @Index(name = "idx_followings_following_user_id", columnList = "following_user_id")
  }
)
src/main/java/konkuk/thip/user/adapter/in/web/response/UserShowFollowingsInFeedViewResponse.java (1)

24-26: LGTM – 빈 목록 팩토리 적절합니다

Collections.emptyList()로 null 방지/불변 리스트를 반환하는 선택 좋습니다.

src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java (2)

46-46: LGTM – 신규 UseCase 의존성 교체 적절합니다

컨트롤러 의존성이 새로운 요구사항에 맞게 잘 치환되었습니다.


162-170: 정렬 및 최대 10명 제한 로직 확인 완료
서비스 레이어 SIZE = 10 상수를 사용하고,

  • feedQueryPort.findLatestPublicFeedCreatorsIn(followingUserIds, SIZE)로 최근 공개 피드 작성자 우선 추출
  • assembleOrderedIds에서 부족한 인원은 최근 팔로우 순으로 채워 최대 10명으로 제한

요구사항(“최근 공개 피드 작성자 우선 + 최대 10명”)을 충족함을 확인했습니다.

src/main/java/konkuk/thip/user/application/mapper/UserQueryMapper.java (1)

18-21: FollowingQueryDto 필드 검증 완료

FollowingQueryDto 레코드에 다음 필드가 모두 존재함을 확인했습니다:

  • followingTargetUserId
  • followingUserNickname
  • followingUserProfileImageUrl

MapStruct 매핑 시점에 컴파일 오류가 발생하지 않습니다.

src/main/java/konkuk/thip/user/application/service/UserShowFollowingsInFeedViewService.java (3)

28-56: 요구사항 정합성 OK — 정렬/중복 제거/상한(10) 로직이 명확합니다

  • “최근 공개 피드 작성자 우선 → 최근 팔로잉 순” 조합을 LinkedHashSet으로 구현해 중복 제거와 순서 보존을 동시에 만족합니다.
  • 팔로잉이 없을 때 빈 리스트 반환 분기 처리도 적절합니다.

58-68: LinkedHashSet을 활용한 순서 보존 + 중복 제거가 목적에 부합합니다

요구사항(중복 제거 및 정렬 우선순위 보존)에 딱 맞는 선택입니다. 상한(SIZE) 체크로 불필요한 삽입도 방지하고 있습니다.


39-41: 확인: findLatestPublicFeedCreatorsIn 구현이 요구사항에 부합합니다

  • groupBy(feed.userJpaEntity.userId)로 작성자별로 중복 없이 그룹핑
  • orderBy(feed.createdAt.max().desc())로 각 작성자의 최신 공개 피드 작성일 기준 DESC 정렬
  • limit(size)로 상단에서 SIZE 제한 적용

추가 수정 필요 없습니다.

src/test/java/konkuk/thip/user/adapter/in/web/UserShowFollowingsInFeedViewApiTest.java (3)

71-75: 팔로우 외 사용자 배제 케이스 커버리지 LGTM

user3는 비팔로우 상태에서 공개 피드를 작성하지만 응답에 포함되지 않음을 검증하고 있어, 필터링 요건을 잘 커버합니다.


187-204: 상한 10명 및 최신 공개 피드 우선 정렬 검증이 명료합니다

created_at를 직접 고정시각으로 업데이트하여 명확한 정렬 기대값을 만들고, 12명 중 상위 10명만 반환됨을 확인합니다. 요구사항을 정확히 테스트하고 있습니다.


206-327: 혼합 정렬(최신 공개 피드 → 최근 팔로우) + 중복 제거 시나리오 커버리지 우수

  • 공개 피드 작성자가 5명뿐인 상황에서 부족분을 “최근 팔로우”로 채우고,
  • 이미 포함된 사용자(중복)는 제외하는 규칙을 정확히 검증합니다.

핵심 동작을 현실적인 데이터로 촘촘히 점검하고 있어 신뢰도가 높습니다.

Comment on lines +127 to +129
.join(following.followingUserJpaEntity, followingTargetUser)
.join(followingTargetUser.aliasForUserJpaEntity, alias)
.where(

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

alias 조인은 leftJoin이 더 안전합니다

동일 클래스 내 기존 메서드들은 alias에 leftJoin을 사용합니다(별칭/프로필이 없는 사용자 탈락 방지). 여기서는 join(inner join)이라 alias 없는 사용자가 결과에서 빠질 수 있습니다. 일관성과 누락 방지 차원에서 leftJoin 권장합니다.

-                .join(followingTargetUser.aliasForUserJpaEntity, alias)
+                .leftJoin(followingTargetUser.aliasForUserJpaEntity, alias)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.join(following.followingUserJpaEntity, followingTargetUser)
.join(followingTargetUser.aliasForUserJpaEntity, alias)
.where(
.join(following.followingUserJpaEntity, followingTargetUser)
.leftJoin(followingTargetUser.aliasForUserJpaEntity, alias)
.where(
🤖 Prompt for AI Agents
In
src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepositoryImpl.java
around lines 127 to 129, the join on alias uses an inner join which can drop
users without an alias; change the alias join from
.join(followingTargetUser.aliasForUserJpaEntity, alias) to
.leftJoin(followingTargetUser.aliasForUserJpaEntity, alias) so it matches other
methods and preserves users without aliases.

@seongjunnoh seongjunnoh requested a review from buzz0331 August 17, 2025 19:23
@seongjunnoh seongjunnoh merged commit 484749e into develop Aug 17, 2025
4 checks passed
@seongjunnoh seongjunnoh deleted the hotfix/#213-user-show-following-in-feed-view branch August 17, 2025 19:24

@buzz0331 buzz0331 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

굿굿 수고하셨습니다~ 엄청 고민하신게 보이네요 👍🏻 👍🏻

Comment on lines +28 to +57
@Override
@Transactional(readOnly = true)
public UserShowFollowingsInFeedViewResponse showMyFollowingsInFeedView(Long userId) {
// 1. 유저가 팔로잉하는 사람들을 팔로잉을 최근에 맺은 순으로 조회
// TODO : 유저가 팔로잉하는 사람들이 너무 많으면??? -> 고민해봐야 함
List<FollowingQueryDto> followingQueryDtos = followingQueryPort.findAllFollowingUsersOrderByFollowedAtDesc(userId);

if (followingQueryDtos.isEmpty()) {
return UserShowFollowingsInFeedViewResponse.returnEmptyList();
}

// 2. 유저가 팔로잉하는 사람들 중, 가장 최근에 공개 피드를 작성한 사람들을 조회
List<Long> latestPublicFeedCreators = fetchLatestPublicFeedCreators(followingQueryDtos);

// 3. 결과 조합 : 팔로잉 유저들 중, 최신 공개 피드 작성자 우선 -> 최근 팔로우 맺은 순
LinkedHashSet<Long> orderedIds = assembleOrderedIds(latestPublicFeedCreators, followingQueryDtos);

// 4. ID 순서대로 DTO 매핑하여 response 반환
Map<Long, FollowingQueryDto> followingMap = followingQueryDtos.stream()
.collect(Collectors.toMap(
FollowingQueryDto::followingTargetUserId,
dto -> dto
));
List<FollowingQueryDto> result = orderedIds.stream()
.map(followingMap::get)
.toList();

return new UserShowFollowingsInFeedViewResponse(userQueryMapper.toFollowingFeedViewDtos(result));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

쿼리 성능을 엄청 신경쓰신게 느껴지네요. 다만, 적어 놓으신대로 사용자가 팔로우하는 수가 많아질수록 메모리 적재량 증가 + findLatestPublicFeedCreatorsIn 메서드의 in 절의 오버헤드 증가로 인해 서버의 부담이 커질 것 같긴하네요. 이 부분은 그냥 User 테이블에 최근에 Feed 작성한 시간을 나타내는 컬럼을 추가하거나 Redis에 각 사용자의 최신 피드 작성 시간을 저장해두고 사용하는 것도 하나의 방법일 것 같아욥. 이렇게 되면, 한번의 쿼리로 조회할 수 있을 것 같네요! 추후에 한번 고려해보죠

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.

넵 좋습니다! redis를 적극적으로 활용하는 것도 좋을 거 같습니다!

Comment on lines +7 to +16
public record FollowingQueryDto(
Long userId,
Long followingTargetUserId,
String followingUserNickname,
String followingUserProfileImageUrl,
LocalDateTime followedAt
) {
@QueryProjection
public FollowingQueryDto {}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

흠 이걸 보니까 UserQueryDto를 조금 고쳐야 될 것 같네요. 저는 팔로우 관련 조회도 모두 UserQueryDto로 수행했는데 UserQueryDto에서 createdAt이 사실 followedAt으로 쓰이고 있는 것 같네요. 나중에 수정해보겠습니다

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.

UserQueryDto 를 그대로 활용할까 하다가, 뭔가 팔로잉하는 다른 사람들을 조회하는 모델이 하나더 있으면 좋지않나? 싶어서 추가해본거긴 합니다!

하나의 dto 내부에 QueryProjection을 위한 생성자가 많은것도 별로지 않나 싶은 생각도 있었습니다

추후에 관련해서 얘기나눠보면 좋을 것 같습니다!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[THIP2025-258] [hotfix] 전체 피드 조회 상단 내 구독 목록 조회 api 로직 수정

2 participants