Skip to content

[REFACTOR] 유저 프로필 조회 API 리팩토링#105

Merged
Be-HinD merged 1 commit intomainfrom
refactor/profile
Dec 13, 2025
Merged

[REFACTOR] 유저 프로필 조회 API 리팩토링#105
Be-HinD merged 1 commit intomainfrom
refactor/profile

Conversation

@Be-HinD
Copy link
Member

@Be-HinD Be-HinD commented Dec 13, 2025

📝 Pull Request

📌 PR 종류

해당하는 항목에 체크해주세요.

  • 기능 추가 (Feature)
  • 버그 수정 (Fix)
  • 문서 수정 (Docs)
  • 코드 리팩터링 (Refactor)
  • 테스트 추가 (Test)
  • 기타 변경 (Chore)

✨ 변경 내용

기존 회원 프로필 조회 API에 대한 리팩토링을 수행했습니다.
변경 적용점은 아래와 같습니다.

  • 팔로우 여부를 나타내는 필드 추가 isFollow

  • 로그인 여부를 위한 @AuthenticationPrincipal 매개변수 추가

  • 로그인 여부/본인 여부에 따라 팔로우 여부 조회 로직 적용

  • UserInfoResponse DTO 정적 팩토리 메서드 추가


🔍 관련 이슈

해당 PR이 해결하는 이슈가 있다면 연결해주세요.
Closes #104


🧪 테스트

변경된 기능에 대한 테스트 범위 또는 테스트 결과를 작성해주세요.

  • 유닛 테스트 추가 / 수정
  • 통합 테스트 검증
  • 수동 테스트 완료
image

🚨 확인해야 할 사항 (Checklist)

PR을 제출하기 전에 아래 항목들을 확인해주세요.

  • 코드 포매팅 완료
  • 불필요한 파일/코드 제거
  • 로직 검증 완료
  • 프로젝트 빌드 성공
  • 린트/정적 분석 통과 (해당 시)

🙋 기타 참고 사항

리뷰어가 참고하면 좋을 만한 추가 설명이 있다면 적어주세요.

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 사용자 프로필 조회 시 팔로우 상태 정보 표시
  • 개선사항

    • 인증된 사용자 정보 기반 프로필 관련 API 개선
    • 데이터베이스 조회 성능 최적화

✏️ Tip: You can customize this high-level summary in your review settings.

- 로그인/비로그인에 따른 응답 분기 처리
- 팔로우 여부 필드 추가 및 로직 적용
- 팔로우 리스트 조회 transactional(readOnly) 적용
@Be-HinD Be-HinD self-assigned this Dec 13, 2025
@Be-HinD Be-HinD added the ✨enhancement New feature or request label Dec 13, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 13, 2025

Walkthrough

프로필 조회 API가 로그인 상태를 구분하고 팔로우 여부를 응답에 포함하도록 리팩토링됩니다. UserService와 UserController의 메서드 시그니처가 인증된 사용자 정보를 매개변수로 받도록 변경되고, 응답 객체에 isFollow 필드가 추가되며, FollowService의 읽기 작업이 최적화됩니다.

Changes

Cohort / File(s) Summary
팔로우 서비스 최적화
src/main/java/team/wego/wegobackend/user/application/FollowService.java
followList() 메서드에 @Transactional(readOnly = true) 추가
사용자 프로필 조회 로직 개선
src/main/java/team/wego/wegobackend/user/application/UserService.java
메서드 시그니처 변경 (getProfile(Long userId) → getProfile(Long loginUserId, Long targetUserId)), FollowRepository 의존성 추가, 로그인 여부에 따른 조건부 팔로우 상태 확인 로직
응답 DTO 확장
src/main/java/team/wego/wegobackend/user/application/dto/response/UserInfoResponse.java
isFollow 필드 추가, from(User user, Boolean isFollow) 메서드 오버로드
컨트롤러 인증 처리
src/main/java/team/wego/wegobackend/user/presentation/UserController.java
profile, profileImage, profileInfo, changeNotificationConfig, follow, unFollow 메서드에 @AuthenticationPrincipal CustomUserDetails userDetails 파라미터 추가, Javadoc 문법 수정
API 문서 업데이트
src/main/java/team/wego/wegobackend/user/presentation/UserControllerDocs.java
profile() 메서드 시그니처에 @AuthenticationPrincipal CustomUserDetails userDetails 추가
테스트 HTTP 요청
src/test/http/user/user-api.http
로그인 상태와 비로그인 상태 프로필 조회 분리, 팔로우 요청 파라미터 실제 값(user0)으로 업데이트

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

특별히 주의가 필요한 부분:

  • UserService의 조건부 로직: loginUserId null 체크 및 동일성 비교에 따른 분기 처리 흐름 검증
  • FollowRepository 호출: existsByFollowerIdAndFolloweeId() 메서드 사용 시 정확한 ID 순서와 의도 확인
  • 메서드 시그니처 변경: UserService.getProfile의 매개변수 변경이 모든 호출 부분에 올바르게 반영되었는지 확인
  • UserController의 다중 메서드 변경: 인증 정보 추가에 따른 6개 메서드의 일관성 검증

Possibly related PRs

Poem

🐰 프로필 조회, 로그인 상태 가려
팔로우 여부를 속삭이네
isFollow 필드 반짝이고
읽기 트랜잭션 최적화되며
사용자 경험, 더욱 세밀해진다! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 52.94% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 프로필 조회 API 리팩토링이라는 주요 변경사항을 명확하게 설명하고 있으며, 변경사항의 핵심을 정확하게 반영하고 있습니다.
Linked Issues check ✅ Passed PR의 모든 코드 변경이 #104 이슈의 요구사항을 충족합니다: 팔로우 여부 필드 추가, 로그인/비로그인 분기 처리, API 로직 및 명세서 수정이 구현되어 있습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 프로필 조회 API 리팩토링 범위 내에 있습니다. FollowService의 @transactional 추가는 관련된 조회 최적화이며, 핵심 요구사항을 벗어나는 변경은 없습니다.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/profile

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between ed073b8 and bdbb4b3.

📒 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

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


### 팔로우 요청
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.

@Be-HinD Be-HinD merged commit 40ae502 into main Dec 13, 2025
1 check passed
@Be-HinD Be-HinD deleted the refactor/profile branch December 13, 2025 11:49
@github-project-automation github-project-automation bot moved this from Backlog to Done in WeGo-Together Backend Dec 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨enhancement New feature or request

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[TASK] 프로필 조회 API 리팩토링 작업

1 participant