Conversation
- 프로필 이미지 변경 API 개발 - 프로필 정보 변경 API 개발 (JSON) - .http 테스트 추가
Walkthrough사용자 프로필 편집 기능이 추가되었습니다: 프로필 이미지 업로드, 기본 정보(닉네임·MBTI·프로필 메시지) 수정, 알림 설정 변경을 위한 서비스·컨트롤러·DTO·엔터티 메서드가 추가되었고 API 경로가 Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant UserController
participant UserService
participant ImageUploadService
participant UserRepository
rect rgb(200,220,255)
Note over Client,UserRepository: 프로필 이미지 업로드
Client->>UserController: PATCH /api/v1/users/profile-image (file)
UserController->>UserService: updateProfileImage(userId, file)
UserService->>ImageUploadService: upload(file)
ImageUploadService-->>UserService: ImageFileResponse(url)
UserService->>UserRepository: findById(userId)
UserRepository-->>UserService: User
UserService->>User: updateProfileImage(url)
UserService->>UserRepository: save(User)
UserService-->>UserController: UserInfoResponse
UserController-->>Client: 200 OK
end
sequenceDiagram
participant Client
participant UserController
participant UserService
participant UserRepository
rect rgb(220,255,200)
Note over Client,UserRepository: 프로필 정보 수정
Client->>UserController: PATCH /api/v1/users/profile (ProfileUpdateRequest)
UserController->>UserService: updateProfileInfo(userId, request)
UserService->>UserRepository: findById(userId)
UserRepository-->>UserService: User
UserService->>User: updateNickName/updateMbti/updateProfileMessage
UserService->>UserRepository: save(User)
UserService-->>UserController: UserInfoResponse
UserController-->>Client: 200 OK
end
Estimated code review effort🎯 3 (중간) | ⏱️ ~25분
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/team/wego/wegobackend/user/presentation/UserController.java (1)
47-55: Remove sensitive fields from public profile endpointThe GET
/{userId}endpoint exposes private user data throughUserInfoResponse, includingisNotificationEnabled(a user preference/setting) andCreate a separate public profile DTO (e.g.,
PublicUserInfoResponse) that excludesisNotificationEnabled, and use it in theprofile()method. KeepUserInfoResponsefor authenticated endpoints like the profile update.
🧹 Nitpick comments (3)
src/main/java/team/wego/wegobackend/user/application/dto/request/ProfileUpdateRequest.java (1)
16-17: MBTI 유효성 검사 강화 권장현재
@Size(max = 4)만 적용되어 있어 "ABC"나 "1234" 같은 잘못된 값도 허용됩니다. MBTI 형식에 맞는 패턴 검증을 추가하는 것이 좋습니다.+import jakarta.validation.constraints.Pattern; + @Getter @Setter public class ProfileUpdateRequest { // ... - @Size(max = 4, message = "MBTI는 4자여야 합니다") + @Pattern(regexp = "^(E|I)(N|S)(T|F)(J|P)$", message = "올바른 MBTI 형식이 아닙니다") private String mbti;src/main/java/team/wego/wegobackend/user/presentation/UserController.java (2)
57-68: PATCH 요청에 HTTP 201 대신 200 사용 권장
PATCH메서드는 기존 리소스를 수정하므로 일반적으로200 OK를 반환합니다.201 Created는 새로운 리소스가 생성될 때(주로POST) 사용됩니다.return ResponseEntity - .status(HttpStatus.CREATED) - .body(ApiResponse.success(201, response)); + .status(HttpStatus.OK) + .body(ApiResponse.success(200, response));
70-81: 동일하게 200 OK 반환 권장프로필 정보 수정도 업데이트 작업이므로
200 OK가 더 적절합니다.return ResponseEntity - .status(HttpStatus.CREATED) - .body(ApiResponse.success(201, response)); + .status(HttpStatus.OK) + .body(ApiResponse.success(200, response));
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/main/java/team/wego/wegobackend/auth/presentation/AuthController.java(0 hunks)src/main/java/team/wego/wegobackend/user/application/UserService.java(2 hunks)src/main/java/team/wego/wegobackend/user/application/dto/request/ProfileUpdateRequest.java(1 hunks)src/main/java/team/wego/wegobackend/user/domain/User.java(1 hunks)src/main/java/team/wego/wegobackend/user/presentation/UserController.java(3 hunks)src/test/http/user/user-api.http(1 hunks)
💤 Files with no reviewable changes (1)
- src/main/java/team/wego/wegobackend/auth/presentation/AuthController.java
🧰 Additional context used
🧬 Code graph analysis (2)
src/main/java/team/wego/wegobackend/user/presentation/UserController.java (4)
src/main/java/team/wego/wegobackend/user/application/UserService.java (1)
Slf4j(17-67)src/main/java/team/wego/wegobackend/auth/application/AuthService.java (1)
Slf4j(24-111)src/main/java/team/wego/wegobackend/common/security/jwt/JwtTokenProvider.java (1)
Slf4j(23-163)src/main/java/team/wego/wegobackend/image/presentation/ImageController.java (1)
RestController(21-118)
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 (2)
src/main/java/team/wego/wegobackend/user/domain/User.java (1)
120-138: LGTM! 도메인 업데이트 메서드가 올바르게 구현되었습니다.기존 카운트 메서드들과 일관된 패턴으로 프로필 업데이트 메서드가 추가되었습니다. 유효성 검사는 DTO 레벨(
ProfileUpdateRequest)에서 처리되므로 도메인 메서드는 단순하게 유지됩니다.src/test/http/user/user-api.http (1)
24-50: LGTM! 새로운 엔드포인트에 대한 테스트 케이스가 잘 작성되었습니다.프로필 이미지 업로드(multipart)와 프로필 정보 수정(JSON) 테스트가 적절하게 구성되어 있습니다. 이미지 파일 경로(
../image/resources/img1.png)도 올바르게 참조되고 있습니다.
| @Size(max = 50, message = "프로필 메시지는 50자 이하여야 합니다") | ||
| private String profileMessage; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find User entity class and profileMessage field definition
find . -type f -name "User.java" | head -20Repository: WeGo-Together/WeGo_BackEnd
Length of output: 129
🏁 Script executed:
# Search for profileMessage column length in Java files
rg -A 3 "profileMessage" --type java | head -50Repository: WeGo-Together/WeGo_BackEnd
Length of output: 2038
🏁 Script executed:
# More specific search for @Column annotation with length specification
rg "@Column.*profileMessage|profileMessage.*@Column" -A 2 -B 2 --type javaRepository: WeGo-Together/WeGo_BackEnd
Length of output: 52
🏁 Script executed:
# Read User.java to see the profileMessage field definition and surrounding context
sed -n '1,200p' ./src/main/java/team/wego/wegobackend/user/domain/User.java | cat -nRepository: WeGo-Together/WeGo_BackEnd
Length of output: 4736
🏁 Script executed:
# Search more specifically for profileMessage with preceding lines to see annotations
rg -B 5 "private String profileMessage" ./src/main/java/team/wego/wegobackend/user/domain/User.javaRepository: WeGo-Together/WeGo_BackEnd
Length of output: 246
profileMessage 길이 제한 불일치 확인 필요
DTO에서는 50자로 제한하지만, User 엔티티의 profile_message 컬럼은 500자를 허용합니다. 의도적인 제한인지 명확히 하거나 제한값을 정렬하세요.
| public ImageFileResponse updateProfileImage(Long userId, MultipartFile file) { | ||
|
|
||
| User user = userRepository.findById(userId).orElseThrow(UserNotFoundException::new); | ||
|
|
||
| ImageFile image = imageUploadService.uploadAsWebpWithSize(file, 440, 240); | ||
|
|
||
| user.updateProfileImage(image.url()); | ||
|
|
||
| return ImageFileResponse.from(image); | ||
| } |
There was a problem hiding this comment.
기존 프로필 이미지 삭제 누락 가능성
새 프로필 이미지를 업로드하기 전에 기존 이미지를 삭제하지 않으면 스토리지에 고아 파일(orphaned files)이 누적될 수 있습니다.
기존 이미지 삭제 로직 추가를 고려해 주세요:
public ImageFileResponse updateProfileImage(Long userId, MultipartFile file) {
User user = userRepository.findById(userId).orElseThrow(UserNotFoundException::new);
+ // 기존 프로필 이미지가 있으면 삭제
+ if (user.getProfileImage() != null) {
+ imageUploadService.delete(user.getProfileImage());
+ }
+
ImageFile image = imageUploadService.uploadAsWebpWithSize(file, 440, 240);
user.updateProfileImage(image.url());
return ImageFileResponse.from(image);
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/main/java/team/wego/wegobackend/user/application/UserService.java around
lines 34–43, the method can leave orphaned old profile images because it uploads
a new image without removing the previous one; capture the user's current
profile image URL before uploading, perform the upload, update and persist the
user's profileImage with the new URL, then if the previous URL is non-null and
different attempt to delete the old image via imageUploadService (wrap the
delete in try/catch so failures are logged but do not break the update), and
ensure the removal happens after a successful upload/update (or after
transaction commit) to avoid data loss.
| public UserInfoResponse updateProfileInfo(Long userId, ProfileUpdateRequest request) { | ||
|
|
||
| User user = userRepository.findById(userId).orElseThrow(UserNotFoundException::new); | ||
|
|
||
| if (request.getNickName() != null) { | ||
| user.updateNickName(request.getNickName()); | ||
| } | ||
|
|
||
| if (request.getNotificationEnabled() != null) { | ||
| user.updateNotificationEnabled(request.getNotificationEnabled()); | ||
| } | ||
|
|
||
| if (request.getMbti() != null) { | ||
| user.updateMbti(request.getMbti()); | ||
| } | ||
|
|
||
| if (request.getProfileMessage() != null) { | ||
| user.updateProfileMessage(request.getProfileMessage()); | ||
| } | ||
|
|
||
| return UserInfoResponse.from(user); | ||
| } |
There was a problem hiding this comment.
닉네임 중복 검사 누락
nickName 컬럼에 unique 제약 조건이 있지만, 업데이트 전 중복 검사가 없습니다. 중복 시 DataIntegrityViolationException이 발생하여 사용자에게 불친절한 에러 메시지가 전달됩니다.
닉네임 중복 검사 추가를 권장합니다:
public UserInfoResponse updateProfileInfo(Long userId, ProfileUpdateRequest request) {
User user = userRepository.findById(userId).orElseThrow(UserNotFoundException::new);
if (request.getNickName() != null) {
+ // 본인의 현재 닉네임이 아니고, 이미 존재하는 닉네임인 경우
+ if (!request.getNickName().equals(user.getNickName())
+ && userRepository.existsByNickName(request.getNickName())) {
+ throw new NickNameAlreadyExistsException();
+ }
user.updateNickName(request.getNickName());
}UserRepository에 existsByNickName 메서드와 적절한 예외 클래스 추가가 필요합니다.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/main/java/team/wego/wegobackend/user/application/UserService.java around
lines 45-66, the updateProfileInfo method misses a duplicate check for nickName
before persisting, causing DataIntegrityViolationException on unique constraint
violation; add a pre-check: if request.getNickName() is non-null and different
from the current user.getNickName(), call
userRepository.existsByNickName(request.getNickName()) (add this method to
UserRepository) and if true throw a clear domain exception (e.g.,
NickNameAlreadyExistsException); then only call user.updateNickName(...) when
the check passes. Ensure the new exception is handled by the
controller/exception handler to return a friendly error to clients.
- mbti 열거형으로 변경
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (1)
src/main/java/team/wego/wegobackend/user/application/dto/request/ProfileUpdateRequest.java (1)
19-20: profileMessage 길이 제한 불일치를 재확인하세요.DTO에서는 최대 20자로 제한하지만, 이전 리뷰에서 User 엔티티의 profile_message 컬럼은 500자를 허용한다고 지적되었습니다. 현재 20자 제한이 의도적인 것인지, 아니면 엔티티와 맞춰야 하는지 명확히 해야 합니다.
다음 스크립트로 엔티티의 컬럼 길이를 확인하세요:
#!/bin/bash # User 엔티티에서 profileMessage 필드의 @Column 정의 확인 rg -B 5 "private String profileMessage" --type java src/main/java/team/wego/wegobackend/user/domain/User.java
🧹 Nitpick comments (6)
src/test/http/user/user-api.http (2)
40-49: JSON 프로필 업데이트 테스트 구조 적절요청 구조와 데이터가 적절합니다. 다만 에러 케이스 테스트(예: 잘못된 MBTI 값, 빈 닉네임, 너무 긴 프로필 메시지 등)도 추가하시면 더욱 견고한 API가 될 것입니다.
51-53: 알림 설정 변경 방식 개선 제안PATCH 요청에서 쿼리 파라미터 대신 request body를 사용하는 것이 RESTful API 관례에 더 부합합니다.
다음과 같이 변경을 고려해보세요:
-### 알림 설정 변경 -PATCH http://localhost:8080/api/v1/users/notification?isNotificationEnabled=true -Authorization: Bearer {{accessToken}} +### 알림 설정 변경 +PATCH http://localhost:8080/api/v1/users/notification +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "isNotificationEnabled": true +}src/main/java/team/wego/wegobackend/auth/application/dto/request/LoginRequest.java (1)
20-22: 비밀번호 검증 강화를 고려하세요.현재는 특수문자만 검증하고 있습니다. 보안을 강화하려면 대문자, 소문자, 숫자 포함 여부도 검증하는 것을 권장합니다.
예시:
-@Pattern(regexp = "^(?=.*[!@#$%^&*]).*$", - message = "특수문자(!@#$%^&*)를 1개 이상 포함해야 합니다") +@Pattern(regexp = "^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)(?=.*[!@#$%^&*]).*$", + message = "대문자, 소문자, 숫자, 특수문자(!@#$%^&*)를 각각 1개 이상 포함해야 합니다")src/main/java/team/wego/wegobackend/user/application/dto/request/ProfileUpdateRequest.java (1)
13-15: 에러 메시지 일관성 개선이 필요합니다."이름은 필수입니다"보다는 "닉네임은 필수입니다"가 필드명과 일관성이 있습니다.
-@NotBlank(message = "이름은 필수입니다") +@NotBlank(message = "닉네임은 필수입니다")src/main/java/team/wego/wegobackend/user/presentation/UserController.java (2)
21-21: 미사용 import를 제거하세요.
ImageFileResponse가 사용되지 않습니다.-import team.wego.wegobackend.image.application.dto.ImageFileResponse;
87-87: 중복된 속성 제거를 고려하세요.
@RequestParam(required = true)에서required = true는 기본값이므로 생략 가능합니다.-@RequestParam(required = true) Boolean isNotificationEnabled +@RequestParam Boolean isNotificationEnabled
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
src/main/java/team/wego/wegobackend/auth/application/dto/request/LoginRequest.java(2 hunks)src/main/java/team/wego/wegobackend/auth/application/dto/request/SignupRequest.java(2 hunks)src/main/java/team/wego/wegobackend/user/application/UserService.java(2 hunks)src/main/java/team/wego/wegobackend/user/application/dto/Mbti.java(1 hunks)src/main/java/team/wego/wegobackend/user/application/dto/request/ProfileUpdateRequest.java(1 hunks)src/main/java/team/wego/wegobackend/user/presentation/UserController.java(3 hunks)src/test/http/user/user-api.http(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/main/java/team/wego/wegobackend/user/application/UserService.java
🧰 Additional context used
🧬 Code graph analysis (2)
src/main/java/team/wego/wegobackend/user/application/dto/request/ProfileUpdateRequest.java (2)
src/main/java/team/wego/wegobackend/auth/application/dto/request/LoginRequest.java (1)
Getter(10-24)src/main/java/team/wego/wegobackend/auth/application/dto/request/SignupRequest.java (1)
Getter(10-29)
src/main/java/team/wego/wegobackend/user/presentation/UserController.java (1)
src/main/java/team/wego/wegobackend/user/application/UserService.java (1)
Slf4j(17-72)
🔇 Additional comments (6)
src/test/http/user/user-api.http (1)
24-26: URL 경로 표준화 변경 확인복수형 리소스 경로(
/users)로의 변경이 RESTful API 관례에 부합합니다.src/main/java/team/wego/wegobackend/auth/application/dto/request/LoginRequest.java (1)
16-16: 이메일 길이 제한이 추가되어 좋습니다.최대 50자 제한이 합리적이며, SignupRequest와 일관성을 유지하고 있습니다.
src/main/java/team/wego/wegobackend/user/application/dto/Mbti.java (1)
3-8: MBTI 열거형이 올바르게 정의되었습니다.16개의 표준 MBTI 유형이 모두 포함되어 있으며, 구현이 깔끔합니다.
src/main/java/team/wego/wegobackend/auth/application/dto/request/SignupRequest.java (1)
16-16: 검증 규칙이 일관성 있게 추가되었습니다.LoginRequest 및 ProfileUpdateRequest와 동일한 검증 규칙을 적용하여 일관성을 유지하고 있습니다. 다만, LoginRequest와 마찬가지로 비밀번호 패턴에 대문자/소문자/숫자 검증을 추가하는 것을 권장합니다.
Also applies to: 20-22, 26-26
src/main/java/team/wego/wegobackend/user/presentation/UserController.java (2)
29-29: API 경로가 RESTful 규칙에 맞게 변경되었습니다.단수형
/api/v1/user에서 복수형/api/v1/users로 변경하여 REST API 네이밍 컨벤션을 따릅니다.
48-48: 경로에 슬래시가 추가되어 일관성이 개선되었습니다.
@GetMapping("/{userId}")로 변경하여 명시적인 경로 표현이 되었습니다.
src/main/java/team/wego/wegobackend/user/application/dto/request/ProfileUpdateRequest.java
Show resolved
Hide resolved
| @NotBlank(message = "이름은 필수입니다") | ||
| @Size(min = 2, max = 14, message = "닉네임은 2-14자여야 합니다") | ||
| private String nickName; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# UserService의 updateProfileInfo 메서드에서 null 체크 로직 확인
rg -A 10 "updateProfileInfo" --type java src/main/java/team/wego/wegobackend/user/application/UserService.javaRepository: WeGo-Together/WeGo_BackEnd
Length of output: 483
Remove redundant null check or make nickName optional field.
The @NotBlank annotation on nickName in ProfileUpdateRequest enforces that the field cannot be null or blank. However, UserService's updateProfileInfo method contains if (request.getNickName() != null) which is unnecessary—validated requests will never have null nickName. Either remove the @NotBlank annotation to make nickName optional, or remove the null check in the service logic.
🤖 Prompt for AI Agents
In
src/main/java/team/wego/wegobackend/user/application/dto/request/ProfileUpdateRequest.java
around lines 13-15, the nickName field is annotated with @NotBlank and @Size but
UserService.updateProfileInfo still checks if (request.getNickName() != null);
since validation guarantees non-null/blank, remove the redundant null check in
updateProfileInfo and treat nickName as always provided, or alternatively if
nickName should be optional remove @NotBlank/@Size from this DTO and allow
nulls—pick one approach and apply the corresponding change consistently in the
service and DTO.
src/main/java/team/wego/wegobackend/user/presentation/UserController.java
Show resolved
Hide resolved
src/main/java/team/wego/wegobackend/user/presentation/UserController.java
Show resolved
Hide resolved
src/main/java/team/wego/wegobackend/user/presentation/UserController.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/main/java/team/wego/wegobackend/user/presentation/UserController.java (2)
21-21: 사용되지 않는 import가 있습니다.
ImageFileResponse가 import되어 있지만 이 파일에서 사용되지 않습니다. 제거해 주세요.-import team.wego.wegobackend.image.application.dto.ImageFileResponse;
84-96:required = true는 기본값이므로 생략 가능합니다.HTTP 상태 코드 수정은 잘 되었습니다. 다만,
@RequestParam의required속성은 기본값이true이므로 명시적으로 지정할 필요가 없습니다.- @RequestParam(required = true) Boolean isNotificationEnabled + @RequestParam Boolean isNotificationEnabled
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/main/java/team/wego/wegobackend/user/application/dto/request/ProfileUpdateRequest.java(1 hunks)src/main/java/team/wego/wegobackend/user/presentation/UserController.java(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/main/java/team/wego/wegobackend/user/application/dto/request/ProfileUpdateRequest.java
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/team/wego/wegobackend/user/presentation/UserController.java (1)
src/main/java/team/wego/wegobackend/user/application/UserService.java (1)
Slf4j(17-72)
🔇 Additional comments (3)
src/main/java/team/wego/wegobackend/user/presentation/UserController.java (3)
48-56: 프로필 조회 API의 인증 여부를 확인해 주세요.
/{userId}엔드포인트는 인증 없이 다른 사용자의 프로필을 조회할 수 있습니다. 이것이 의도된 설계(공개 프로필)인지, 아니면 인증된 사용자만 접근해야 하는지 확인이 필요합니다.새로 추가된 다른 엔드포인트들(
/profile-image,/profile,/notification)은 모두@AuthenticationPrincipal을 사용하여 본인 리소스만 수정할 수 있도록 되어 있습니다.
58-69: LGTM!HTTP 상태 코드가 200 OK로 올바르게 수정되었습니다.
@RequestPart를 사용한 multipart 파일 처리와 인증 사용자 기반 접근 제어가 적절합니다.
71-82: LGTM!HTTP 상태 코드가 200 OK로 올바르게 수정되었습니다.
@Valid어노테이션을 통한 요청 본문 검증과 인증 사용자 기반 접근 제어가 적절합니다.
📝 Pull Request
📌 PR 종류
해당하는 항목에 체크해주세요.
✨ 변경 내용
프로필 이미지 변경 API 추가
기본 정보 변경 API 추가 (닉네임, mbti, 상태메시지)
알림 설정 변경 API 추가
🔍 관련 이슈
해당 PR이 해결하는 이슈가 있다면 연결해주세요.
Closes #51
🧪 테스트
변경된 기능에 대한 테스트 범위 또는 테스트 결과를 작성해주세요.
🚨 확인해야 할 사항 (Checklist)
PR을 제출하기 전에 아래 항목들을 확인해주세요.
🙋 기타 참고 사항
리뷰어가 참고하면 좋을 만한 추가 설명이 있다면 적어주세요.
Summary by CodeRabbit
릴리스 노트
새 기능
개선
기타
✏️ Tip: You can customize this high-level summary in your review settings.