Fix: 유저 정보 조회 api 물 관련 수정#165
Conversation
Walkthrough인증 사용자 기반으로 UserController의 프로필 조회 흐름을 수정하고, UserService의 급수일 계산 로직을 전역 유틸 TimeUtil로 이관했습니다. Asia/Seoul 기준 정오 시작 규칙을 TimeUtil에 추가했으며, 불필요한 예외/헬퍼 메서드와 관련 import를 정리했습니다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Client
participant Controller as UserController
participant Security as SecurityContext
participant Service as UserService
participant Time as TimeUtil
Client->>Controller: GET /users/{userId}
Controller->>Security: 인증 사용자 조회
Security-->>Controller: Authenticated User
Controller->>Service: getUserProfile(currentUserId, targetUserId)
Service->>Time: getStartOfCurrentWateringDay()
Time-->>Service: LocalDateTime(startAtNoonKST)
Service-->>Controller: UserProfileResponse
Controller-->>Client: 200 OK (ApiResponse)
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
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: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
src/main/java/com/example/cp_main_be/domain/member/user/presentation/UserController.java (1)
45-49: 경로 변수 바인딩 오류(@RequestParam ↔ @PathVariable).매핑은
/me/{avatarId}인데 파라미터는@RequestParam("avatarId")로 선언되어 있어 400 바인딩 오류가 발생합니다.@PathVariable로 수정하세요.- public ResponseEntity<ApiResponse<Void>> updateAvatar( - @AuthenticationPrincipal User user, - @RequestBody @Valid AvatarChangeRequest request, - @RequestParam("avatarId") Long avatarId) { + public ResponseEntity<ApiResponse<Void>> updateAvatar( + @AuthenticationPrincipal User user, + @RequestBody @Valid AvatarChangeRequest request, + @PathVariable("avatarId") Long avatarId) {src/main/java/com/example/cp_main_be/domain/member/user/service/UserService.java (3)
73-81: 영속 엔티티가 아닌 파라미터 객체를 save 하고 있습니다.
managedUser를 갱신해놓고userRepository.save(user)를 호출합니다. 영속 상태 보장 및 변경감지 일관성을 위해managedUser를 저장하세요.- managedUser.updateProfile(newNickname, null); - userRepository.save(user); + managedUser.updateProfile(newNickname, null); + userRepository.save(managedUser);
51-71: 권한 체크 부재: 타인의 아바타 수정 가능성.
avatarId만으로 조회 후 수정하면 소유자 검증이 없어 타인의 아바타가 수정될 수 있습니다. 소유자 제약으로 조회하거나, 조회 후 소유자 일치 여부를 검증하세요.두 가지 중 택1:
- 옵션 A(권장): 리포지토리 제약 조회 메서드 사용
Avatar avatar = avatarRepository.findByIdAndUserId(avatarId, user.getId()) .orElseThrow(() -> new CustomApiException(ErrorCode.AVATAR_NOT_FOUND));
- 옵션 B: 조회 후 소유자 검증(도메인 모델에 맞춰 필드/메서드명 조정)
Avatar avatar = avatarRepository .findById(avatarId) .orElseThrow(() -> new CustomApiException(ErrorCode.AVATAR_NOT_FOUND)); + // TODO: 프로젝트의 ErrorCode(예: FORBIDDEN/ACCESS_DENIED)에 맞춰 교체 + if (!avatar.belongsTo(user)) { + throw new CustomApiException(ErrorCode.FORBIDDEN, "본인 아바타만 수정할 수 있습니다."); + }
247-253: NPE 위험: avatar가 null일 수 있는 케이스를 빌더에서 직접 역참조합니다.
avatarName,avatarImageUrl에서garden.getAvatar()를 바로 역참조하고 있어 NPE가 납니다. 로컬 변수로 안전하게 처리하세요.- HomeResponseDto.AvatarInfo avatarInfoForGarden = - HomeResponseDto.AvatarInfo.builder() - // 아바타가 없는 텃밭이 있을 수 있는 예외 케이스를 방어합니다. - .avatarId(garden.getAvatar() != null ? garden.getAvatar().getId() : null) - .avatarName(garden.getAvatar().getNickname()) - .avatarImageUrl(garden.getAvatar().getAvatarMaster().getDefaultImageUrl()) - .build(); + var avatar = garden.getAvatar(); + String avatarName = + (avatar != null) ? avatar.getNickname() : null; + String avatarImageUrl = + (avatar == null) + ? null + : (avatar.getImageUrl() != null && !avatar.getImageUrl().isBlank() + ? avatar.getImageUrl() + : avatar.getAvatarMaster().getDefaultImageUrl()); + + HomeResponseDto.AvatarInfo avatarInfoForGarden = + HomeResponseDto.AvatarInfo.builder() + .avatarId(avatar != null ? avatar.getId() : null) + .avatarName(avatarName) + .avatarImageUrl(avatarImageUrl) + .build();
🧹 Nitpick comments (3)
src/main/java/com/example/cp_main_be/global/util/TimeUtil.java (1)
20-29: 정오 경계 처리는 적절합니다. 테스트 용이성과 가독성 개선 제안(Clock 주입, LocalTime.NOON 사용).동작은 맞습니다. 다만 테스트(경계 시각, 11:59:59/12:00:00) 용이성을 위해 Clock 주입 오버로드를 추가하고, NOON 상수를 사용하면 의도가 더 분명합니다.
변경 제안(diff: 기존 메서드 본문만 최소 수정):
public static LocalDateTime getStartOfCurrentWateringDay() { - LocalDateTime now = LocalDateTime.now(KST); - LocalDateTime todayNoon = now.toLocalDate().atTime(12, 0); - - if (now.isBefore(todayNoon)) { - return todayNoon.minusDays(1); // 아직 정오가 안 지났으면, 어제 정오가 시작 시간 - } else { - return todayNoon; // 정오가 지났으면, 오늘 정오가 시작 시간 - } + return getStartOfCurrentWateringDay(java.time.Clock.system(KST)); }오버로드 추가(선택):
public static LocalDateTime getStartOfCurrentWateringDay(java.time.Clock clock) { var now = java.time.ZonedDateTime.now(clock); var todayNoon = now.toLocalDate().atTime(java.time.LocalTime.NOON).atZone(KST); return now.isBefore(todayNoon) ? todayNoon.minusDays(1).toLocalDateTime() : todayNoon.toLocalDateTime(); }src/main/java/com/example/cp_main_be/domain/member/user/service/UserService.java (2)
167-173: 읽기 전용 트랜잭션으로 명시해 불필요한 쓰기 플러시를 방지하세요.
getUserProfile는 조회 전용이므로@Transactional(readOnly = true)부여를 권장합니다.- public UserProfileResponse getUserProfile(Long currentUserId, Long profileUserId) { + @Transactional(readOnly = true) + public UserProfileResponse getUserProfile(Long currentUserId, Long profileUserId) {Also applies to: 213-214
242-246: 주석과 변수 의미가 불일치합니다(가독성).주석은 “아직 안 줬고 횟수 남으면 true”를 설명하지만, 바로 아래
alreadyWateredByMe는 반대 의미입니다. 혼동 방지를 위해 주석을isWateringAbleByMe에 맞춰 이동/수정하세요.
📜 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 (3)
src/main/java/com/example/cp_main_be/domain/member/user/presentation/UserController.java(1 hunks)src/main/java/com/example/cp_main_be/domain/member/user/service/UserService.java(4 hunks)src/main/java/com/example/cp_main_be/global/util/TimeUtil.java(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/example/cp_main_be/domain/member/user/service/UserService.java (1)
src/main/java/com/example/cp_main_be/global/util/TimeUtil.java (1)
TimeUtil(7-33)
🔇 Additional comments (1)
src/main/java/com/example/cp_main_be/domain/member/user/presentation/UserController.java (1)
96-99: 서비스 위임 및 인증 사용자 활용으로 책임 분리가 좋아졌습니다.현재 사용자와 대상 사용자 ID를 분리해 전달하는 변경은 명확하고 안전합니다.
📝 개요
이번 PR의 핵심 내용을 한 줄로 요약해 주세요.
💻 작업 내용
이번 PR에서 작업한 내용을 상세히 설명해 주세요.
작업 내용 1
작업 내용 2
...
✅ PR 체크리스트
PR을 보내기 전에 아래 체크리스트를 확인해 주세요.
커밋 메시지는 포맷에 맞게 작성했나요?
스스로 코드를 다시 한번 검토했나요?
관련 이슈를 연결했나요?
빌드 및 테스트가 로컬에서 성공했나요?
🔗 관련 이슈
스크린샷 (선택)
UI 변경 사항이 있다면 스크린샷을 첨부해 주세요.
Summary by CodeRabbit