refactor: 특정 유저 프로필 조회 수정#140
Hidden character warning
Conversation
Walkthrough프로필 조회 로직(getUserProfile)에 프로필 이미지 결정 규칙을 추가해 정렬된 정원 아바타 이미지를 우선 사용하고, 없을 경우 기존 프로필 이미지를 사용하도록 변경. addExperience의 사용자 미존재 처리 강화 및 getCurrentUser의 instanceof 패턴 매칭 리팩터링 포함. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant U as Client
participant S as UserService
participant R as UserRepository
participant G as Gardens (from User)
participant A as Avatar/AvatarMaster
U->>S: getUserProfile(profileUserId, currentUserId)
S->>R: findById(profileUserId)
R-->>S: profileUser
S->>R: findById(currentUserId)
R-->>S: currentUser
rect rgba(230,245,255,0.6)
S->>G: profileUser.getGardens()
Note right of S: slotNumber 오름차순 최솟값 선택
S->>S: select min slotNumber garden
alt 아바타 및 기본 이미지 존재
S->>A: garden.getAvatar().getAvatarMaster().getDefaultImageUrl()
A-->>S: avatarImageUrl
S->>S: profileImageUrl = avatarImageUrl
else 미존재
S->>S: profileImageUrl = profileUser.getProfileImageUrl()
end
end
S-->>U: UserProfileResponse(profileImageUrl 포함)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/main/java/com/example/cp_main_be/domain/member/user/service/UserService.java (2)
121-126: anonymousUser 등 UUID 아님 문자열 처리 누락 → 500 위험Spring Security 기본 익명 principal("anonymousUser")가 들어오면
UUID.fromString(...)에서IllegalArgumentException이 발생하여 500으로 이어질 수 있습니다. try/catch로 감싸 401/적절한 에러로 변환하세요.- if (principal instanceof String uuidString) { - UUID userUuid = UUID.fromString(uuidString); - return userRepository - .findByUuid(userUuid) - .orElseThrow(() -> new IllegalArgumentException("현재 로그인한 사용자를 찾을 수 없습니다.")); - } + if (principal instanceof String uuidString) { + try { + UUID userUuid = UUID.fromString(uuidString); + return userRepository + .findByUuid(userUuid) + .orElseThrow(() -> new IllegalArgumentException("현재 로그인한 사용자를 찾을 수 없습니다.")); + } catch (IllegalArgumentException e) { + // e.g. "anonymousUser" 등 UUID가 아닌 문자열인 경우 + throw new IllegalArgumentException("인증되지 않은 사용자입니다."); + } + }
198-203: 정원 아바타 null 시 NPE일부 정원에 아바타가 없을 수 있습니다. 현재는
garden.getAvatar().get...로 바로 접근하여 NPE 위험이 있습니다. null-safe로 빌드하세요.- HomeResponseDto.AvatarInfo avatarInfoForGarden = - HomeResponseDto.AvatarInfo.builder() - .avatarName(garden.getAvatar().getNickname()) - .avatarImageUrl(garden.getAvatar().getAvatarMaster().getDefaultImageUrl()) - .build(); + Avatar avatar = garden.getAvatar(); + String avatarName = (avatar != null) ? avatar.getNickname() : null; + String avatarImageUrl = + (avatar != null && avatar.getAvatarMaster() != null) + ? avatar.getAvatarMaster().getDefaultImageUrl() + : null; + + HomeResponseDto.AvatarInfo avatarInfoForGarden = + HomeResponseDto.AvatarInfo.builder() + .avatarName(avatarName) + .avatarImageUrl(avatarImageUrl) + .build();
🧹 Nitpick comments (2)
src/main/java/com/example/cp_main_be/domain/member/user/service/UserService.java (2)
45-49: Optional.get() 회피는 👍; 예외 타입/메시지 일관성 검토 제안동일 서비스 내에서
CustomApiException(ErrorCode.NOT_FOUND)/UserNotFoundException("...")/UserNotFoundException(ErrorCode.USER_NOT_FOUND.getMessage())가 혼재합니다. 컨트롤러/글로벌 핸들러 기준으로 하나의 예외/코드 체계로 통일하는 것을 권장합니다.
175-180: 남에게 물 주기 남은 횟수 음수 가능성 클램프 필요DB/비즈니스 규칙에 따라 당일 횟수가 3 초과일 수 있다면 음수 응답이 나옵니다. 0으로 클램프하세요.
- Long leftWaterCountForOthers = - (long) (3 - todayWateringCountForOthers); // MAX_FRIEND_WATERING_PER_DAY = 3 + Long leftWaterCountForOthers = + Math.max(0L, 3L - (long) todayWateringCountForOthers); // MAX_FRIEND_WATERING_PER_DAY = 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
src/main/java/com/example/cp_main_be/domain/member/user/service/UserService.java(6 hunks)
🔇 Additional comments (2)
src/main/java/com/example/cp_main_be/domain/member/user/service/UserService.java (2)
5-5: 필요한 import 추가 적절새로운 로직에서 Garden/Comparator/Objects가 실제로 사용되고 있어 적합합니다.
Also applies to: 22-22, 24-24
216-224: 동적 프로필 이미지 적용 변경 사항 LGTM가든 기반 대표 이미지 선택 후 기존 프로필 이미지로 폴백하는 전략이 명확합니다.
| // [추가] 프로필 이미지 URL을 첫 번째 텃밭의 아바타 이미지로 설정 | ||
| String profileImageUrl = | ||
| profileUser.getGardens().stream() | ||
| .min(Comparator.comparing(Garden::getSlotNumber)) // 슬롯 번호가 가장 낮은 텃밭 찾기 | ||
| .map(Garden::getAvatar) // 해당 텃밭의 아바타 가져오기 | ||
| .filter(Objects::nonNull) // 아바타가 null이 아닌 경우 필터링 | ||
| .map(avatar -> avatar.getAvatarMaster().getDefaultImageUrl()) // 아바타의 이미지 URL 가져오기 | ||
| .orElse(profileUser.getProfileImageUrl()); // 텃밭/아바타가 없으면 기존 프로필 이미지 사용 | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
프로필 이미지 URL 계산 시 NPE 가능성(avatarMaster/defaultImageUrl null)
avatar.getAvatarMaster().getDefaultImageUrl() 체인이 null을 전제하지 않습니다. null-safe 필터를 추가해 안전하게 처리하세요. (slotNumber가 null일 수 있다면 Comparator.nullsLast(...)도 고려)
- .map(avatar -> avatar.getAvatarMaster().getDefaultImageUrl()) // 아바타의 이미지 URL 가져오기
- .orElse(profileUser.getProfileImageUrl()); // 텃밭/아바타가 없으면 기존 프로필 이미지 사용
+ .map(Avatar::getAvatarMaster) // 마스터 객체 추출
+ .filter(Objects::nonNull) // null 안전
+ .map(master -> master.getDefaultImageUrl()) // 기본 이미지 URL
+ .filter(Objects::nonNull) // null URL 방지
+ .orElse(profileUser.getProfileImageUrl()); // 텃밭/아바타/이미지 없으면 기존 프로필 이미지추가로(선택): slotNumber null 방지를 위해
- .min(Comparator.comparing(Garden::getSlotNumber))
+ .min(Comparator.comparing(Garden::getSlotNumber, Comparator.nullsLast(Comparator.naturalOrder())))📝 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.
| // [추가] 프로필 이미지 URL을 첫 번째 텃밭의 아바타 이미지로 설정 | |
| String profileImageUrl = | |
| profileUser.getGardens().stream() | |
| .min(Comparator.comparing(Garden::getSlotNumber)) // 슬롯 번호가 가장 낮은 텃밭 찾기 | |
| .map(Garden::getAvatar) // 해당 텃밭의 아바타 가져오기 | |
| .filter(Objects::nonNull) // 아바타가 null이 아닌 경우 필터링 | |
| .map(avatar -> avatar.getAvatarMaster().getDefaultImageUrl()) // 아바타의 이미지 URL 가져오기 | |
| .orElse(profileUser.getProfileImageUrl()); // 텃밭/아바타가 없으면 기존 프로필 이미지 사용 | |
| // [추가] 프로필 이미지 URL을 첫 번째 텃밭의 아바타 이미지로 설정 | |
| String profileImageUrl = | |
| profileUser.getGardens().stream() | |
| .min(Comparator.comparing( | |
| Garden::getSlotNumber, | |
| Comparator.nullsLast(Comparator.naturalOrder()) | |
| )) // 슬롯 번호가 가장 낮은 텃밭 찾기 (null-safe) | |
| .map(Garden::getAvatar) // 해당 텃밭의 아바타 가져오기 | |
| .filter(Objects::nonNull) // 아바타가 null이 아닌 경우 필터링 | |
| - .map(avatar -> avatar.getAvatarMaster().getDefaultImageUrl()) // 아바타의 이미지 URL 가져오기 | |
| .map(Avatar::getAvatarMaster) // 마스터 객체 추출 | |
| .filter(Objects::nonNull) // null 안전 | |
| .map(master -> master.getDefaultImageUrl()) // 기본 이미지 URL | |
| .filter(Objects::nonNull) // null URL 방지 | |
| .orElse(profileUser.getProfileImageUrl()); // 텃밭/아바타/이미지 없으면 기존 프로필 이미지 |
🤖 Prompt for AI Agents
In
src/main/java/com/example/cp_main_be/domain/member/user/service/UserService.java
around lines 148 to 156, the stream chain
avatar.getAvatarMaster().getDefaultImageUrl() can throw NPE if avatarMaster or
its defaultImageUrl is null (and Comparator may throw if slotNumber is null);
fix by adding null-safe checks: when mapping from Garden to avatar, filter out
null avatars, then filter avatar.getAvatarMaster() != null and
avatar.getAvatarMaster().getDefaultImageUrl() != null before mapping to the URL,
and use Comparator.nullsLast(Comparator.comparing(Garden::getSlotNumber)) (or
Comparator.comparing(g -> g.getSlotNumber(), Comparator.nullsLast(...))) to
handle null slotNumber values so the code safely falls back to
profileUser.getProfileImageUrl().
📝 개요
이번 PR의 핵심 내용을 한 줄로 요약해 주세요.
💻 작업 내용
이번 PR에서 작업한 내용을 상세히 설명해 주세요.
작업 내용 1
작업 내용 2
...
✅ PR 체크리스트
PR을 보내기 전에 아래 체크리스트를 확인해 주세요.
커밋 메시지는 포맷에 맞게 작성했나요?
스스로 코드를 다시 한번 검토했나요?
관련 이슈를 연결했나요?
빌드 및 테스트가 로컬에서 성공했나요?
🔗 관련 이슈
이번 PR과 관련된 이슈 번호를 기재해 주세요. 예: Closes #138
스크린샷 (선택)
UI 변경 사항이 있다면 스크린샷을 첨부해 주세요.
Summary by CodeRabbit