[feat] 특정 유저의 피드 전체 조회 api 개발#129
Conversation
Walkthrough특정 유저가 작성한 공개 피드를 페이징 처리하여 전체 조회하는 API가 새로 추가되었습니다. 이를 위해 컨트롤러, 서비스, 포트, 어댑터, 매퍼, 리포지토리 계층에 새로운 메서드 및 DTO가 도입되었으며, 기존 "내 피드 조회" 관련 코드가 통합 및 리팩터링되었습니다. 통합 테스트도 추가되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Controller
participant Service
participant FeedQueryPort
participant SavedFeedPort
participant LikedPostPort
Client->>Controller: GET /feeds/users/{userId}?cursor=...
Controller->>Service: showSpecificUserFeeds(requestUserId, feedOwnerId, cursor)
Service->>FeedQueryPort: findSpecificUserFeedsByCreatedAt(feedOwnerId, cursor)
Service->>SavedFeedPort: findSavedFeedIdsByUserId(requestUserId, feedIds)
Service->>LikedPostPort: findLikedFeedIdsByUserId(requestUserId, feedIds)
Service->>Service: Map FeedQueryDto + saved/liked info → FeedShowByUserResponse
Service->>Controller: FeedShowByUserResponse
Controller->>Client: BaseResponse<FeedShowByUserResponse>
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~15-25 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes(해당 변경사항 내에서는 연관 이슈(#121)와 무관한 기능적 코드 변경이 발견되지 않았습니다.) Suggested labels
Suggested reviewers
Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/main/java/konkuk/thip/feed/adapter/in/web/FeedQueryController.java (1)
54-65: 새로운 특정 유저 피드 조회 엔드포인트가 올바르게 구현됨새로운 API 엔드포인트가 다음과 같이 잘 구현되었습니다:
- 명확한 Swagger 문서화
- 적절한 파라미터 어노테이션 사용
- 기존 엔드포인트와 일관된 패턴
다만 매개변수 명명에 대한 개선 제안이 있습니다.
가독성 향상을 위해
@PathVariable이름을 더 명확하게 변경하는 것을 고려해보세요:- @Parameter(description = "해당 유저(= 피드 주인)의 userId 값") @PathVariable("userId") final Long feedOwnerId, + @Parameter(description = "해당 유저(= 피드 주인)의 userId 값") @PathVariable("feedOwnerId") final Long feedOwnerId,그리고 URL 패스도 함께 수정:
- @GetMapping("/feeds/users/{userId}") + @GetMapping("/feeds/users/{feedOwnerId}")이렇게 하면 인증된 사용자의
userId와 피드 소유자의feedOwnerId를 더 명확히 구분할 수 있습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
src/main/java/konkuk/thip/feed/adapter/in/web/FeedQueryController.java(4 hunks)src/main/java/konkuk/thip/feed/adapter/in/web/response/FeedShowByUserResponse.java(1 hunks)src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java(2 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(2 hunks)src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.java(2 hunks)src/main/java/konkuk/thip/feed/application/port/in/FeedShowAllOfUserUseCase.java(1 hunks)src/main/java/konkuk/thip/feed/application/port/in/FeedShowMineUseCase.java(0 hunks)src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java(1 hunks)src/main/java/konkuk/thip/feed/application/service/FeedShowAllOfUserService.java(1 hunks)src/main/java/konkuk/thip/feed/application/service/FeedShowMineService.java(0 hunks)src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSpecificUserTest.java(1 hunks)
💤 Files with no reviewable changes (2)
- src/main/java/konkuk/thip/feed/application/port/in/FeedShowMineUseCase.java
- src/main/java/konkuk/thip/feed/application/service/FeedShowMineService.java
🧰 Additional context used
🧠 Learnings (3)
📓 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#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 단일 커서 방식을 선호하며, 복합 키 기반 커서보다 구현 단순성과 성능을 우선시한다.
📚 Learning: thip 프로젝트에서는 cqrs port 분리 시 다음 컨벤션을 따름: commandport에는 findbyxxx를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, querypo...
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
Applied to files:
src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java
📚 Learning: thip 프로젝트에서는 query api(조회 api)에 한해서는 application 계층에서 adapter.in.web.response 패키지의 response dto를 직접 ...
Learnt from: buzz0331
PR: THIP-TextHip/THIP-Server#78
File: src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java:3-3
Timestamp: 2025-07-14T18:22:56.538Z
Learning: THIP 프로젝트에서는 Query API(조회 API)에 한해서는 application 계층에서 adapter.in.web.response 패키지의 response DTO를 직접 참조하는 것을 허용함. 이는 CQRS 아키텍처에서 읽기 전용 작업의 효율성을 위한 팀 컨벤션임.
Applied to files:
src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.javasrc/main/java/konkuk/thip/feed/adapter/in/web/response/FeedShowByUserResponse.java
🧬 Code Graph Analysis (1)
src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSpecificUserTest.java (1)
src/test/java/konkuk/thip/common/util/TestEntityFactory.java (1)
TestEntityFactory(31-295)
⏰ 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 (21)
src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java (1)
13-27: JavaDoc 주석 추가로 코드 가독성 향상각 메서드 그룹별로 명확한 목적을 설명하는 JavaDoc 주석이 추가되어 코드의 이해도가 향상되었습니다. 특히 CQRS 패턴에서 Query Port의 역할을 명확히 구분하는 데 도움이 됩니다.
src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepository.java (1)
18-18: 새로운 메서드 선언이 기존 패턴과 일관성 있게 추가됨
findSpecificUserFeedsByCreatedAt메서드가 기존의 다른 피드 조회 메서드들과 동일한 시그니처 패턴(userId, lastCreatedAt, size)을 따라 일관성 있게 추가되었습니다.src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java (2)
56-57: 코드 포맷팅 개선기존 메서드의 닫는 괄호 포맷팅이 개선되어 코드 일관성이 향상되었습니다.
72-83: 새로운 메서드 구현이 기존 패턴을 올바르게 따름
findSpecificUserFeedsByCreatedAt메서드가 다음과 같이 올바르게 구현되었습니다:
- 커서에서
lastCreatedAt과size추출- 리포지토리 메서드 호출
createdAt을 기반으로 한 커서 인코딩- 기존 메서드들과 동일한
CursorBasedList구성 패턴커서 기반 페이지네이션 로직이 일관성 있게 적용되었습니다.
src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java (2)
199-216: 새로운 특정 유저 피드 조회 메서드가 올바르게 구현됨
findSpecificUserFeedsByCreatedAt메서드가 기존 패턴을 정확히 따라 구현되었습니다:
- ID 목록 조회 → 엔티티 조회 → DTO 변환의 3단계 접근법
- 순서 보존을 위한 Map 기반 정렬
- priority 정보 없이 DTO 변환 (일관성 유지)
233-247: 보안을 고려한 올바른 필터링 조건
fetchSpecificUserFeedIdsByCreatedAt헬퍼 메서드가 다음과 같이 올바르게 구현되었습니다:
ACTIVE상태 필터링- 특정 유저 ID 필터링
isPublic.eq(Boolean.TRUE)필터링으로 공개 피드만 조회 (중요한 보안 요소)- 커서 기반 페이지네이션 및 최신순 정렬
공개 피드만 반환하도록 하는 필터링은 사용자 프라이버시 보호를 위해 필수적입니다.
src/main/java/konkuk/thip/feed/adapter/in/web/FeedQueryController.java (2)
8-13: Import 구문 업데이트가 적절함
FeedShowMineUseCase를FeedShowAllOfUserUseCase로 대체하고 새로운 응답 DTO인FeedShowByUserResponse를 추가하여 리팩터링된 서비스 계층과 올바르게 연동됩니다.
27-27: 통합된 Use Case 사용으로 일관성 향상
feedShowMineUseCase필드가FeedShowAllOfUserUseCase타입으로 변경되어 "내 피드 조회"와 "특정 유저 피드 조회" 기능을 통합적으로 처리할 수 있게 되었습니다.src/main/java/konkuk/thip/feed/application/port/in/FeedShowAllOfUserUseCase.java (2)
3-4: 아키텍처 계층 분리 원칙 검토 필요UseCase 인터페이스에서
adapter.in.web.response패키지의 DTO를 직접 참조하고 있습니다. 일반적으로는 application 계층이 adapter 계층을 의존하지 않아야 하지만, 팀 컨벤션에서 Query API에 한해 허용한다고 학습되어 있으므로 이는 문제없습니다.
6-11: 인터페이스 설계가 명확하고 일관성 있음두 메서드 모두 cursor 기반 페이징을 지원하며, 메서드명이 직관적이고 파라미터 구성이 일관성 있게 설계되었습니다.
src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.java (2)
36-39: 새로운 매핑 메서드 추가가 적절함내 피드 조회를 위한 단순 매핑 메서드들이 추가되었습니다. 기존 패턴과 일관성 있게 구현되어 있고,
DateUtil.formatBeforeTime을 사용한 날짜 포맷팅도 적절합니다.
41-51: 특정 유저 피드를 위한 매핑 로직이 올바름저장/좋아요 정보를 포함한 매핑 메서드가 추가되었습니다.
savedFeedIds.contains()와likedFeedIds.contains()표현식을 사용한 플래그 설정이 적절하게 구현되어 있습니다.src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSpecificUserTest.java (4)
86-157: 포괄적인 통합 테스트 시나리오특정 유저의 피드 조회 API에 대한 핵심 시나리오들이 잘 구현되어 있습니다:
- 공개 피드만 조회되는지 확인
- 저장/좋아요 플래그가 올바르게 설정되는지 검증
- 최신순 정렬 확인
JDBC를 통한 직접적인 timestamp 조작으로 정렬 테스트를 구현한 것도 효과적입니다.
159-220: 공개 피드 필터링 로직 검증이 철저함다양한 공개/비공개 피드 조합을 통해 공개 피드만 반환되는지 확인하는 테스트가 잘 구현되어 있습니다. 8개의 피드 중 공개 피드 3개만 반환되는 것을 정확히 검증하고 있습니다.
222-308: 페이징 첫 페이지 로직 검증12개 피드 중 첫 10개를 반환하고
isLast: false를 설정하는 페이징 로직이 올바르게 테스트되었습니다. 피드 순서도 최신순으로 정확히 검증하고 있습니다.
310-374: 커서 기반 페이징 구현이 정확함커서를 사용한 다음 페이지 조회 테스트가 잘 구현되어 있습니다. DB에서 실제 timestamp를 조회하여 커서로 사용하는 현실적인 접근 방식이 좋습니다. 마지막 페이지에서
isLast: true를 반환하는 것도 올바르게 검증되었습니다.src/main/java/konkuk/thip/feed/adapter/in/web/response/FeedShowByUserResponse.java (2)
7-17: 페이징 정보를 포함한 응답 DTO 구조가 적절함Java record를 사용한 불변 DTO 설계가 좋습니다.
feedList,nextCursor,isLast필드로 페이징 정보가 명확하게 구성되어 있고, Swagger 어노테이션으로 API 문서화도 잘 되어 있습니다.
18-33: 피드 상세 정보 DTO가 포괄적으로 설계됨피드의 모든 필수 정보(ID, 날짜, 책정보, 콘텐츠, 통계, 사용자별 플래그)가 포함되어 있습니다.
isSaved,isLiked플래그를 통해 요청 사용자의 상호작용 상태도 제공하여 클라이언트에서 UI 상태 관리가 용이할 것입니다.src/main/java/konkuk/thip/feed/application/service/FeedShowAllOfUserService.java (3)
21-30: 서비스 클래스 구조와 의존성 관리가 적절함생성자 주입을 통한 의존성 관리, 상수로 정의된 페이지 크기, 그리고 필요한 포트들의 주입이 깔끔하게 구성되어 있습니다.
31-45: 내 피드 조회 로직이 간결하고 명확함커서 생성 → 피드 조회 → 응답 매핑의 단계별 처리가 명확합니다.
@Transactional(readOnly = true)사용도 적절합니다.
47-73: 특정 유저 피드 조회 로직이 효율적으로 구현됨특정 유저의 피드를 조회한 후, 요청 유저의 저장/좋아요 정보를 별도로 조회하여 매핑하는 로직이 효율적입니다.
Collectors.toUnmodifiableSet()을 사용한 것도 좋은 선택입니다.피드 ID 추출 → 저장/좋아요 정보 조회 → 매핑의 단계별 처리가 논리적이고 성능 효율적입니다.
buzz0331
left a comment
There was a problem hiding this comment.
수고하셨습니다~~ 사소한 리뷰 몇개 남겨놨습니다!!
| @Schema(description = "조회된 피드 목록") | ||
| List<FeedShowByUserResponse.FeedDto> feedList, | ||
|
|
||
| @Schema(description = "다음 페이지를 요청할 때 사용할 커서") | ||
| String nextCursor, | ||
|
|
||
| @Schema(description = "마지막 페이지 여부") | ||
| boolean isLast |
There was a problem hiding this comment.
오 저는 response까지는 굳이 필요할 것 같지 않아 설명을 작성하진 않았는데 친절하시네욥 😀
| public interface FeedShowAllOfUserUseCase { | ||
|
|
||
| FeedShowMineResponse showMyFeeds(Long userId, String cursor); | ||
|
|
||
| FeedShowByUserResponse showSpecificUserFeeds(Long userId, Long feedOwnerId, String cursor); | ||
| } |
| return new FeedShowMineResponse( | ||
| feedQueryMapper.toFeedShowMineResponse(result.contents()), | ||
| result.nextCursor(), | ||
| !result.hasNext() | ||
| ); |
|
|
||
| @Operation( | ||
| summary = "특정 유저 피드 조회", | ||
| description = "내가 아닌 다른 유저가 작성한 피드를 전체 조회합니다." |
There was a problem hiding this comment.
p3: api 이름을 '특정 유저 공개 피드 조회'라고 하는 것이 어떨까요? 내 피드 조회랑 차이를 두고 싶어서,,
| @GetMapping("/feeds/users/{userId}") | ||
| public BaseResponse<FeedShowByUserResponse> showSpecificUserFeeds( | ||
| @Parameter(hidden = true) @UserId final Long userId, | ||
| @Parameter(description = "해당 유저(= 피드 주인)의 userId 값") @PathVariable("userId") final Long feedOwnerId, | ||
| @Parameter(description = "커서 (첫번째 요청시 : null, 다음 요청시 : 이전 요청에서 반환받은 nextCursor 값)") | ||
| @RequestParam(value = "cursor", required = false) final String cursor) { | ||
| return BaseResponse.ok(feedShowMineUseCase.showSpecificUserFeeds(userId, feedOwnerId, cursor)); | ||
| } |
There was a problem hiding this comment.
p3: 메서드 네이밍도 처음에 피드 상세보기로 해석이 되어서 showUserPublicFeeds 이런식으로 바꿔보는 것 어떨까요!
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/main/java/konkuk/thip/feed/application/service/FeedShowAllOfUserService.java (1)
47-73: 특정 유저 피드 조회 로직이 올바르게 구현되었습니다.메서드의 단계별 처리 흐름이 명확하고, 스트림을 활용한 함수형 프로그래밍 패턴이 잘 적용되었습니다.
Collectors.toUnmodifiableSet()사용으로 불변성도 보장하고 있습니다.선택적 개선사항: 다중 DB 호출 최적화 고려
현재 피드 조회, 저장된 피드 조회, 좋아요한 피드 조회를 각각 별도로 수행하고 있습니다. 데이터양이 많아질 경우 성능 최적화를 위해 배치 쿼리나 조인을 활용한 단일 호출로 통합하는 것을 고려해볼 수 있습니다.
// 현재: 3번의 별도 DB 호출 CursorBasedList<FeedQueryDto> result = feedQueryPort.findSpecificUserFeedsByCreatedAt(feedOwnerId, nextCursor); Set<Long> savedFeedIdsByUser = savedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(feedIds, userId); Set<Long> likedFeedIdsByUser = postLikeQueryPort.findPostIdsLikedByUser(feedIds, userId); // 개선안: 피드 조회 시 저장/좋아요 정보를 함께 조회하는 메서드 추가 고려 // CursorBasedList<EnrichedFeedQueryDto> result = feedQueryPort.findSpecificUserFeedsWithUserPreferences(feedOwnerId, userId, nextCursor);다만 현재 구현도 충분히 명확하고 유지보수하기 좋으므로, 성능 이슈가 실제로 발생할 때 고려하시면 됩니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/main/java/konkuk/thip/feed/adapter/in/web/FeedQueryController.java(5 hunks)src/main/java/konkuk/thip/feed/application/port/in/FeedShowAllOfUserUseCase.java(1 hunks)src/main/java/konkuk/thip/feed/application/service/FeedShowAllOfUserService.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/main/java/konkuk/thip/feed/application/port/in/FeedShowAllOfUserUseCase.java
- src/main/java/konkuk/thip/feed/adapter/in/web/FeedQueryController.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#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 (2)
src/main/java/konkuk/thip/feed/application/service/FeedShowAllOfUserService.java (2)
21-29: 의존성 구조가 잘 설계되었습니다.서비스 클래스의 구조가 깔끔하게 구성되어 있고, 필요한 포트들이 적절히 주입되어 있습니다. PAGE_SIZE를 상수로 정의한 것도 좋은 관례입니다.
31-45: 내 피드 조회 로직이 간결하고 효율적입니다.커서 기반 페이지네이션을 활용한 구현이 기존 학습된 패턴과 일치하며, 트랜잭션 설정도 적절합니다. 코드가 읽기 쉽고 유지보수하기 좋게 작성되었습니다.
#️⃣ 연관된 이슈
📝 작업 내용
특정 유저가 작성한 공개 피드 목록을 최신순으로 조회하는 api를 개발하였습니다
하는 과정을 거쳐 response를 반환합니다
📸 스크린샷
💬 리뷰 요구사항
📌 PR 진행 시 이러한 점들을 참고해 주세요
Summary by CodeRabbit
신규 기능
버그 수정
문서화
테스트