Conversation
- http 테스트 작성
Walkthrough팔로워 목록 조회 기능을 추가합니다. 새로운 API 엔드포인트는 지정된 사용자의 팔로워를 페이지네이션과 함께 반환하며, 각 팔로워에 대한 상호 팔로우 상태 정보를 포함합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as 클라이언트
participant Controller as UserController
participant Service as FollowService
participant Repo as FollowRepository/<br/>Custom
participant DB as 데이터베이스
Client->>Controller: GET /api/v1/users/{userId}/follower<br/>(cursor, size)
Controller->>Service: followerList(userId, cursor, size)
Service->>Service: 사용자 존재 검증
Service->>Repo: findFollowerList(userId, cursor, size)
Repo->>DB: SELECT 팔로워 정보 with 페이지네이션
DB-->>Repo: List<FollowResponse>
Repo-->>Service: 팔로워 목록
Service->>Repo: findFolloweeIdsByFollowerId(userId)
Repo->>DB: SELECT 팔로우 ID 집합
DB-->>Repo: Set<Long>
Repo-->>Service: 팔로우 ID 집합
Service->>Service: 각 팔로워의<br/>상호 팔로우 여부 계산
Service->>Service: WrapperFollowerResponse 구성<br/>(isFollow 플래그 포함)
Service->>Service: 다음 커서 계산
Service-->>Controller: FollowerListResponse
Controller-->>Client: ApiResponse<FollowerListResponse><br/>(HTTP 200)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
src/main/java/team/wego/wegobackend/user/repository/FollowRepositoryImpl.java (1)
21-47: 기존 팔로잉 목록 조회 로직 확인 완료.구현이 올바르며, 새로 추가된
findFollowerList메서드와 구조가 거의 동일합니다.💡 선택적 리팩토링: 중복 코드 제거
두 메서드(
findFollowingList와findFollowerList)가 거의 동일한 구조를 가지고 있습니다. 향후 유지보수성을 위해 공통 로직을 추출하는 것을 고려해보세요.private List<FollowResponse> findFollowList( Long userId, Long cursorFollowId, int size, boolean isFollowing // true면 팔로잉, false면 팔로워 ) { QFollow follow = QFollow.follow; QUser user = QUser.user; return jpaQueryFactory .select(new QFollowResponse( follow.id, user.id, user.profileImage, user.nickName, user.profileMessage )) .from(follow) .join(isFollowing ? follow.followee : follow.follower, user) .where( isFollowing ? follow.follower.id.eq(userId) : follow.followee.id.eq(userId), itCursor(cursorFollowId) ) .orderBy(follow.id.desc()) .limit(size) .fetch(); }src/main/java/team/wego/wegobackend/user/application/dto/response/WrapperFollowerResponse.java (1)
5-24: Java Record 사용을 고려해보세요.현재 구현은 정상적으로 동작하지만, Java 16+의 record 문법을 사용하면 더 간결하게 작성할 수 있습니다. Record는 불변 데이터 클래스에 적합하며 보일러플레이트 코드를 줄여줍니다.
🔎 Record로 리팩토링하는 제안
-@Getter -public class WrapperFollowerResponse { - - private final Long followId; //cursor - private final Long userId; - private final String profileImage; - private final String nickname; - private final String profileMessage; - private final boolean isFollow; - - public WrapperFollowerResponse(FollowResponse followResponse, boolean isFollow) { - this.followId = followResponse.getFollowId(); - this.userId = followResponse.getUserId(); - this.profileImage = followResponse.getProfileImage(); - this.nickname = followResponse.getNickname(); - this.profileMessage = followResponse.getProfileMessage(); - this.isFollow = isFollow; - } - -} +public record WrapperFollowerResponse( + Long followId, //cursor + Long userId, + String profileImage, + String nickname, + String profileMessage, + boolean isFollow +) { + public WrapperFollowerResponse(FollowResponse followResponse, boolean isFollow) { + this( + followResponse.getFollowId(), + followResponse.getUserId(), + followResponse.getProfileImage(), + followResponse.getNickname(), + followResponse.getProfileMessage(), + isFollow + ); + } +}
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
src/main/java/team/wego/wegobackend/user/application/FollowService.javasrc/main/java/team/wego/wegobackend/user/application/dto/response/FollowerListResponse.javasrc/main/java/team/wego/wegobackend/user/application/dto/response/WrapperFollowerResponse.javasrc/main/java/team/wego/wegobackend/user/presentation/UserController.javasrc/main/java/team/wego/wegobackend/user/repository/FollowRepository.javasrc/main/java/team/wego/wegobackend/user/repository/FollowRepositoryCustom.javasrc/main/java/team/wego/wegobackend/user/repository/FollowRepositoryImpl.javasrc/test/http/user/user-api.http
🔇 Additional comments (7)
src/test/http/user/user-api.http (1)
71-71: 테스트 데이터 및 커서 전파 로직 확인 완료.팔로우 목록의 커서 전파 로직이 올바르게 추가되었습니다.
Also applies to: 94-96
src/main/java/team/wego/wegobackend/user/application/dto/response/FollowerListResponse.java (1)
1-7: 팔로워 목록 응답 DTO 구조 확인 완료.
items와nextCursor를 포함한 record 구조가 적절하며, 커서 기반 페이지네이션 패턴을 올바르게 따르고 있습니다.src/main/java/team/wego/wegobackend/user/presentation/UserController.java (1)
32-32: 팔로워 목록 조회 엔드포인트 구현 확인 완료.새로운 REST 엔드포인트가 올바르게 구현되었습니다:
- 기존
followList엔드포인트와 일관된 패턴을 따릅니다.- 페이지네이션 파라미터 검증이 적절합니다 (
@Min(1) @Max(100)).- 서비스 계층으로의 위임이 명확합니다.
Also applies to: 178-193
src/main/java/team/wego/wegobackend/user/application/FollowService.java (3)
104-106: 사용자 존재 검증 로직이 올바릅니다.팔로워 목록 조회 전에 사용자 존재 여부를 확인하는 것은 적절한 검증입니다.
108-111: 맞팔로우 확인 로직이 효율적입니다.팔로워 목록과 팔로이 ID 집합을 분리하여 조회한 후
contains메서드로 확인하는 방식은 O(1) 조회 성능을 제공합니다.
122-124: 커서 기반 페이지네이션 구현이 적절합니다.기존
followList메서드와 동일한 패턴으로 커서를 계산하여 일관성 있는 API 동작을 제공합니다.src/main/java/team/wego/wegobackend/user/repository/FollowRepository.java (1)
19-20: JPQL 쿼리의 엔티티 관계가 올바르게 정의되어 있습니다.Follow 엔티티의 관계 정보를 확인했으며,
follower와followee필드가@ManyToOne관계로 올바르게 정의되어 있습니다. JPQL 쿼리SELECT f.followee.id FROM Follow f WHERE f.follower.id = :followerId는 엔티티 구조와 정확하게 일치하므로 추가 수정이 필요하지 않습니다.
| for(FollowResponse follower : list) { | ||
| boolean isFollow = followingUserIds.contains(follower.getUserId()); | ||
| follower.getUserId(), follower.getNickname(), isFollow); | ||
| WrapperFollowerResponse response = new WrapperFollowerResponse(follower, isFollow); | ||
| result.add(response); | ||
| } |
There was a problem hiding this comment.
불필요한 코드 라인을 제거하세요.
Line 117에 의미 없는 코드가 남아있습니다. 이 라인은 어떤 동작도 수행하지 않으며, 디버깅 과정에서 완전히 삭제되지 않은 것으로 보입니다.
🔎 수정 제안
for(FollowResponse follower : list) {
boolean isFollow = followingUserIds.contains(follower.getUserId());
- follower.getUserId(), follower.getNickname(), isFollow);
WrapperFollowerResponse response = new WrapperFollowerResponse(follower, isFollow);
result.add(response);
}📝 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.
| for(FollowResponse follower : list) { | |
| boolean isFollow = followingUserIds.contains(follower.getUserId()); | |
| follower.getUserId(), follower.getNickname(), isFollow); | |
| WrapperFollowerResponse response = new WrapperFollowerResponse(follower, isFollow); | |
| result.add(response); | |
| } | |
| for(FollowResponse follower : list) { | |
| boolean isFollow = followingUserIds.contains(follower.getUserId()); | |
| WrapperFollowerResponse response = new WrapperFollowerResponse(follower, isFollow); | |
| result.add(response); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/team/wego/wegobackend/user/application/FollowService.java
around lines 115 to 120, there is a leftover meaningless statement on line 117
(an expression like follower.getUserId(), follower.getNickname(), isFollow);
that does nothing and should be removed; delete that stray line and ensure the
loop only constructs WrapperFollowerResponse and adds it to result, then
reformat/compile to confirm no unused imports or warnings remain.
| List<FollowResponse> findFollowerList( | ||
| Long followerId, | ||
| Long cursorFollowId, | ||
| int size | ||
| ); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
매개변수 네이밍 개선 필요.
인터페이스 메서드의 매개변수명이 의미를 정확히 전달하지 못합니다:
- Line 17:
followerId는 실제로 팔로워를 조회할 대상 사용자(followee)의 ID입니다. - 구현체(
FollowRepositoryImpl)에서도follow.followee.id와 비교하고 있습니다. - 더 명확한 이름은
followeeId입니다.
이는 FollowRepositoryImpl의 같은 메서드와 함께 수정되어야 합니다.
🔎 제안하는 수정
List<FollowResponse> findFollowerList(
- Long followerId,
+ Long followeeId,
Long cursorFollowId,
int size
);📝 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.
| List<FollowResponse> findFollowerList( | |
| Long followerId, | |
| Long cursorFollowId, | |
| int size | |
| ); | |
| List<FollowResponse> findFollowerList( | |
| Long followeeId, | |
| Long cursorFollowId, | |
| int size | |
| ); |
🤖 Prompt for AI Agents
In
src/main/java/team/wego/wegobackend/user/repository/FollowRepositoryCustom.java
around lines 16 to 20, the parameter name followerId is misleading because it
actually represents the followee (the user being followed); rename the parameter
to followeeId in the interface signature and update any Javadoc/comments
accordingly, and then update the corresponding method signature and usages in
FollowRepositoryImpl to use followeeId so names are consistent between interface
and implementation.
| @Override | ||
| public List<FollowResponse> findFollowerList( | ||
| Long followId, | ||
| Long cursorFollowId, | ||
| int size | ||
| ) { | ||
|
|
||
| QFollow follow = QFollow.follow; | ||
| QUser user = QUser.user; | ||
|
|
||
| return jpaQueryFactory | ||
| .select(new QFollowResponse( | ||
| follow.id, | ||
| user.id, | ||
| user.profileImage, | ||
| user.nickName, | ||
| user.profileMessage | ||
| )) | ||
| .from(follow) | ||
| .join(follow.follower, user) | ||
| .where( | ||
| follow.followee.id.eq(followId), | ||
| itCursor(cursorFollowId) | ||
| ) | ||
| .orderBy(follow.id.desc()) //최신 팔로우 순 | ||
| .limit(size) | ||
| .fetch(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
매개변수 네이밍 개선 필요.
메서드 로직은 올바르지만, 매개변수명이 의미를 명확히 전달하지 못합니다:
- Line 51:
followId는 실제로 팔로워 목록을 조회할 대상 사용자(followee)의 ID를 나타냅니다. - Line 70:
follow.followee.id.eq(followId)를 보면 이것이 followee ID임을 알 수 있습니다. - 더 명확한 이름은
followeeId입니다.
이 변경은 FollowRepositoryCustom 인터페이스에도 적용되어야 합니다.
🔎 제안하는 수정
@Override
public List<FollowResponse> findFollowerList(
- Long followId,
+ Long followeeId,
Long cursorFollowId,
int size
) {
QFollow follow = QFollow.follow;
QUser user = QUser.user;
return jpaQueryFactory
.select(new QFollowResponse(
follow.id,
user.id,
user.profileImage,
user.nickName,
user.profileMessage
))
.from(follow)
.join(follow.follower, user)
.where(
- follow.followee.id.eq(followId),
+ follow.followee.id.eq(followeeId),
itCursor(cursorFollowId)
)
.orderBy(follow.id.desc())
.limit(size)
.fetch();
}📝 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.
| @Override | |
| public List<FollowResponse> findFollowerList( | |
| Long followId, | |
| Long cursorFollowId, | |
| int size | |
| ) { | |
| QFollow follow = QFollow.follow; | |
| QUser user = QUser.user; | |
| return jpaQueryFactory | |
| .select(new QFollowResponse( | |
| follow.id, | |
| user.id, | |
| user.profileImage, | |
| user.nickName, | |
| user.profileMessage | |
| )) | |
| .from(follow) | |
| .join(follow.follower, user) | |
| .where( | |
| follow.followee.id.eq(followId), | |
| itCursor(cursorFollowId) | |
| ) | |
| .orderBy(follow.id.desc()) //최신 팔로우 순 | |
| .limit(size) | |
| .fetch(); | |
| } | |
| @Override | |
| public List<FollowResponse> findFollowerList( | |
| Long followeeId, | |
| Long cursorFollowId, | |
| int size | |
| ) { | |
| QFollow follow = QFollow.follow; | |
| QUser user = QUser.user; | |
| return jpaQueryFactory | |
| .select(new QFollowResponse( | |
| follow.id, | |
| user.id, | |
| user.profileImage, | |
| user.nickName, | |
| user.profileMessage | |
| )) | |
| .from(follow) | |
| .join(follow.follower, user) | |
| .where( | |
| follow.followee.id.eq(followeeId), | |
| itCursor(cursorFollowId) | |
| ) | |
| .orderBy(follow.id.desc()) //최신 팔로우 순 | |
| .limit(size) | |
| .fetch(); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/team/wego/wegobackend/user/repository/FollowRepositoryImpl.java
around lines 49 to 76, rename the method parameter followId to followeeId (and
update its usages inside the method, e.g., follow.followee.id.eq(followeeId)) to
accurately reflect that it is the target user's ID; also update the
corresponding declaration in FollowRepositoryCustom (and any
implementations/usages) to use followeeId to keep signatures consistent and
avoid compile errors.
| ### 팔로워 리스트 조회 (초기값) | ||
| GET http://localhost:8080/api/v1/users/102/follower?size=10 | ||
|
|
||
| > {% | ||
| client.global.set("nextCursor", response.body.data.nextCursor); | ||
| %} | ||
|
|
||
| ### 팔로워 리스트 조회 (cursor) | ||
| GET http://localhost:8080/api/v1/users/1/follower?cursor={{nextCursor}}&size=10 | ||
|
|
||
| > {% | ||
| client.global.set("nextCursor", response.body.data.nextCursor); | ||
| %} No newline at end of file |
There was a problem hiding this comment.
테스트 케이스의 userId 불일치.
페이지네이션 테스트에서 서로 다른 userId를 사용하고 있습니다:
- Line 99:
userId=102(초기 조회) - Line 106:
userId=1(커서 기반 조회)
페이지네이션이 올바르게 동작하려면 동일한 사용자의 팔로워 목록을 조회해야 합니다.
🔎 제안하는 수정
### 팔로워 리스트 조회 (cursor)
-GET http://localhost:8080/api/v1/users/1/follower?cursor={{nextCursor}}&size=10
+GET http://localhost:8080/api/v1/users/102/follower?cursor={{nextCursor}}&size=10🤖 Prompt for AI Agents
In src/test/http/user/user-api.http around lines 98 to 110, the pagination test
uses different userIds between the initial follower request (userId=102) and the
cursor-based request (userId=1); update the second GET to use the same userId as
the first (change userId=1 to userId=102) so both requests target the same
user's follower list and the saved nextCursor applies correctly.
📝 Pull Request
📌 PR 종류
해당하는 항목에 체크해주세요.
✨ 변경 내용
이번 PR에서 어떤 변경이 이루어졌는지 명확하게 작성해주세요.
팔로워 목록 조회 API가 추가되었습니다.
기존 팔로잉 목록 조회 응답에서 맞팔로우 여부를 나타내는 isFollow 필드 및 관련 로직이 추가되었습니다.
🔍 관련 이슈
해당 PR이 해결하는 이슈가 있다면 연결해주세요.
🧪 테스트
변경된 기능에 대한 테스트 범위 또는 테스트 결과를 작성해주세요.
🚨 확인해야 할 사항 (Checklist)
PR을 제출하기 전에 아래 항목들을 확인해주세요.
🙋 기타 참고 사항
리뷰어가 참고하면 좋을 만한 추가 설명이 있다면 적어주세요.
Summary by CodeRabbit
릴리스 노트
✏️ Tip: You can customize this high-level summary in your review settings.