[feat] 피드 조회 시 [내 정보 or 특정 유저 정보] 조회하는 api 개발#119
Conversation
Walkthrough피드 상단 화면 정보를 제공하는 새로운 API가 추가되었습니다. 내 피드와 특정 유저의 피드 상단 정보를 조회할 수 있는 GET 엔드포인트가 구현되었으며, 관련 도메인 서비스, 포트, 어댑터, DTO 및 테스트 코드가 함께 도입·수정되었습니다. 기존의 유저 이미지 URL 필드는 별도의 alias 엔티티로 이동되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
actor Client
participant FeedQueryController
participant FeedShowUserInfoService
participant UserCommandPort
participant FollowingQueryPort
participant FeedQueryPort
Client->>FeedQueryController: GET /feeds/mine/info (userId)
FeedQueryController->>FeedShowUserInfoService: showMyInfoInFeeds(userId)
FeedShowUserInfoService->>UserCommandPort: findUserById(userId)
FeedShowUserInfoService->>FollowingQueryPort: getLatestFollowerProfileImageUrlsByUserId(userId, 5)
FeedShowUserInfoService->>FeedQueryPort: countAllFeedsByUserId(userId)
FeedShowUserInfoService-->>FeedQueryController: FeedShowUserInfoResponse
FeedQueryController-->>Client: BaseResponse<FeedShowUserInfoResponse>
Client->>FeedQueryController: GET /feeds/users/{userId}/info
FeedQueryController->>FeedShowUserInfoService: showAnotherUserInfoInFeeds(userId)
FeedShowUserInfoService->>UserCommandPort: findUserById(userId)
FeedShowUserInfoService->>FollowingQueryPort: getLatestFollowerProfileImageUrlsByUserId(userId, 5)
FeedShowUserInfoService->>FeedQueryPort: countPublicFeedsByUserId(userId)
FeedShowUserInfoService-->>FeedQueryController: FeedShowUserInfoResponse
FeedQueryController-->>Client: BaseResponse<FeedShowUserInfoResponse>
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
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. 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
🧰 Additional context used🧠 Learnings (1)📓 Common learnings⏰ 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)
🔇 Additional comments (2)
✨ 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
♻️ Duplicate comments (1)
src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java (1)
77-80: 동일한 형변환 이슈 적용위
countAllFeedsByUserId메서드와 동일한 long→int 형변환 이슈가 적용됩니다. 일관성을 위해 같은 해결책을 적용하는 것이 좋겠습니다.
🧹 Nitpick comments (5)
src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepository.java (1)
16-16: 메서드명 단순화 및 파라미터 검증 고려메서드명이 매우 길어 가독성이 떨어집니다.
findLatestFollowerImageUrls(Long userId, int size)정도로 단순화하는 것을 고려해보세요.또한 size 파라미터에 대한 범위 제한(예: 최대 10개)을 문서화하거나 구현체에서 검증하는 것이 좋겠습니다.
- List<String> findTopFollowerProfileImageUrlsByUserIdOrderByCreatedAtDesc(Long userId, int size); + List<String> findLatestFollowerImageUrls(Long userId, int size);src/main/java/konkuk/thip/feed/application/port/in/FeedShowUserInfoUseCase.java (1)
5-10: 메서드명 개선 및 JavaDoc 추가 권장
메서드명에서 "Info" 중복을 제거하는 것을 고려해보세요:
showMyInfoInFeeds→showMyFeedsshowAnotherUserInfoInFeeds→showUserFeeds각 메서드의 구체적인 동작과 예외 상황에 대한 JavaDoc 추가를 권장합니다.
public interface FeedShowUserInfoUseCase { + /** + * 현재 사용자의 피드 정보를 조회합니다 (전체 피드 포함) + * @param userId 조회할 사용자 ID + * @return 사용자 피드 정보 + */ - FeedShowUserInfoResponse showMyInfoInFeeds(Long userId); + FeedShowUserInfoResponse showMyFeeds(Long userId); + /** + * 다른 사용자의 피드 정보를 조회합니다 (공개 피드만 포함) + * @param anotherUserId 조회할 다른 사용자 ID + * @return 사용자 피드 정보 + */ - FeedShowUserInfoResponse showAnotherUserInfoInFeeds(Long anotherUserId); + FeedShowUserInfoResponse showUserFeeds(Long anotherUserId); }src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepositoryImpl.java (1)
93-109: QueryDSL 구현이 올바르게 작성되었습니다최신 팔로워들의 프로필 이미지 URL을 조회하는 로직이 정확하게 구현되었습니다. 조인 관계와 필터링 조건이 적절하며, 생성일 기준 내림차순 정렬로 최신 팔로워를 가져오도록 했습니다.
성능 최적화를 위해
following.followingUserJpaEntity.userId와following.createdAt에 대한 인덱스 추가를 고려해볼 수 있습니다.src/main/java/konkuk/thip/feed/adapter/in/web/FeedQueryController.java (2)
53-56: API 문서화를 위한 @operation 애노테이션 추가 권장다른 엔드포인트들과 일관성을 위해
@Operation애노테이션을 추가하는 것이 좋겠습니다.+@Operation( + summary = "내 피드 상단 정보 조회", + description = "인증된 사용자의 피드 상단에 표시될 정보를 조회합니다." +) @GetMapping("/feeds/mine/info") public BaseResponse<FeedShowUserInfoResponse> showMyInfoInFeeds(@Parameter(hidden = true) @UserId final Long userId) {
58-63: API 문서화를 위한 @operation 애노테이션 추가 권장위 엔드포인트와 마찬가지로
@Operation애노테이션을 추가하여 API 문서화를 완성하는 것이 좋겠습니다.+@Operation( + summary = "특정 유저의 피드 상단 정보 조회", + description = "지정된 사용자의 피드 상단에 표시될 정보를 조회합니다." +) @GetMapping("/feeds/users/{userId}/info") public BaseResponse<FeedShowUserInfoResponse> showAnotherUserInfoInFeeds(
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (33)
src/main/java/konkuk/thip/feed/adapter/in/web/FeedQueryController.java(3 hunks)src/main/java/konkuk/thip/feed/adapter/in/web/response/FeedShowUserInfoResponse.java(1 hunks)src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java(1 hunks)src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedJpaRepository.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/in/FeedShowUserInfoUseCase.java(1 hunks)src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java(1 hunks)src/main/java/konkuk/thip/feed/application/service/FeedShowUserInfoService.java(1 hunks)src/main/java/konkuk/thip/record/adapter/out/persistence/repository/RecordQueryRepositoryImpl.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntity.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java(0 hunks)src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepository.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepositoryImpl.java(1 hunks)src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java(1 hunks)src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java(0 hunks)src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchControllerTest.java(0 hunks)src/test/java/konkuk/thip/book/adapter/in/web/BookMostSearchedBooksControllerTest.java(0 hunks)src/test/java/konkuk/thip/book/adapter/in/web/BookQueryControllerTest.java(0 hunks)src/test/java/konkuk/thip/common/util/TestEntityFactory.java(1 hunks)src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowUserInfoApiTest.java(1 hunks)src/test/java/konkuk/thip/record/adapter/in/web/RecordSearchApiTest.java(0 hunks)src/test/java/konkuk/thip/room/adapter/in/web/RoomCloseJoinApiTest.java(0 hunks)src/test/java/konkuk/thip/room/adapter/in/web/RoomCreateAPITest.java(0 hunks)src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListApiTest.java(0 hunks)src/test/java/konkuk/thip/room/adapter/in/web/RoomJoinApiTest.java(0 hunks)src/test/java/konkuk/thip/room/adapter/in/web/RoomPlayingDetailViewApiTest.java(0 hunks)src/test/java/konkuk/thip/room/adapter/in/web/RoomRecruitingDetailViewApiTest.java(0 hunks)src/test/java/konkuk/thip/room/adapter/in/web/RoomSearchApiTest.java(0 hunks)src/test/java/konkuk/thip/room/adapter/in/web/RoomVerifyPasswordAPITest.java(1 hunks)src/test/java/konkuk/thip/user/adapter/in/web/UserFollowApiTest.java(0 hunks)src/test/java/konkuk/thip/user/adapter/in/web/UserVerifyNicknameControllerTest.java(0 hunks)src/test/java/konkuk/thip/vote/adapter/in/web/VoteCreateControllerTest.java(0 hunks)
💤 Files with no reviewable changes (16)
- src/test/java/konkuk/thip/book/adapter/in/web/BookMostSearchedBooksControllerTest.java
- src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchControllerTest.java
- src/test/java/konkuk/thip/room/adapter/in/web/RoomCreateAPITest.java
- src/test/java/konkuk/thip/vote/adapter/in/web/VoteCreateControllerTest.java
- src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java
- src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListApiTest.java
- src/test/java/konkuk/thip/book/adapter/in/web/BookQueryControllerTest.java
- src/test/java/konkuk/thip/room/adapter/in/web/RoomSearchApiTest.java
- src/test/java/konkuk/thip/room/adapter/in/web/RoomJoinApiTest.java
- src/test/java/konkuk/thip/user/adapter/in/web/UserFollowApiTest.java
- src/test/java/konkuk/thip/user/adapter/in/web/UserVerifyNicknameControllerTest.java
- src/test/java/konkuk/thip/record/adapter/in/web/RecordSearchApiTest.java
- src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java
- src/test/java/konkuk/thip/room/adapter/in/web/RoomCloseJoinApiTest.java
- src/test/java/konkuk/thip/room/adapter/in/web/RoomRecruitingDetailViewApiTest.java
- src/test/java/konkuk/thip/room/adapter/in/web/RoomPlayingDetailViewApiTest.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 단일 커서 방식을 선호하며, 복합 키 기반 커서보다 구현 단순성과 성능을 우선시한다.
src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java (1)
Learnt from: seongjunnoh
PR: #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로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java (1)
Learnt from: seongjunnoh
PR: #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로부터 조회하는 메서드를 추가함.
🧬 Code Graph Analysis (2)
src/test/java/konkuk/thip/room/adapter/in/web/RoomVerifyPasswordAPITest.java (1)
src/test/java/konkuk/thip/common/util/TestEntityFactory.java (1)
TestEntityFactory(29-291)
src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowUserInfoApiTest.java (1)
src/test/java/konkuk/thip/common/util/TestEntityFactory.java (1)
TestEntityFactory(29-291)
⏰ 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 (23)
src/test/java/konkuk/thip/common/util/TestEntityFactory.java (1)
189-189: LGTM - 코드 포맷팅 개선파라미터 목록의 공백 추가로 코드 가독성이 향상되었습니다.
src/main/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntity.java (1)
46-46: LGTM – 도메인 모델 일관성 개선
imageUrl필드 제거 및updateFrom메서드 수정이 적절합니다.- 사용자 프로필 이미지를 alias 엔티티(
aliasForUserJpaEntity)로 관리하는 설계가 더 일관성 있습니다.- 추가 확인 필요: 코드베이스 전반에서 여전히
user.imageUrl직접 참조가 남아 있지 않은지 수동으로 점검해주세요.src/test/java/konkuk/thip/room/adapter/in/web/RoomVerifyPasswordAPITest.java (1)
75-75: LGTM - 테스트 코드 개선수동 엔티티 생성을 팩토리 메서드로 대체하여 테스트 코드의 일관성과 유지보수성이 향상되었습니다.
src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java (2)
6-7: LGTM - 필요한 임포트 추가새로운 메서드의 반환 타입을 위한 List 임포트가 적절히 추가되었습니다.
12-12: LGTM - 피드 기능을 위한 적절한 메서드 추가최신 팔로워의 프로필 이미지 URL을 조회하는 메서드가 적절히 추가되었습니다. size 파라미터를 통한 조회 수 제한으로 성능도 고려되었으며, CQRS 패턴에 맞게 QueryPort에 배치되었습니다.
src/main/java/konkuk/thip/record/adapter/out/persistence/repository/RecordQueryRepositoryImpl.java (1)
206-206: 변경사항 확인 완료 – 추가 수정 불필요
src/main/java/하위의 모든 Repository 구현체에서user.imageUrl직접 참조가 없음을 확인했습니다.해당 변경으로 인한 추가 조치는 필요하지 않습니다.
src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java (1)
224-224: aliasForUserJpaEntity는 nullable=false로 NPE 발생 우려 없음UserJpaEntity 에서
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_alias_id", nullable = false) private AliasJpaEntity aliasForUserJpaEntity;로 정의되어 있어 DB와 JPA 매핑 차원에서 null을 허용하지 않고, 테스트 코드에서도 항상 alias를 주입하고 있습니다.
따라서getAliasForUserJpaEntity().getImageUrl()호출 시 NPE 걱정 없이 그대로 사용하셔도 됩니다.src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java (1)
44-47: 구현이 적절합니다단순한 위임 구조로 깔끔하게 구현되었습니다. 레이어 간 책임 분리가 잘 되어 있습니다.
src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java (1)
19-21: 메서드명 변경 관련 코드베이스 일관성 확인 완료
countFeedsByUserId호출은 더 이상 존재하지 않으며,
모든 참조가countAllFeedsByUserId와countPublicFeedsByUserId로 올바르게 업데이트된 것을 확인했습니다.추가 조치가 필요 없습니다.
src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java (1)
72-75: long에서 int로의 형변환 시 오버플로우 위험성 검토 필요개발자 코멘트에서도 확인되듯이 형변환에 대한 우려가 있습니다.
Integer.MAX_VALUE(약 21억)를 초과하는 피드 수가 있을 경우 데이터 손실이 발생할 수 있습니다.다음 중 하나의 방법을 고려해보세요:
- 포트 인터페이스와 서비스 레이어에서
long타입 사용- 오버플로우 체크 로직 추가
public int countAllFeedsByUserId(Long userId) { - // int 로 강제 형변환 해도 괜찮죠?? - return (int) feedJpaRepository.countAllFeedsByUserId(userId, StatusType.ACTIVE); + long count = feedJpaRepository.countAllFeedsByUserId(userId, StatusType.ACTIVE); + if (count > Integer.MAX_VALUE) { + throw new IllegalStateException("Feed count exceeds integer limit: " + count); + } + return (int) count; }src/main/java/konkuk/thip/feed/adapter/in/web/response/FeedShowUserInfoResponse.java (1)
7-16: 응답 구조가 적절하게 설계되었습니다피드 상단 화면에 필요한 모든 정보를 포함하고 있으며,
@Builder애노테이션을 통해 객체 생성이 용이하도록 구성되어 있습니다. 필드명도 명확하고 타입도 적절합니다.src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedJpaRepository.java (2)
11-12: 메서드명 변경과 쿼리 구현이 적절합니다기존
countFeedsByUserId에서countAllFeedsByUserId로 변경하여 의도를 명확히 했고, JPQL 쿼리도 올바르게 구현되었습니다.
14-15: 공개 피드만 조회하는 로직이 올바르게 구현되었습니다
isPublic = TRUE조건을 추가하여 공개 피드만 카운트하도록 구현했습니다. 다른 사용자의 피드 정보 조회 시 개인정보 보호를 위한 중요한 구분입니다.src/main/java/konkuk/thip/feed/application/service/FeedShowUserInfoService.java (5)
3-14: 적절한 import 구성필요한 의존성들이 명확하게 import되어 있고, 불필요한 import가 없어 깔끔합니다.
19-22: 적절한 상수 정의와 의존성 구성팔로워 프로필 이미지 표시 제한을 상수로 정의한 것이 좋고, 필요한 포트들이 명확하게 구성되어 있습니다.
24-37: 내 피드 정보 조회 로직이 적절함읽기 전용 트랜잭션 설정이 적절하고, 사용자 조회 → 팔로워 프로필 이미지 조회 → 전체 피드 수 조회의 순서가 논리적입니다. 내 피드에서는 모든 피드(공개/비공개 포함)를 카운트하는 것이 올바른 비즈니스 로직입니다.
39-52: 다른 유저 피드 정보 조회 로직이 적절함공개 피드만 카운트하는 것이 프라이버시 보호 측면에서 올바른 비즈니스 로직입니다. 전체적인 구조도 내 피드 조회와 일관성 있게 구성되어 있습니다.
54-64: 응답 생성 헬퍼 메서드가 잘 구성됨private 메서드로 적절히 캡슐화되어 있고, alias 엔티티에서 이미지 URL을 가져오는 것이 도메인 모델 변경에 맞게 올바르게 구현되어 있습니다. 모든 필요한 필드가 명확하게 매핑되어 있습니다.
src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowUserInfoApiTest.java (5)
32-53: 테스트 설정과 정리가 적절함통합 테스트 어노테이션 구성이 올바르고, tearDown에서 외래키 제약조건을 고려한 순서로 데이터를 정리하고 있습니다. JdbcTemplate 주입으로 타임스탬프 조작이 가능한 점도 좋습니다.
55-98: 포괄적인 내 피드 정보 조회 테스트테스트 시나리오가 잘 구성되어 있고, 특히 팔로워 프로필 이미지의 최신순 정렬 검증이 중요한 비즈니스 로직을 적절히 테스트하고 있습니다. 전체 피드 수(공개+비공개) 카운트 검증도 올바릅니다.
100-162: 팔로워 제한 로직에 대한 경계값 테스트7명의 팔로워 중 최신 5명만 반환되는 제한 로직을 정확히 검증하고 있습니다. 타임스탬프 조작을 통한 순서 제어도 적절하게 구현되어 있습니다.
164-207: 다른 유저 피드 정보 조회의 프라이버시 로직 검증다른 유저의 피드에서 공개 피드만 카운트되는 중요한 프라이버시 로직을 정확히 검증하고 있습니다. 비공개글 2개가 제외되어 총 2개만 카운트되는 것을 확인하는 테스트가 적절합니다.
209-270: 다른 유저 컨텍스트에서의 팔로워 제한 검증다른 유저의 피드에서도 팔로워 프로필 이미지 제한(최대 5개) 로직이 동일하게 작동하는지 검증하는 완성도 높은 테스트입니다. 전체적으로 테스트 커버리지가 우수합니다.
- 가독성을 위해 메서드 네이밍 길이 축소
buzz0331
left a comment
There was a problem hiding this comment.
수고하셨습니다~ 리뷰 겸 간단한 제안 하나 남겼는데 확인 부탁드려요!
| private FeedShowUserInfoResponse buildResponse(User feedOwner, int totalFeedCount, List<String> latestFollowerProfileImageUrls) { | ||
| return FeedShowUserInfoResponse.builder() | ||
| .profileImageUrl(feedOwner.getAlias().getImageUrl()) | ||
| .nickname(feedOwner.getNickname()) | ||
| .aliasName(feedOwner.getAlias().getValue()) | ||
| .aliasColor(feedOwner.getAlias().getColor()) | ||
| .followerCount(feedOwner.getFollowerCount()) | ||
| .totalFeedCount(totalFeedCount) | ||
| .latestFollowerProfileImageUrls(latestFollowerProfileImageUrls) | ||
| .build(); | ||
| } |
There was a problem hiding this comment.
p2: 현재 저희가 MapStruct를 queryDto -> responseDto를 매핑하는 용도로만 사용 중인 것 같은데 단순히 dto를 파싱하는 부분도 MapStruct에게 책임을 위임하는 것이 어떨까요? 처음에 mapper 이름을 FollowDtoMapper 이런식으로 지은 것도 이러한 책임을 의도했기 때문인데, 이러한 의도를 전달하지 못했었네요!
이렇게 하면 서비스 클래스에서는 비즈니스 로직만 집중할 수 있고, dto 변환 로직이 분리되어 코드 가독성과 책임 분리가 더 명확해질 것 같아요!!
There was a problem hiding this comment.
좋은 것 같습니다!! 저희는 하나의 서비스 클래스가 많은 책임을 가지는 게 아니라, 하나의 유스케이스 동작만을 담당하는 경우가 많아서 response 구성은 서비스에서 해도 문제없다고 생각했었는데 매퍼로 분리해도 좋습니다!
다만 이러면 매퍼에 해당 도메인에 속하는 서비스의 response 를 생성하는 메서드가 모두 포함되게 되는데, 이 부분은 괜찮다고 생각하시나여??
There was a problem hiding this comment.
엇 저도 처음엔 성준님 의견처럼 저희가 서비스 클래스 하나당 하나의 유즈케이스만 담당하니까 상관없다고 생각했는데, 점점 controller에 주입하는 usecase가 많아질수록 비슷한 usecase들끼리는 같은 서비스 클래스에서 담당하도록 하는 것이 좋을 것 같다는 생각이 들었습니다! 따라서 mapper가 매핑 책임을 담당하도록 해서 서비스 클래스의 책임을 분리하면 추후에 저희가 비슷한 usecase들을 하나의 클래스에 모을때 더 수월할 것 같습니다!
response를 생성하는 메서드라고 하면 getter들을 말씀하시는 것 맞을까요? 제가 생각한 코드는 다음과 같은 느낌이였습니다!
@Mapper(componentModel = "spring")
public interface FeedQueryMapper {
@Mapping(target = "profileImageUrl", source = "feedOwner.alias.imageUrl")
@Mapping(target = "nickname", source = "feedOwner.nickname")
@Mapping(target = "aliasName", source = "feedOwner.alias.value")
@Mapping(target = "aliasColor", source = "feedOwner.alias.color")
@Mapping(target = "followerCount", source = "feedOwner.followerCount")
@Mapping(target = "totalFeedCount", source = "totalFeedCount")
@Mapping(target = "latestFollowerProfileImageUrls", source = "latestFollowerProfileImageUrls")
FeedShowUserInfoResponse toFeedShowUserInfoResponse(User feedOwner,
int totalFeedCount,
List<String> latestFollowerProfileImageUrls);
}return feedShowUserInfoMapper.toFeedShowUserInfoResponse(feedOwner, allFeedCount, latestFollowerProfileImageUrls);There was a problem hiding this comment.
유스케이스가 추가될때마다 현준님 예시코드에서의 toFeedShowUserInfoResponse 와 같은 dto -> response로의 매핑 메서드가 계속 늘어나는 걸 얘기한 거였습니다!
mapper 에 추가해보겟습니닷
There was a problem hiding this comment.
아하 음 제 생각은 모든 api에서 dto를 파싱할때 사용하지 않고 현재 성준님이 구현하신 api처럼 반환 필드가 많아서 따로 메서드를 추출해야 하는 경우에만 사용하면 mapper에 너무 많은 메서드가 쌓이지 않을 것 같습니다!
There was a problem hiding this comment.
아하아하 의도를 이제야 이해했네요!! 좋습니다!!
| @Column(name = "image_url", columnDefinition = "TEXT", nullable = false) | ||
| private String imageUrl; | ||
|
|
| public int countAllFeedsByUserId(Long userId) { | ||
| // int 로 강제 형변환 해도 괜찮죠?? | ||
| return (int) feedJpaRepository.countFeedsByUserId(userId, StatusType.ACTIVE); | ||
| return (int) feedJpaRepository.countAllFeedsByUserId(userId, StatusType.ACTIVE); | ||
| } | ||
|
|
||
| @Override | ||
| public int countPublicFeedsByUserId(Long userId) { | ||
| return (int) feedJpaRepository.countPublicFeedsByUserId(userId, StatusType.ACTIVE); | ||
| } |
There was a problem hiding this comment.
여기서는 파라미터로 statusType까지 전달해서 동적으로 필터링한 이유가 있나요?? (단순 궁금)
There was a problem hiding this comment.
그냥 jpa repository 내부에서 status = ACTIVE 구문을 추가할까 하다가, 영속성 adapter 에서 주입해주는게 "status가 ACTIVE인 것을 찾는다" 라는 것을 보여주고 싶어서 위와 같이 구현해보았습니다
| @Override | ||
| public List<String> findLatestFollowerImageUrls(Long userId, int size) { | ||
| QFollowingJpaEntity following = QFollowingJpaEntity.followingJpaEntity; | ||
| QUserJpaEntity follower = QUserJpaEntity.userJpaEntity; // userId 를 팔로우하는 사람들(= follower) | ||
| QAliasJpaEntity alias = QAliasJpaEntity.aliasJpaEntity; | ||
|
|
||
| return jpaQueryFactory | ||
| .select(alias.imageUrl) | ||
| .from(following) | ||
| .join(following.userJpaEntity, follower) | ||
| .join(follower.aliasForUserJpaEntity, alias) | ||
| .where(following.followingUserJpaEntity.userId.eq(userId) | ||
| .and(following.status.eq(StatusType.ACTIVE))) | ||
| .orderBy(following.createdAt.desc()) | ||
| .limit(size) | ||
| .fetch(); | ||
| } |
- 유효성 검증 - CursorBasedList -> Response DTO 파싱 - Response DTO에 필요한 필드 추가 쿼리 요청
#️⃣ 연관된 이슈
📝 작업 내용
피드 조회 화면의 상단 부분을 구성하는 [유저 정보, 유저의 팔로워 정보, 유저가 작성한 피드 개수] 등을 반환하는 api 를 개발하였습니다.
현재 유저의 피드 조회 api가 [내 피드 조회, 특정 유저의 피드 조회] 이렇게 2가지로 있어서, 해당 api도 2개로 분리하였습니다!!
api 주요 로직
그 외 수정사항
📸 스크린샷
💬 리뷰 요구사항
📌 PR 진행 시 이러한 점들을 참고해 주세요
Summary by CodeRabbit
신규 기능
버그 수정
테스트
리팩토링