Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,10 @@ public void unFollow(String unFollowNickname, Long followerId) {
follow.decreaseFollowerCount();
}

@Transactional(readOnly = true)
public FollowListResponse followList(Long userId, Long cursor, Integer size) {

if(!userRepository.existsById(userId)) {
if (!userRepository.existsById(userId)) {
throw new UserNotFoundException();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import team.wego.wegobackend.user.domain.User;
import team.wego.wegobackend.user.exception.SameNicknameException;
import team.wego.wegobackend.user.exception.UserNotFoundException;
import team.wego.wegobackend.user.repository.FollowRepository;
import team.wego.wegobackend.user.repository.UserRepository;

@Slf4j
Expand All @@ -24,14 +25,27 @@
public class UserService {

private final UserRepository userRepository;
private final FollowRepository followRepository;
private final ImageUploadService imageUploadService;

@Transactional(readOnly = true)
public UserInfoResponse getProfile(Long userId) {
public UserInfoResponse getProfile(Long loginUserId, Long targetUserId) {

User user = userRepository.findById(userId).orElseThrow(UserNotFoundException::new);
User targetUser = userRepository.findById(targetUserId)
.orElseThrow(UserNotFoundException::new);

return UserInfoResponse.from(user);
// 비로그인 or 본인 조회
if (loginUserId == null || loginUserId.equals(targetUserId)) {
return UserInfoResponse.from(targetUser);
}

User loginUser = userRepository.findById(loginUserId)
.orElseThrow(UserNotFoundException::new);

boolean isFollow = followRepository.existsByFollowerIdAndFolloweeId(loginUserId,
targetUserId);

return UserInfoResponse.from(targetUser, isFollow);
}

public UserInfoResponse updateProfileImage(Long userId, MultipartFile file) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public class UserInfoResponse {

private Boolean isNotificationEnabled;

private Boolean isFollow;

private LocalDateTime createdAt;

public static UserInfoResponse from(User user) {
Expand All @@ -55,4 +57,22 @@ public static UserInfoResponse from(User user) {
.createdAt(user.getCreatedAt())
.build();
}

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();
}
Comment on lines +61 to +77
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ 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.

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,17 @@ public class UserController implements UserControllerDocs {

/**
* 프로필 조회
* */
*/
@GetMapping("/{userId}")
public ResponseEntity<ApiResponse<UserInfoResponse>> profile(@PathVariable Long userId) {
public ResponseEntity<ApiResponse<UserInfoResponse>> profile(
@AuthenticationPrincipal CustomUserDetails userDetails,
@PathVariable Long userId
) {

UserInfoResponse response = userService.getProfile(userId);
UserInfoResponse response = userService.getProfile(
userDetails == null ? null : userDetails.getId(),
userId
);

return ResponseEntity
.status(HttpStatus.OK)
Expand All @@ -56,7 +62,7 @@ public ResponseEntity<ApiResponse<UserInfoResponse>> profile(@PathVariable Long

/**
* 프로필 이미지 변경
* */
*/
@PatchMapping(value = "/profile-image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<ApiResponse<UserInfoResponse>> profileImage(
@AuthenticationPrincipal CustomUserDetails userDetails,
Expand All @@ -72,7 +78,7 @@ public ResponseEntity<ApiResponse<UserInfoResponse>> profileImage(

/**
* 프로필 정보 변경
* */
*/
@PatchMapping("/profile")
public ResponseEntity<ApiResponse<UserInfoResponse>> profileInfo(
@AuthenticationPrincipal CustomUserDetails userDetails,
Expand All @@ -88,7 +94,7 @@ public ResponseEntity<ApiResponse<UserInfoResponse>> profileInfo(

/**
* 알림 설정 변경
* */
*/
@PatchMapping("/notification")
public ResponseEntity<ApiResponse<UserInfoResponse>> changeNotificationConfig(
@AuthenticationPrincipal CustomUserDetails userDetails,
Expand All @@ -105,7 +111,7 @@ public ResponseEntity<ApiResponse<UserInfoResponse>> changeNotificationConfig(

/**
* 팔로우 요청
* */
*/
@PostMapping("/follow")
public ResponseEntity<ApiResponse<String>> follow(
@AuthenticationPrincipal CustomUserDetails userDetails,
Expand All @@ -121,7 +127,7 @@ public ResponseEntity<ApiResponse<String>> follow(

/**
* 팔로우 취소
* */
*/
@DeleteMapping("/unfollow")
public ResponseEntity<ApiResponse<String>> unFollow(
@AuthenticationPrincipal CustomUserDetails userDetails,
Expand All @@ -137,7 +143,7 @@ public ResponseEntity<ApiResponse<String>> unFollow(

/**
* 팔로우 리스트 조회
* */
*/
@GetMapping("/{userId}/follow")
public ResponseEntity<ApiResponse<FollowListResponse>> followList(
@PathVariable Long userId, //다른 유저 조회를 위한 파라메터
Expand All @@ -154,7 +160,7 @@ public ResponseEntity<ApiResponse<FollowListResponse>> followList(

/**
* 이메일 중복검사
* */
*/
@GetMapping("/email/availability")
public ResponseEntity<ApiResponse<AvailabilityResponse>> checkEmailAvailability(
@RequestParam("email") String email
Expand All @@ -169,7 +175,7 @@ public ResponseEntity<ApiResponse<AvailabilityResponse>> checkEmailAvailability(

/**
* 닉네임 중복검사
* */
*/
@GetMapping("/nickname/availability")
public ResponseEntity<ApiResponse<AvailabilityResponse>> checkNicknameAvailability(
@RequestParam("nickname") String nickname
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@
@Tag(name = "유저 API", description = "유저와 관련된 API 리스트 \uD83D\uDC08")
public interface UserControllerDocs {

@Operation(summary = "유저 프로필 조회 API", description = "PathVariable로 들어온 userId에 해당하는 유저 프로필에 대한 응답")
ResponseEntity<ApiResponse<UserInfoResponse>> profile(@PathVariable Long userId);
@Operation(summary = "유저 프로필 조회 API", description =
"PathVariable로 들어온 userId에 해당하는 유저 프로필에 대한 응답 \n"
+ "로그인 여부/본인 여부에 따라 팔로우 여부를 null 혹은 true/false로 응답합니다.")
ResponseEntity<ApiResponse<UserInfoResponse>> profile(
@AuthenticationPrincipal CustomUserDetails userDetails,
@PathVariable Long userId
);

@Operation(summary = "프로필 이미지 변경 API", description = "토큰 주인장 이미지만 변경 가능합니다.")
ResponseEntity<ApiResponse<UserInfoResponse>> profileImage(
Expand Down
8 changes: 6 additions & 2 deletions src/test/http/user/user-api.http
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@ Content-Type: application/json
client.global.set("accessToken", response.body.data.accessToken);
%}

### 유저 정보 조회
### 유저 정보 조회 (로그인)
GET http://localhost:8080/api/v1/users/1
Authorization: Bearer {{accessToken}}

### 유저 정보 조회 (비로그인)
GET http://localhost:8080/api/v1/users/1


### 유저 프로필 이미지 변경
PATCH http://localhost:8080/api/v1/users/profile-image
Authorization: Bearer {{accessToken}}
Expand Down Expand Up @@ -64,7 +68,7 @@ Content-Type: application/json
}

### 팔로우 요청
POST http://localhost:8080/api/v1/users/follow?followNickname=팔로우 대상
POST http://localhost:8080/api/v1/users/follow?followNickname=user0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

테스트 데이터 일관성을 확인하세요.

라인 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.

Authorization: Bearer {{accessToken}}

### 팔로우 취소
Expand Down