[feat] 내가 팔로잉하는 유저들 중, 최근 공개 피드 작성한 유저 정보 조회 api 개발#199
Conversation
- 유저가 작성한 여러 공개 피드들 중, 가장 최근에 작성한 피드의 createdAt 기준으로 count + 정렬 되는지 확인하는 테스트 코드 추가
Walkthrough팔로잉한 사용자 중 최근에 공개 피드를 작성한 작성자 목록(최대 10명)을 조회하는 신규 API를 추가했다. 컨트롤러 엔드포인트, 응용 서비스/포트/매퍼, 영속 어댑터/리포지토리(QueryDSL), 응답 DTO, Query DTO 프로젝션, 통합 테스트가 추가·확장되었다. Changes
Sequence Diagram(s)sequenceDiagram
participant C as Client
participant WC as UserQueryController
participant S as UserShowFollowingRecentWritersService
participant P as UserQueryPort
participant PA as UserQueryPersistenceAdapter
participant R as UserQueryRepository
participant DB as Database
participant M as UserQueryMapper
C->>WC: GET /users/my-followings/recent-feeds (userId)
WC->>S: showMyFollowingRecentWriters(userId)
S->>P: findRecentFeedWritersOfMyFollowings(userId, 10)
P->>PA: delegate
PA->>R: findFeedWritersOfMyFollowingsOrderByCreatedAtDesc(userId, 10)
R->>DB: Query (followings + feeds + alias)
DB-->>R: List<UserQueryDto>
R-->>PA: List<UserQueryDto>
PA-->>P: List<UserQueryDto>
P-->>S: List<UserQueryDto>
S->>M: toRecentWriterResponses(dtos)
M-->>S: UserFollowingRecentWritersResponse
S-->>WC: Response DTO
WC-->>C: 200 OK (BaseResponse<UserFollowingRecentWritersResponse>)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes(없음) Possibly related PRs
Suggested reviewers
Poem
✨ 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. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (6)
src/main/java/konkuk/thip/user/application/service/UserShowFollowingRecentWritersService.java (2)
14-14: SIZE 상수를 설정 가능하도록 개선 고려현재 SIZE가 하드코딩된 상수로 정의되어 있습니다. 향후 요구사항 변경 시 유연성을 위해 application.yml에서 설정 가능하도록 하는 것을 고려해보세요.
- private static final int SIZE = 10; + @Value("${user.following.recent-writers.size:10}") + private int size;그리고 Line 20에서도 변경:
- return userQueryMapper.toRecentWriterResponses(userQueryPort.findRecentFeedWritersOfMyFollowings(userId, SIZE)); + return userQueryMapper.toRecentWriterResponses(userQueryPort.findRecentFeedWritersOfMyFollowings(userId, size));
19-21: 트랜잭션 설정 확인 필요읽기 전용 작업이므로
@Transactional(readOnly = true)를 추가하여 성능을 최적화할 수 있습니다.+ @Transactional(readOnly = true) @Override public UserFollowingRecentWritersResponse showMyFollowingRecentWriters(Long userId) { return userQueryMapper.toRecentWriterResponses(userQueryPort.findRecentFeedWritersOfMyFollowings(userId, SIZE)); }필요한 import 추가:
import org.springframework.transaction.annotation.Transactional;src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java (1)
12-12: 와일드카드 import 사용 지양와일드카드 import는 코드의 명확성을 떨어뜨릴 수 있습니다. 명시적인 import를 사용하는 것이 좋습니다.
-import konkuk.thip.user.application.port.in.*; +import konkuk.thip.user.application.port.in.UserViewAliasChoiceUseCase; +import konkuk.thip.user.application.port.in.UserGetFollowUsecase; +import konkuk.thip.user.application.port.in.UserIsFollowingUsecase; +import konkuk.thip.user.application.port.in.UserVerifyNicknameUseCase; +import konkuk.thip.user.application.port.in.UserSearchUsecase; +import konkuk.thip.user.application.port.in.UserMyPageUseCase; +import konkuk.thip.user.application.port.in.UserShowFollowingRecentWritersUseCase;src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepositoryImpl.java (2)
178-179: JOIN 조건 개선 가능현재
join(feed).on(feed.userJpaEntity.eq(writer))구문은 동작하지만, QueryDSL의 일반적인 패턴과 다릅니다. 더 명확한 조인 구문을 사용하는 것이 좋습니다.- .join(feed).on(feed.userJpaEntity.eq(writer)) + .join(QFeedJpaEntity.feedJpaEntity, feed).on(feed.userJpaEntity.eq(writer))
187-191: GROUP BY 절 최적화 고려현재
alias.imageUrl을 GROUP BY에 포함시키고 있는데, 이는 NULL 값이 있을 때 여러 행이 생성될 수 있습니다. writer의 고유 식별자만으로 그룹핑하고 집계 함수를 사용하는 것이 더 효율적일 수 있습니다..groupBy( // 그룹핑 writer.userId, - writer.nickname, - alias.imageUrl + writer.nickname )alias.imageUrl은 이미 LEFT JOIN으로 가져오므로, SELECT 절에서 MAX나 MIN 같은 집계 함수를 사용할 수도 있습니다:
.select(new QUserQueryDto( writer.userId, writer.nickname, - alias.imageUrl + alias.imageUrl.max() ))src/test/java/konkuk/thip/user/adapter/in/web/UserShowFollowingRecentWritersApiTest.java (1)
125-228: 최대 10개 제한 테스트의 반복 코드를 개선할 수 있습니다.테스트 로직은 정확하지만 12명의 사용자와 피드를 생성하는 반복 코드가 많습니다.
헬퍼 메서드를 사용하여 코드를 간소화할 수 있습니다:
+private void createUsersAndFeeds(AliasJpaEntity alias, UserJpaEntity me, int count, BookJpaEntity book) { + List<UserJpaEntity> users = new ArrayList<>(); + for (int i = 1; i <= count; i++) { + UserJpaEntity user = userJpaRepository.save(TestEntityFactory.createUser(alias, "user" + i)); + users.add(user); + followingJpaRepository.save(TestEntityFactory.createFollowing(me, user)); + FeedJpaEntity feed = feedJpaRepository.save(TestEntityFactory.createFeed(user, book, true, 50, 10, List.of())); + + feedJpaRepository.flush(); + LocalDateTime base = LocalDateTime.now(); + jdbcTemplate.update( + "UPDATE posts SET created_at = ? WHERE post_id = ?", + Timestamp.valueOf(base.minusMinutes(65 - i * 5)), feed.getPostId()); + } + return users; +}그러나 현재 코드도 테스트의 의도가 명확하므로 필수적인 개선은 아닙니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java(3 hunks)src/main/java/konkuk/thip/user/adapter/in/web/response/UserFollowingRecentWritersResponse.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/persistence/UserQueryPersistenceAdapter.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepository.java(1 hunks)src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepositoryImpl.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(1 hunks)src/main/java/konkuk/thip/user/application/port/out/UserQueryPort.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(1 hunks)src/test/java/konkuk/thip/user/adapter/in/web/UserShowFollowingRecentWritersApiTest.java(1 hunks)
🧰 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 (16)
src/main/java/konkuk/thip/user/application/port/out/dto/UserQueryDto.java (2)
27-34: 새로운 3-arg 생성자 구현이 적절합니다.QueryDSL 프로젝션을 위한 새로운 생성자가 올바르게 구현되었습니다. null 값으로 초기화하여 최근 피드 작성자 정보 조회에 필요한 최소한의 필드만 사용하는 설계가 합리적입니다.
21-24: 검증 로직 주석 처리 안전 확인 완료7-arg 생성자를 직접 호출하거나 QueryDSL Projections로 사용하는 곳이 코드베이스에서 발견되지 않아, 해당 검증 로직 주석 처리는 다른 사용처에 영향을 주지 않습니다.
src/main/java/konkuk/thip/user/adapter/in/web/response/UserFollowingRecentWritersResponse.java (1)
7-11: API 응답 DTO 설계가 우수합니다.Swagger 문서화와 명확한 필드명을 통해 API 스펙이 잘 정의되었습니다. 중첩된 RecentWriter 레코드로 구조를 명확하게 분리한 것도 좋습니다.
src/main/java/konkuk/thip/user/application/mapper/UserQueryMapper.java (1)
16-23: 매핑 로직이 효율적으로 구현되었습니다.MapStruct를 활용한 매핑 메서드들이 잘 구성되었습니다. 특히 default 메서드로 래핑 로직을 제공하여 서비스 레이어에서 간편하게 사용할 수 있도록 한 것이 좋습니다.
src/main/java/konkuk/thip/user/application/port/out/UserQueryPort.java (1)
29-29: 포트 메서드 시그니처가 명확합니다.메서드명과 파라미터가 기능을 잘 표현하고 있으며, 기존 포트 인터페이스와 일관성을 유지하고 있습니다.
src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepository.java (1)
21-21: 리포지토리 메서드 네이밍이 명확합니다.메서드명이 수행하는 작업을 정확히 표현하고 있으며, 정렬 기준(OrderByCreatedAtDesc)도 명시적으로 나타내고 있습니다. 이전 학습에서 확인된 바와 같이 단순한 LocalDateTime 기반 정렬을 선호하는 패턴과도 일치합니다.
src/main/java/konkuk/thip/user/adapter/out/persistence/UserQueryPersistenceAdapter.java (1)
72-75: 메소드 구현이 깔끔합니다!QueryDSL을 통해 데이터를 직접 조회하여 N+1 문제를 방지한 점이 좋습니다.
src/main/java/konkuk/thip/user/application/port/in/UserShowFollowingRecentWritersUseCase.java (1)
1-8: 인터페이스 설계가 적절합니다!단일 책임 원칙에 따라 use case를 분리한 점이 좋습니다.
src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java (1)
160-169: 새로운 엔드포인트 구현이 적절합니다!API 설계가 RESTful하고, 적절한 설명과 파라미터 처리가 잘 되어 있습니다.
src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepositoryImpl.java (1)
164-195: 쿼리 최적화가 잘 되었습니다!QueryDSL과 Query Projection을 활용하여 N+1 문제를 방지하고, 필요한 필드만 조회하도록 구현한 점이 훌륭합니다. GROUP BY와 MAX 집계 함수를 사용한 정렬 로직도 요구사항에 정확히 부합합니다.
src/test/java/konkuk/thip/user/adapter/in/web/UserShowFollowingRecentWritersApiTest.java (6)
1-31: 임포트와 기본 설정이 적절하게 구성되었습니다.통합 테스트에 필요한 모든 의존성이 올바르게 임포트되었고, Spring Boot 테스트 환경 설정도 적합합니다.
34-38: 테스트 클래스 설정이 올바릅니다.
@AutoConfigureMockMvc(addFilters = false)설정으로 인증 필터를 비활성화하여 테스트 환경에서 인증 없이 API를 테스트할 수 있도록 구성했습니다.
76-87: JDBC를 통한 타임스탬프 조작이 효과적입니다.JPA 엔티티의
@CreatedDate어노테이션으로 인해 자동 생성되는 타임스탬프를 JDBC로 직접 수정하는 방식이 테스트 데이터 설정에 적합합니다. 시간 순서를 명확하게 제어할 수 있어 테스트의 예측 가능성을 높입니다.
90-101: 첫 번째 테스트 케이스가 핵심 요구사항을 잘 검증합니다.팔로잉한 사용자 중 공개 피드를 작성한 사용자만 조회되고, 최근 작성 순으로 정렬되는 것을 검증합니다. 응답 필드(userId, nickname, profileImageUrl)도 올바르게 확인하고 있습니다.
103-123: 비공개 피드 필터링 테스트가 중요한 엣지 케이스를 검증합니다.비공개 피드를 작성한 사용자가 결과에서 제외되는 것을 확인하는 테스트로, 개인정보 보호 요구사항을 잘 검증합니다.
230-269: 사용자별 최신 피드 기준 정렬 테스트가 핵심 비즈니스 로직을 검증합니다.user1이 두 개의 피드(f1, f3)를 작성했을 때, 가장 최신 피드(f3)의 시간을 기준으로 정렬되는 것을 확인하는 중요한 테스트 케이스입니다. 이는 PR 목표의 핵심인 "각 유저의 가장 최근 공개피드의 createdAt 기준으로 정렬" 요구사항을 정확히 검증합니다.
| @AfterEach | ||
| void tearDown() { | ||
| feedJpaRepository.deleteAllInBatch(); | ||
| followingJpaRepository.deleteAllInBatch(); | ||
| userJpaRepository.deleteAllInBatch(); | ||
| aliasJpaRepository.deleteAllInBatch(); | ||
| bookJpaRepository.deleteAllInBatch(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
데이터 정리 순서를 외래키 제약조건에 맞게 조정해야 합니다.
현재 정리 순서가 외래키 제약조건을 위반할 수 있습니다. 참조되는 엔티티를 먼저 삭제하면 제약조건 위반 오류가 발생할 수 있습니다.
다음과 같이 의존성 순서에 맞게 수정하세요:
@AfterEach
void tearDown() {
feedJpaRepository.deleteAllInBatch();
followingJpaRepository.deleteAllInBatch();
- userJpaRepository.deleteAllInBatch();
- aliasJpaRepository.deleteAllInBatch();
bookJpaRepository.deleteAllInBatch();
+ userJpaRepository.deleteAllInBatch();
+ aliasJpaRepository.deleteAllInBatch();
}📝 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.
| @AfterEach | |
| void tearDown() { | |
| feedJpaRepository.deleteAllInBatch(); | |
| followingJpaRepository.deleteAllInBatch(); | |
| userJpaRepository.deleteAllInBatch(); | |
| aliasJpaRepository.deleteAllInBatch(); | |
| bookJpaRepository.deleteAllInBatch(); | |
| } | |
| @AfterEach | |
| void tearDown() { | |
| feedJpaRepository.deleteAllInBatch(); | |
| followingJpaRepository.deleteAllInBatch(); | |
| bookJpaRepository.deleteAllInBatch(); | |
| userJpaRepository.deleteAllInBatch(); | |
| aliasJpaRepository.deleteAllInBatch(); | |
| } |
🤖 Prompt for AI Agents
In
src/test/java/konkuk/thip/user/adapter/in/web/UserShowFollowingRecentWritersApiTest.java
around lines 48-55, the tearDown deletes repositories in an order that can
violate foreign-key constraints; change the deletion order to remove dependent
child records first then parents — e.g., delete feedJpaRepository, then
followingJpaRepository, then aliasJpaRepository, then bookJpaRepository, and
finally userJpaRepository (keep using deleteAllInBatch calls), so all child rows
are removed before their referenced parent rows.
hd0rable
left a comment
There was a problem hiding this comment.
수고하셨습니다!! 간단히 리뷰남겼는데 확인 부탁드립니다!!
|
|
||
| import java.util.List; | ||
|
|
||
| @Schema(description = "전체 피드 조회 상단에서의 내 띱 목록 응답 DTO") |
| .from(following) | ||
| .join(following.followingUserJpaEntity, writer) // writer : 나에게 팔로잉 당하는 사람들 | ||
| .join(feed).on(feed.userJpaEntity.eq(writer)) | ||
| .leftJoin(writer.aliasForUserJpaEntity, alias) |
There was a problem hiding this comment.
p2: 저희 모든 유저가 alias와 1대1 매핑관계이고, 모든 유저가 alias를 가지고있으니 inner join으로 하는 것은 어떨까요?
There was a problem hiding this comment.
오 예리하시네요 UserJpaEntity와 연관된 AliasJpaEntity는 nullable = false 이니 inner join이 더 성능적으로 좋을 것 같네요! 수정하겠습니다!
|
|
||
| } No newline at end of file | ||
| // 단건 매핑: UserQueryDto -> RecentWriter | ||
| UserFollowingRecentWritersResponse.RecentWriter toRecentWriter(UserQueryDto dto); |
There was a problem hiding this comment.
넵 맞습니다! map struct 가 사용할 매핑 메서드를 미리 인터페이스에 정의해놓았습니다
| @RequiredArgsConstructor | ||
| public class UserShowFollowingRecentWritersService implements UserShowFollowingRecentWritersUseCase { | ||
|
|
||
| private static final int SIZE = 10; |
There was a problem hiding this comment.
p2: 페이징 처리 필요하지않으면 사용하지 않으니 빼셔도 좋을듯합니다!
There was a problem hiding this comment.
이 부분은 아마 기본 사이즈라도 유지보수하기 쉽게 서비스 로직에서 LIMIT도 넘겨주기로 통일했던 것 같습니다!
| private final UserQueryPort userQueryPort; | ||
| private final UserQueryMapper userQueryMapper; | ||
|
|
||
| @Override |
There was a problem hiding this comment.
엇 서비스에서 도메인 조회가 없어서 트랜잭션을 추가하진 않았는데 팀 컨벤션 차원으로 추가해보겟습니다!
| } | ||
|
|
||
| @Test | ||
| @DisplayName("유저가 가장 최근에 작성한 피드의 작성 시각(= createdAt)을 기준으로 정렬하여 반환된다.") |
| .where( | ||
| following.userJpaEntity.userId.eq(userId) // 내가 팔로잉 하는 사람 | ||
| .and(writer.status.eq(StatusType.ACTIVE)) // 그 중 아직 회원인 사람 | ||
| .and(feed.status.eq(StatusType.ACTIVE)) // 그 중 삭제 X & 공개피드를 작성한 사람 | ||
| .and(feed.isPublic.isTrue()) | ||
| ) | ||
| .groupBy( // 그룹핑 | ||
| writer.userId, | ||
| writer.nickname, | ||
| alias.imageUrl | ||
| ) |
| List<UserFollowingRecentWritersResponse.RecentWriter> toRecentWriterList(List<UserQueryDto> dtos); | ||
| // 래핑: List<UserQueryDto> -> UserFollowingRecentWritersResponse | ||
| default UserFollowingRecentWritersResponse toRecentWriterResponses(List<UserQueryDto> dtos) { | ||
| return new UserFollowingRecentWritersResponse(toRecentWriterList(dtos)); | ||
| } |
There was a problem hiding this comment.
오호 dto 생성 책임도 mapper가 갖도록 했네요! 좋습니다! 서비스 로직이 가벼워질 것 같아요
| // Assert.notNull(aliasName, "aliasName must not be null"); | ||
| // Assert.notNull(aliasColor, "aliasColor must not be null"); | ||
| // Assert.notNull(followerCount, "followerCount must not be null"); // 내 팔로잉 목록 조회에서는 필요 x | ||
| Assert.notNull(createdAt, "createdAt must not be null"); | ||
| // Assert.notNull(createdAt, "createdAt must not be null"); |
| @RequiredArgsConstructor | ||
| public class UserShowFollowingRecentWritersService implements UserShowFollowingRecentWritersUseCase { | ||
|
|
||
| private static final int SIZE = 10; |
There was a problem hiding this comment.
이 부분은 아마 기본 사이즈라도 유지보수하기 쉽게 서비스 로직에서 LIMIT도 넘겨주기로 통일했던 것 같습니다!
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/main/java/konkuk/thip/user/application/service/UserShowFollowingRecentWritersService.java (1)
20-21: 조회용 트랜잭션(readOnly = true) 적용이 적절합니다 — 이전 리뷰 피드백 반영 완료.읽기 전용 쿼리 경로에 @transactional(readOnly = true) 적용으로 불필요한 flush 방지 및 성능에 유리합니다.
🧹 Nitpick comments (2)
src/main/java/konkuk/thip/user/application/service/UserShowFollowingRecentWritersService.java (2)
15-15: SIZE 상수의 의미 명확화 및 관리 전략 개선 제안.
- 네이밍: 도메인 의미가 드러나도록 LIMIT/…_LIMIT 형태가 가독성에 유리합니다.
- 관리 전략: 팀 컨벤션(서비스에서 LIMIT 전달)에는 부합하지만, 운영 중 조정 가능성을 고려하면 프로퍼티로 외부화하는 것도 유지보수에 유리합니다.
우선 네이밍 개선만 적용하려면 아래처럼 제안합니다.
- private static final int SIZE = 10; + private static final int RECENT_WRITERS_LIMIT = 10;- return userQueryMapper.toRecentWriterResponses(userQueryPort.findRecentFeedWritersOfMyFollowings(userId, SIZE)); + return userQueryMapper.toRecentWriterResponses(userQueryPort.findRecentFeedWritersOfMyFollowings(userId, RECENT_WRITERS_LIMIT));프로퍼티 외부화(선택 사항):
- 장점: 재배포 없이 튜닝 가능, 환경별 상이한 설정 용이.
- 구현 스케치:
// import 추가 import org.springframework.beans.factory.annotation.Value; // 필드 추가 @Value("${thip.user.following.recent-writers-limit:10}") private int recentWritersLimit; // 사용처 return userQueryMapper.toRecentWriterResponses( userQueryPort.findRecentFeedWritersOfMyFollowings(userId, recentWritersLimit) );Also applies to: 22-22
21-23: 사전조건 체크 추가 권장: userId null 방지.서비스 경계에서 명시적으로 방어하면 원인 파악이 쉬워집니다.
@Override @Transactional(readOnly = true) public UserFollowingRecentWritersResponse showMyFollowingRecentWriters(Long userId) { + java.util.Objects.requireNonNull(userId, "userId must not be null"); return userQueryMapper.toRecentWriterResponses(userQueryPort.findRecentFeedWritersOfMyFollowings(userId, SIZE)); }추가로 상위 Port 인터페이스의 파라미터에도 @NotNull을 선언하면 계약 수준에서 안전성이 강화됩니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepositoryImpl.java(2 hunks)src/main/java/konkuk/thip/user/application/service/UserShowFollowingRecentWritersService.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- 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.258Z
Learning: seongjunnoh는 데이터 무결성과 중복 방지에 대한 고민이 깊으며, LinkedHashSet을 활용한 중복 제거와 순서 보장을 동시에 달성하는 솔루션을 선호한다.
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/user/application/service/UserShowFollowingRecentWritersService.java (2)
11-13: 서비스 책임 분리가 명확합니다.UseCase 구현체로서 포트/매퍼만 주입받아 오케스트레이션에 집중한 구조가 좋습니다.
21-23: 포트 계약 확인 요청: null 대신 빈 컬렉션을 반환하도록 보장되어 있나요?매퍼가 null을 입력받지 않는다는 전제라면, UserQueryPort.findRecentFeedWritersOfMyFollowings는 빈 컬렉션을 반환하는 계약을 명시하는 것이 안전합니다. 해당 계약이 테스트로도 검증되어 있는지 확인 부탁드립니다. 또한 정렬 순서(최신 공개 피드 createdAt 기준)가 응답까지 그대로 유지되는지(매퍼에서 순서가 깨지지 않는지)도 함께 확인해 주세요.
#️⃣ 연관된 이슈
📝 작업 내용
이슈 참고해주시면 됩니다.
요구사항 정리
주요 코드 설명
📸 스크린샷
💬 리뷰 요구사항
@heeeeyong 님이 빠른 연동을 원하셔서 리뷰를 빠르게 해주시면 감사하겠습니다!! @hd0rable , @buzz0331
📌 PR 진행 시 이러한 점들을 참고해 주세요
Summary by CodeRabbit