Conversation
- 로그인/비로그인에 따른 응답 분기 처리 - 팔로우 여부 필드 추가 및 로직 적용 - 팔로우 리스트 조회 transactional(readOnly) 적용
Walkthrough프로필 조회 API가 로그인 상태를 구분하고 팔로우 여부를 응답에 포함하도록 리팩토링됩니다. UserService와 UserController의 메서드 시그니처가 인증된 사용자 정보를 매개변수로 받도록 변경되고, 응답 객체에 isFollow 필드가 추가되며, FollowService의 읽기 작업이 최적화됩니다. Changes
Sequence DiagramsequenceDiagram
actor Client
participant Controller as UserController
participant Service as UserService
participant UserRepo as UserRepository
participant FollowRepo as FollowRepository
Client->>Controller: GET /api/v1/users/{userId}<br/>(with AuthToken)
activate Controller
Controller->>Service: getProfile(loginUserId, targetUserId)
activate Service
alt loginUserId is null or equals targetUserId
Service->>UserRepo: findById(targetUserId)
activate UserRepo
UserRepo-->>Service: User
deactivate UserRepo
Service->>Service: build response<br/>(isFollow = null)
else loginUserId differs from targetUserId
Service->>UserRepo: findById(loginUserId)
activate UserRepo
UserRepo-->>Service: loginUser
deactivate UserRepo
Service->>FollowRepo: existsByFollowerIdAndFolloweeId<br/>(loginUserId, targetUserId)
activate FollowRepo
FollowRepo-->>Service: Boolean
deactivate FollowRepo
Service->>UserRepo: findById(targetUserId)
activate UserRepo
UserRepo-->>Service: targetUser
deactivate UserRepo
Service->>Service: build response<br/>(isFollow = true/false)
end
Service-->>Controller: UserInfoResponse
deactivate Service
Controller-->>Client: ApiResponse<UserInfoResponse>
deactivate Controller
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 특별히 주의가 필요한 부분:
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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: 2
🧹 Nitpick comments (2)
src/main/java/team/wego/wegobackend/user/application/FollowService.java (1)
78-80:existsById선체크는 경쟁 상태(TOCTOU)에서 일관성이 흔들릴 수 있어 선택 재검토
현재는existsById(Line 78)로 404를 먼저 내고, 이후 목록을 조회합니다. 동시에 유저가 삭제되는 경우 등 경쟁 상태에서는 “존재 확인은 통과했는데 조회는 빈 리스트” 같은 결과가 나올 수 있어요. 강한 일관성이 필요하면findById(...).orElseThrow(...)로 묶거나, 반대로 선체크를 제거하고 “없으면 빈 리스트/혹은 repo에서 조인으로 검증” 같은 단일 쿼리 전략도 고려해볼 만합니다.src/main/java/team/wego/wegobackend/user/application/UserService.java (1)
32-49: 로그인 상태에 따른 분기 처리가 올바르게 구현되었습니다.비로그인, 본인 조회, 타인 조회의 세 가지 케이스를 모두 적절하게 처리하고 있습니다.
선택적 최적화 제안:
라인 42-43에서
loginUser엔티티 전체를 조회하고 있지만, 실제로는 팔로우 여부 확인에만 ID가 사용됩니다. 엔티티 조회를 제거하여 약간의 성능 개선이 가능합니다:- User loginUser = userRepository.findById(loginUserId) - .orElseThrow(UserNotFoundException::new); - boolean isFollow = followRepository.existsByFollowerIdAndFolloweeId(loginUserId, targetUserId);다만
loginUserId가 유효하지 않은 경우에 대한 검증이 필요하다면, 현재 구현을 유지하는 것도 합리적입니다. 인증된 사용자 ID는 보통 유효하다고 가정할 수 있으므로 엔티티 조회 없이 진행해도 무방합니다.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/main/java/team/wego/wegobackend/user/application/FollowService.java(1 hunks)src/main/java/team/wego/wegobackend/user/application/UserService.java(2 hunks)src/main/java/team/wego/wegobackend/user/application/dto/response/UserInfoResponse.java(2 hunks)src/main/java/team/wego/wegobackend/user/presentation/UserController.java(9 hunks)src/main/java/team/wego/wegobackend/user/presentation/UserControllerDocs.java(1 hunks)src/test/http/user/user-api.http(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/team/wego/wegobackend/user/application/UserService.java (1)
src/main/java/team/wego/wegobackend/user/exception/UserNotFoundException.java (1)
UserNotFoundException(6-11)
🔇 Additional comments (8)
src/main/java/team/wego/wegobackend/user/application/FollowService.java (1)
75-87:@Transactional(readOnly = true)적용이 적절합니다.
findFollowingList()는 QueryDSL을 사용한 순수 SELECT 쿼리(수정/삭제 작업 없음)이므로, readOnly 트랜잭션 적용은 완전히 안전하며 조회 성능 최적화에 효과적입니다. 컨트롤러에서 size 파라미터도@Min(1) @Max(100)으로 검증되고 있습니다.src/main/java/team/wego/wegobackend/user/application/dto/response/UserInfoResponse.java (1)
40-40: LGTM!팔로우 여부를 나타내는
isFollow필드가 올바르게 추가되었습니다.Boolean래퍼 타입을 사용하여 비로그인 상태에서null처리가 가능합니다.src/test/http/user/user-api.http (1)
24-30: LGTM!로그인/비로그인 시나리오를 모두 테스트할 수 있도록 테스트 케이스가 올바르게 분리되었습니다. PR 목표인 로그인 여부에 따른 분기 처리를 검증하기에 적절합니다.
src/main/java/team/wego/wegobackend/user/presentation/UserControllerDocs.java (1)
26-32: LGTM!API 문서가 새로운 인증 기반 동작을 명확하게 설명하고 있습니다. 로그인 여부 및 본인 여부에 따른
isFollow필드의 응답값(null/true/false)이 잘 명시되어 있습니다.src/main/java/team/wego/wegobackend/user/application/UserService.java (1)
18-18: LGTM!팔로우 상태 확인을 위해
FollowRepository의존성이 올바르게 추가되었습니다.Also applies to: 28-28
src/main/java/team/wego/wegobackend/user/presentation/UserController.java (3)
47-55: LGTM!인증된 사용자 정보를 서비스 계층에 올바르게 전달하고 있습니다. 비로그인 요청에 대한
null처리가 적절하며, 서비스 계층의 분기 로직과 일치합니다.
43-45: 코드 품질 개선이 잘 이루어졌습니다.Javadoc 주석 형식이 일관되게 수정되어 코드 가독성이 향상되었습니다.
Also applies to: 63-65, 79-81, 95-97, 112-114, 128-130, 144-146, 161-163, 176-178
67-69: LGTM!모든 인증이 필요한 엔드포인트에서
@AuthenticationPrincipal을 통해 사용자 정보를 일관되게 받고 있습니다.Also applies to: 83-85, 99-101, 116-118, 132-134
| public static UserInfoResponse from(User user, Boolean isFollow) { | ||
| return UserInfoResponse.builder() | ||
| .userId(user.getId()) | ||
| .email(user.getEmail()) | ||
| .nickName(user.getNickName()) | ||
| .mbti(user.getMbti()) | ||
| .profileImage(user.getProfileImage()) | ||
| .profileMessage(user.getProfileMessage()) | ||
| .followeesCnt(user.getFolloweesCnt()) | ||
| .followersCnt(user.getFollowersCnt()) | ||
| .groupJoinedCnt(user.getGroupJoinedCnt()) | ||
| .groupCreatedCnt(user.getGroupCreatedCnt()) | ||
| .isNotificationEnabled(user.getNotificationEnabled()) | ||
| .isFollow(isFollow) | ||
| .createdAt(user.getCreatedAt()) | ||
| .build(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
중복 코드를 제거하여 유지보수성을 개선하세요.
새로운 팩토리 메서드가 기존 from(User) 메서드와 거의 동일한 코드를 중복하고 있습니다. 이는 DRY 원칙을 위반하며, 향후 필드 추가 시 두 메서드를 모두 수정해야 하는 유지보수 부담을 발생시킵니다.
다음과 같이 기존 메서드를 재사용하도록 리팩토링하세요:
public static UserInfoResponse from(User user, Boolean isFollow) {
- return UserInfoResponse.builder()
- .userId(user.getId())
- .email(user.getEmail())
- .nickName(user.getNickName())
- .mbti(user.getMbti())
- .profileImage(user.getProfileImage())
- .profileMessage(user.getProfileMessage())
- .followeesCnt(user.getFolloweesCnt())
- .followersCnt(user.getFollowersCnt())
- .groupJoinedCnt(user.getGroupJoinedCnt())
- .groupCreatedCnt(user.getGroupCreatedCnt())
- .isNotificationEnabled(user.getNotificationEnabled())
- .isFollow(isFollow)
- .createdAt(user.getCreatedAt())
- .build();
+ UserInfoResponse response = from(user);
+ return response.toBuilder()
+ .isFollow(isFollow)
+ .build();
}참고: 위 해결책은 Lombok의 @Builder가 자동으로 생성하는 toBuilder() 메서드를 활용합니다. 만약 toBuilder 옵션이 활성화되어 있지 않다면, 클래스 선언에 @Builder(toBuilder = true)를 추가해야 합니다.
🤖 Prompt for AI Agents
In
src/main/java/team/wego/wegobackend/user/application/dto/response/UserInfoResponse.java
around lines 61 to 77, the new from(User, Boolean) method duplicates the mapping
logic from the existing from(User) method; refactor it to reuse the
single-source-of-truth by invoking the existing from(User) and then set the
isFollow field (using the Lombok-generated toBuilder() to modify only isFollow),
and if toBuilder is not enabled add @Builder(toBuilder = true) to the class
declaration so you can call from(user).toBuilder().isFollow(isFollow).build()
instead of duplicating all fields.
|
|
||
| ### 팔로우 요청 | ||
| POST http://localhost:8080/api/v1/users/follow?followNickname=팔로우 대상 | ||
| POST http://localhost:8080/api/v1/users/follow?followNickname=user0 |
There was a problem hiding this comment.
테스트 데이터 일관성을 확인하세요.
라인 67에서 생성한 테스트 유저의 닉네임은 "팔로우 대상"인데, 라인 71의 팔로우 요청은 "user0"을 대상으로 하고 있습니다. "user0" 유저가 사전에 존재하지 않는다면 이 테스트는 실패할 수 있습니다.
테스트 데이터를 일관되게 수정하세요:
-POST http://localhost:8080/api/v1/users/follow?followNickname=user0
+POST http://localhost:8080/api/v1/users/follow?followNickname=팔로우 대상또는 "user0" 유저가 별도로 생성되는 것이 의도된 것이라면, 해당 유저를 생성하는 테스트 케이스를 추가하거나 주석으로 명시해주세요.
🤖 Prompt for AI Agents
In src/test/http/user/user-api.http around lines 67–71, the follow test at line
71 targets "user0" while the test user created at line 67 has nickname "팔로우 대상",
causing inconsistent test data; update the follow request to use the same
nickname ("팔로우 대상") or add a preceding request that creates "user0" (or add a
comment clarifying the intent). Ensure the target nickname in the POST
/api/v1/users/follow query matches an existing created user in this test file.
📝 Pull Request
📌 PR 종류
해당하는 항목에 체크해주세요.
✨ 변경 내용
기존 회원 프로필 조회 API에 대한 리팩토링을 수행했습니다.
변경 적용점은 아래와 같습니다.
팔로우 여부를 나타내는 필드 추가
isFollow로그인 여부를 위한
@AuthenticationPrincipal매개변수 추가로그인 여부/본인 여부에 따라 팔로우 여부 조회 로직 적용
UserInfoResponseDTO 정적 팩토리 메서드 추가🔍 관련 이슈
해당 PR이 해결하는 이슈가 있다면 연결해주세요.
Closes #104
🧪 테스트
변경된 기능에 대한 테스트 범위 또는 테스트 결과를 작성해주세요.
🚨 확인해야 할 사항 (Checklist)
PR을 제출하기 전에 아래 항목들을 확인해주세요.
🙋 기타 참고 사항
리뷰어가 참고하면 좋을 만한 추가 설명이 있다면 적어주세요.
Summary by CodeRabbit
릴리스 노트
새로운 기능
개선사항
✏️ Tip: You can customize this high-level summary in your review settings.