Skip to content

Conversation

@minjee2758
Copy link
Collaborator

@minjee2758 minjee2758 commented Aug 19, 2025

작업 내용

  • 유저 정보 수정 API 리팩토링
    • application/json과 multipart/form-data 요청을 일관성 있게 처리하도록 API 엔드포인트를 리팩토링
  • github auto push 상태 변경 시, 레포랑 브랜치명도 함께 response에 제공
    • 만약 설정 안한 유저다? -> null로 반환

변경 사항

  • @RequestBody ModifyUserInfoRequest@RequestPart("request") ModifyUserInfoRequest request로 변경
    • multipart/form-data 요청의 JSON 부분을 처리하도록 수정
    • userService.modifyUserInfo() 메서드 내, image 파라미터가 null이거나 비어 있는지(!image.isEmpty()) 확인

참고 사항


코드 리뷰 전 확인 체크리스트

  • 불필요한 콘솔 로그, 주석 제거
  • 커밋 메시지 컨벤션 준수 (type : )
  • 기능 정상 동작 확인

Summary by CodeRabbit

  • New Features

    • 사용자 정보 수정 시 텍스트 필드와 프로필 이미지를 한 번에 업로드할 수 있습니다(멀티파트 지원).
  • Refactor

    • 사용자 정보 수정 요청에서 profileImageUrl 입력을 제거하고 이미지 파일 파라미터로 처리합니다.
    • 별도 프로필 이미지 업로드 엔드포인트를 제거하고 수정 기능으로 통합했습니다.
    • 요청 형식이 JSON에서 multipart/form-data로 변경되어 클라이언트 업데이트가 필요합니다.
    • 새 이미지 업로드 시 기존 프로필 이미지는 자동으로 대체 및 삭제됩니다.
  • New Fields (API 응답)

    • 자동 푸시 관련 응답에 저장소명과 브랜치 정보가 추가되어 반환됩니다.

@coderabbitai
Copy link

coderabbitai bot commented Aug 19, 2025

Walkthrough

ModifyUserInfoRequest에서 profileImageUrl 필드를 제거하고, 사용자 수정 API를 multipart로 변경해 텍스트(request 파트)와 이미지(image 파트)를 함께 처리하도록 통합했습니다. 이미지가 주어지면 S3에 업로드하고 이전 이미지는 삭제하며, UserGitubAutoPushResponse에 githubRepoName과 branch 필드를 추가했습니다.

Changes

Cohort / File(s) Change summary
DTO 요청 변경
src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/request/ModifyUserInfoRequest.java
profileImageUrl 필드 및 관련 @Schema 제거. 레코드 구성: nickname, githubUrl, blogUrl, introduction, age.
컨트롤러(입력 바인딩 및 엔드포인트)
src/main/java/org/ezcode/codetest/presentation/usermanagement/UserController.java
modifyUserInfo 입력을 @RequestBody → multipart: @RequestPart("request") ModifyUserInfoRequest, @RequestPart(value="image", required=false) MultipartFile image로 변경. 별도의 프로필 업로드 엔드포인트 제거.
서비스 통합/시그니처 변경
src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java
modifyUserInfo(AuthUser, ModifyUserInfoRequest)modifyUserInfo(AuthUser, ModifyUserInfoRequest, MultipartFile). 이미지 제공 시 S3 업로드/삭제 처리 및 user.modifyProfileImage 호출로 프로필 갱신. 공개 uploadUserProfileImage 제거.
도메인 엔티티
src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java
modifyUserInfo 시그니처에서 profileImageUrl 제거 — 닉네임/깃허브/블로그/소개/나이만 갱신. 프로필 이미지 변경은 modifyProfileImage로 처리.
DTO 응답 확장
src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/UserGitubAutoPushResponse.java
githubRepoNamebranch 필드 추가. Lombok 생성자 시그니처가 확장되어 호출부 변경 필요.
서비스 응답 사용부 업데이트
src/main/java/org/ezcode/codetest/domain/user/service/UserGithubService.java
changeAutoPushSettinggetAutoPushStatus 등에서 UserGitubAutoPushResponse 생성 시 repo와 branch를 추가로 전달하도록 변경.

Sequence Diagram(s)

sequenceDiagram
  actor U as 사용자
  participant C as UserController
  participant S as UserService
  participant ST as S3
  participant E as User Entity

  U->>C: PUT /api/users (multipart: request + optional image)
  C->>S: modifyUserInfo(authUser, request, image)
  S->>E: modifyUserInfo(nickname, githubUrl, blogUrl, introduction, age)

  alt image 제공됨
    S->>ST: upload(image)
    ST-->>S: newProfileImageUrl
    S->>E: modifyProfileImage(newProfileImageUrl)
    opt 이전 이미지 존재
      S->>ST: delete(oldProfileImageUrl)
      ST-->>S: delete OK
    end
  else 이미지 없음
    Note over S,E: 프로필 이미지 변경 없음
  end

  S-->>C: UserInfoResponse
  C-->>U: 200 OK
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • NCookies
  • chat26666
  • Kimminu7
  • thezz9

Poem

토끼가 말하네, "요청은 한 손에, 사진은 한 손에 🐇
한 번에 척 붙여서 프로필 빛나네.
옛 사진은 보내주고, 새 URL 달아주면,
깡총깡총, 프로필이 산뜻하네.
축하해, 코드도 폴짝!"

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 0ab6727 and 5602c15.

📒 Files selected for processing (3)
  • src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/user/service/UserGithubService.java (4 hunks)
  • src/main/java/org/ezcode/codetest/presentation/usermanagement/UserController.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/org/ezcode/codetest/presentation/usermanagement/UserController.java
  • src/main/java/org/ezcode/codetest/domain/user/service/UserGithubService.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java (2)

80-84: 프로필 외 텍스트 필드 업데이트 분리 OK
profileImageUrl을 도메인 엔티티의 별도 흐름으로 분리하고, 나머지 필드만 modifyUserInfo로 갱신하는 방향 적절합니다. 도메인 메서드 시그니처 변경과 호출부가 일치하는지 정도만 함께 확인하면 충분합니다.


76-76: 컨트롤러 @PutMappingconsumes 속성 추가 필요
서비스 레이어 시그니처 변경은 적절하며, 컨트롤러에서 이미 @RequestPart로 바인딩된 입력을 처리하려면 HTTP 요청이 multipart/form-data로 들어오도록 consumes를 명시해야 합니다.

– 수정 대상
• 파일: src/main/java/org/ezcode/codetest/presentation/usermanagement/UserController.java
• 메서드: modifyUserInfo (라인 52–59)

– 변경 예시

-    @PutMapping("/users")
+    @PutMapping(
+        value = "/users",
+        consumes = MediaType.MULTIPART_FORM_DATA_VALUE
+    )
     public ResponseEntity<UserInfoResponse> modifyUserInfo(
         @AuthenticationPrincipal AuthUser authUser,
         @Valid @RequestPart("request") ModifyUserInfoRequest request,
         @RequestPart(value = "image", required = false) MultipartFile image
     ) {
         return ResponseEntity.status(HttpStatus.OK)
             .body(userService.modifyUserInfo(authUser, request, image));
     }

mandatory_refactors_required

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/user-infos

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 3

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/org/ezcode/codetest/application/usermanagement/user/service/UserService.java (2)

153-160: 업로드 전 파일 검증(확장자/콘텐츠타입/용량) 추가 권장

멀티파트 업로드는 악성 파일 업로드 리스크가 큽니다. 이미지 MIME/확장자 화이트리스트와 용량 제한을 적용하세요. 검증 실패 시 S3 업로드 전에 예외를 던지는 것이 안전합니다.

예시:

 private String uploadProfileImage(MultipartFile image) {
-  try {
+  try {
+    // 간단 검증 예시: 10MB 이하, 이미지 MIME만 허용
+    final long MAX_SIZE = 10L * 1024 * 1024;
+    if (image.getSize() <= 0 || image.getSize() > MAX_SIZE) {
+      throw new S3Exception(S3ExceptionCode.S3_UPLOAD_FAILED);
+    }
+    String contentType = image.getContentType();
+    if (contentType == null || !contentType.startsWith("image/")) {
+      throw new S3Exception(S3ExceptionCode.S3_UPLOAD_FAILED);
+    }
     return s3Uploader.upload(image, S3Directory.PROFILE.getDir());
   } catch (Exception e) {
-    log.error("프로필 이미지 업로드 실패 - image {}", image, e);
+    log.error("프로필 이미지 업로드 실패 - filename: {}, size: {}",
+        image.getOriginalFilename(), image.getSize(), e);
     throw new S3Exception(S3ExceptionCode.S3_UPLOAD_FAILED);
   }
 }

162-179: 삭제 실패 시 응답과 실제 상태 불일치 (클라이언트 오도 가능성)

현재 코드는 S3 삭제가 실패하면 DB의 profileImageUrl도 유지되는데, 응답은 항상 null을 반환합니다. 실제 상태와 응답 불일치로 클라이언트가 삭제된 것으로 오인할 수 있습니다.

DB 포인터는 항상 null로 변경하고, S3 삭제는 베스트에포트로 처리하며, 응답은 실제 DB 값을 반영하도록 변경을 권장합니다.

 @Transactional
 public UserProfileImageResponse deleteUserProfileImage(AuthUser authUser) {
   User user = userDomainService.getUserById(authUser.getId());

   String oldImageUrl = user.getProfileImageUrl();

   // S3에서 기존 이미지 파일 삭제
-  if (oldImageUrl != null) {
-    try {
-      s3Uploader.delete(oldImageUrl, "profile");
-      user.modifyProfileImage(null);
-    } catch (Exception e) {
-      log.warn("프로필 이미지 삭제 실패 - url: {}", oldImageUrl, e);
-    }
-  }
-
-  return new UserProfileImageResponse(null);
+  if (oldImageUrl != null) {
+    // 1) 먼저 DB 포인터 제거
+    user.modifyProfileImage(null);
+    // 2) S3 삭제는 실패해도 서비스는 계속 (로그만 남김)
+    try {
+      s3Uploader.delete(oldImageUrl, S3Directory.PROFILE.getDir());
+    } catch (Exception e) {
+      log.warn("프로필 이미지 삭제 실패 - url: {}", oldImageUrl, e);
+    }
+  }
+  // 실제 DB 상태를 응답에 반영
+  return new UserProfileImageResponse(user.getProfileImageUrl());
 }
🧹 Nitpick comments (1)
src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java (1)

156-160: 문자열 입력값 trim 권장 (불필요 공백 저장 방지)

외부 입력에서 앞뒤 공백이 포함될 수 있어 저장 전에 trim하면 데이터 일관성이 좋아집니다.

다음과 같이 적용을 권장합니다.

- this.nickname = (nickname == null || nickname.isBlank()) ? this.nickname : nickname;
- this.githubUrl = (githubUrl == null || githubUrl.isBlank()) ? this.githubUrl : githubUrl;
- this.blogUrl = (blogUrl == null || blogUrl.isBlank()) ? this.blogUrl : blogUrl;
- this.introduction = (introduction == null || introduction.isBlank()) ? this.introduction : introduction;
+ this.nickname = (nickname == null || nickname.isBlank()) ? this.nickname : nickname.trim();
+ this.githubUrl = (githubUrl == null || githubUrl.isBlank()) ? this.githubUrl : githubUrl.trim();
+ this.blogUrl = (blogUrl == null || blogUrl.isBlank()) ? this.blogUrl : blogUrl.trim();
+ this.introduction = (introduction == null || introduction.isBlank()) ? this.introduction : introduction.trim();
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between d712e78 and 0a98011.

📒 Files selected for processing (4)
  • src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/request/ModifyUserInfoRequest.java (0 hunks)
  • src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java (1 hunks)
  • src/main/java/org/ezcode/codetest/presentation/usermanagement/UserController.java (1 hunks)
💤 Files with no reviewable changes (1)
  • src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/request/ModifyUserInfoRequest.java
🔇 Additional comments (2)
src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java (1)

155-161: 프로필 이미지 갱신 책임을 엔티티에서 분리한 점 좋습니다

profileImageUrl을 modifyUserInfo에서 제거하고, 별도 메서드로 이미지 변경을 위임한 방향이 명확하고 유지보수에도 유리합니다. 문자열 필드들에 isBlank 체크로 기존 값 보존 로직도 자연스럽습니다.

src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java (1)

75-107: 유저 정보 수정 로직과 이미지 갱신 통합 방향 좋습니다

  • 엔티티의 modifyUserInfo 시그니처 변경과 잘 맞물리며, 멀티파트로 이미지가 올 경우에만 이미지 처리하는 분기 또한 자연스럽습니다.
  • 전체 응답(UserInfoResponse)도 최신 엔티티 상태를 반영합니다.

Comment on lines 86 to 93
if (image != null && !image.isEmpty()) {
if (user.getProfileImageUrl()!=null) {
s3Uploader.delete(user.getProfileImageUrl(), "profile");
}
String profileImageUrl = uploadProfileImage(image);

user.modifyProfileImage(profileImageUrl);
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

S3 삭제 시 디렉터리 상수 사용 및 업로드-삭제 순서 개선 제안

  • 하드코딩된 "profile" 대신 S3Directory.PROFILE.getDir()을 사용해 일관성을 유지하세요.
  • 안전한 교체 순서: 새 이미지 업로드 성공 후에 기존 이미지를 삭제하면 실패 시 사용자 이미지가 사라지는 리스크를 줄일 수 있습니다.

다음과 같이 수정 제안 드립니다.

- if (image != null && !image.isEmpty()) {
-   if (user.getProfileImageUrl()!=null) {
-     s3Uploader.delete(user.getProfileImageUrl(), "profile");
-   }
-   String profileImageUrl = uploadProfileImage(image);
-   user.modifyProfileImage(profileImageUrl);
- }
+ if (image != null && !image.isEmpty()) {
+   // 1) 새 이미지 업로드
+   String profileImageUrl = uploadProfileImage(image);
+   String oldImageUrl = user.getProfileImageUrl();
+   // 2) DB 갱신
+   user.modifyProfileImage(profileImageUrl);
+   // 3) 기존 이미지 삭제(실패해도 서비스 흐름 유지)
+   if (oldImageUrl != null) {
+     try {
+       s3Uploader.delete(oldImageUrl, S3Directory.PROFILE.getDir());
+     } catch (Exception e) {
+       log.warn("기존 프로필 이미지 삭제 실패 - url: {}", oldImageUrl, e);
+     }
+   }
+ }

검증이 필요하시면, s3Uploader.delete의 시그니처와 디렉터리 인자를 다음 스크립트로 확인해 주세요.


🏁 Script executed:

#!/bin/bash
# S3Uploader.delete 시그니처 및 사용처 확인
fd -t f -g '*S3Uploader*.java' | while read -r f; do
  echo "=== $f ==="
  rg -n -A2 -B4 -g "$f" -P '\bdelete\s*\('
done

# "profile" 하드코딩 사용 여부 점검
rg -n -C2 '"profile"' --type=java

Length of output: 2630


아래 스크립트로 S3Uploader.delete 메서드 정의(시그니처)와 호출부를 확인해주세요.


🏁 Script executed:

#!/bin/bash
# 1) delete 메서드 시그니처(오버로드 포함) 확인
echo "=== delete 메서드 정의 확인 ==="
rg -nE 'public\s+\S+\s+delete\s*\(' src/main/java/org/ezcode/codetest/infrastructure/s3/S3Uploader.java -C2 || echo ">> delete 메서드 정의를 찾을 수 없습니다."

# 2) delete 호출부 확인
echo
echo "=== delete 호출부 확인 ==="
rg -n -C1 'delete\s*\(' src/main/java/org/ezcode/codetest/infrastructure/s3/S3Uploader.java

Length of output: 536


S3 삭제 리팩토링: 디렉터리 상수 사용 및 안전한 업로드-삭제 순서 적용

  • s3Uploader.delete(String fileUrl, String dirName) 시그니처 확인됨.
  • 하드코딩된 "profile" 대신 S3Directory.PROFILE.getDir() 사용으로 일관성 확보.
  • 새 이미지 업로드→DB 갱신 후 기존 이미지 삭제(try-catch) 순으로 변경해 삭제 실패 시에도 사용자 이미지를 보존.

검토 후 아래와 같이 반영해주세요.

- if (image != null && !image.isEmpty()) {
-   if (user.getProfileImageUrl() != null) {
-     s3Uploader.delete(user.getProfileImageUrl(), "profile");
-   }
-   String profileImageUrl = uploadProfileImage(image);
-   user.modifyProfileImage(profileImageUrl);
- }
+ if (image != null && !image.isEmpty()) {
+   // 1) 새 이미지 업로드
+   String profileImageUrl = uploadProfileImage(image);
+   String oldImageUrl = user.getProfileImageUrl();
+   // 2) DB에 새 URL 저장
+   user.modifyProfileImage(profileImageUrl);
+   // 3) 기존 이미지 삭제(실패해도 서비스 흐름 유지)
+   if (oldImageUrl != null) {
+     try {
+       s3Uploader.delete(oldImageUrl, S3Directory.PROFILE.getDir());
+     } catch (Exception e) {
+       log.warn("기존 프로필 이미지 삭제 실패 - url: {}", oldImageUrl, e);
+     }
+   }
+ }
📝 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.

Suggested change
if (image != null && !image.isEmpty()) {
if (user.getProfileImageUrl()!=null) {
s3Uploader.delete(user.getProfileImageUrl(), "profile");
}
String profileImageUrl = uploadProfileImage(image);
user.modifyProfileImage(profileImageUrl);
}
if (image != null && !image.isEmpty()) {
// 1) 새 이미지 업로드
String profileImageUrl = uploadProfileImage(image);
String oldImageUrl = user.getProfileImageUrl();
// 2) DB에 새 URL 저장
user.modifyProfileImage(profileImageUrl);
// 3) 기존 이미지 삭제(실패해도 서비스 흐름 유지)
if (oldImageUrl != null) {
try {
s3Uploader.delete(oldImageUrl, S3Directory.PROFILE.getDir());
} catch (Exception e) {
log.warn("기존 프로필 이미지 삭제 실패 - url: {}", oldImageUrl, e);
}
}
}
🤖 Prompt for AI Agents
In
src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java
around lines 86 to 93, replace the current logic to (1) call
uploadProfileImage(image) first and get the new profileImageUrl, (2) update the
user with user.modifyProfileImage(profileImageUrl) so the new image is preserved
even if deletion fails, and (3) if an old user.getProfileImageUrl() exists
attempt to delete it inside a try-catch using s3Uploader.delete(oldUrl,
S3Directory.PROFILE.getDir()) (matching the s3Uploader.delete(String, String)
signature) and log or ignore delete errors so they don’t roll back the update.

Comment on lines 52 to 59
@PutMapping("/users")
public ResponseEntity<UserInfoResponse> modifyUserInfo(
@AuthenticationPrincipal AuthUser authUser,
@RequestBody ModifyUserInfoRequest modifyUserInfoRequest
){
return ResponseEntity.status(HttpStatus.OK).body(userService.modifyUserInfo(authUser, modifyUserInfoRequest));
}

//유저 프로필 이미지 등록
@Operation(
summary = "프로필 이미지 등록",
description = "유저의 프로필 이미지를 등록합니다. 기존의 이미지가 있는 경우, 기존 이미지가 삭제되고 새로운 이미지로 교체됩니다.")
@PutMapping("/users/profile")
public ResponseEntity<UserProfileImageResponse> uploadUserProfileImage(
@AuthenticationPrincipal AuthUser authUser,
@RequestPart("request") ModifyUserInfoRequest request,
@RequestPart(value = "image", required = false) MultipartFile image
){
return ResponseEntity.status(HttpStatus.OK).body(userService.uploadUserProfileImage(authUser, image));
return ResponseEntity.status(HttpStatus.OK).body(userService.modifyUserInfo(authUser, request, image));
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

❓ Verification inconclusive

컨텐츠 타입 매핑 명시 및 JSON/Multipart 동시 지원 제안

현재 시그니처는 멀티파트만 처리 가능하게 보입니다. 기존 클라이언트가 application/json으로 전송하면 415가 날 수 있습니다. 소비 타입을 명시하고, JSON 전용 오버로드를 추가하면 하위 호환을 유지할 수 있습니다.

멀티파트 메서드에 consumes를 명시:

- @PutMapping("/users")
+ @PutMapping(value = "/users", consumes = org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE)
 public ResponseEntity<UserInfoResponse> modifyUserInfo(
   @AuthenticationPrincipal AuthUser authUser,
-  @RequestPart("request") ModifyUserInfoRequest request,
+  @Valid @RequestPart("request") ModifyUserInfoRequest request,
   @RequestPart(value = "image", required = false) MultipartFile image
 ){
   return ResponseEntity.status(HttpStatus.OK).body(userService.modifyUserInfo(authUser, request, image));
 }

JSON 전용 오버로드 추가(동일 경로, 다른 consumes):

@PutMapping(value = "/users", consumes = org.springframework.http.MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<UserInfoResponse> modifyUserInfoJson(
    @AuthenticationPrincipal AuthUser authUser,
    @Valid @RequestBody ModifyUserInfoRequest request
) {
    return ResponseEntity.status(HttpStatus.OK).body(userService.modifyUserInfo(authUser, request, null));
}

구현 후 기존 프런트가 JSON 또는 멀티파트 모두 동작하는지 확인을 권장합니다.

다음 쿼리로 기존 @RequestBody ModifyUserInfoRequest 사용 흔적이 남아있는지 확인해 주세요.


🏁 Script executed:

#!/bin/bash
rg -nP '@RequestBody\s+ModifyUserInfoRequest' --type=java -C2

Length of output: 63


멀티파트 및 JSON 요청 동시 지원을 위한 consumes 명시 및 오버로드 추가 제안

아래와 같이 Multipart 전용 핸들러에 consumes 를 명시하고, JSON 전용 오버로드를 추가해 하위 호환을 유지하세요.
스크립트 검증 결과(rg -nP '@RequestBody\s+ModifyUserInfoRequest' --type=java) JSON 전송 전용 핸들러는 아직 존재하지 않습니다.

  • 파일: src/main/java/org/ezcode/codetest/presentation/usermanagement/UserController.java
  • 변경 대상 라인: 52–59
- @PutMapping("/users")
+ @PutMapping(
+   value = "/users",
+   consumes = org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE
+ )
public ResponseEntity<UserInfoResponse> modifyUserInfo(
    @AuthenticationPrincipal AuthUser authUser,
-   @RequestPart("request") ModifyUserInfoRequest request,
+   @Valid @RequestPart("request") ModifyUserInfoRequest request,
    @RequestPart(value = "image", required = false) MultipartFile image
) {
    return ResponseEntity.status(HttpStatus.OK)
        .body(userService.modifyUserInfo(authUser, request, image));
}
@PutMapping(
  value = "/users",
  consumes = org.springframework.http.MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<UserInfoResponse> modifyUserInfoJson(
    @AuthenticationPrincipal AuthUser authUser,
    @Valid @RequestBody ModifyUserInfoRequest request
) {
    return ResponseEntity.status(HttpStatus.OK)
        .body(userService.modifyUserInfo(authUser, request, null));
}

구현 후 기존 프런트엔드에서 JSON 및 Multipart 요청 모두 정상 처리되는지 꼭 확인해 주세요.

🤖 Prompt for AI Agents
In
src/main/java/org/ezcode/codetest/presentation/usermanagement/UserController.java
around lines 52–59, the current single @PutMapping handler accepts multipart but
does not declare consumes and there is no JSON-only overload; modify the
existing multipart handler to declare consumes =
MediaType.MULTIPART_FORM_DATA_VALUE and then add a new JSON-only overloaded
handler annotated with @PutMapping(value = "/users", consumes =
MediaType.APPLICATION_JSON_VALUE) that accepts @Valid @RequestBody
ModifyUserInfoRequest and calls userService.modifyUserInfo(authUser, request,
null); ensure annotations (@AuthenticationPrincipal, @Valid) match and both
endpoints return the same ResponseEntity<UserInfoResponse> so both multipart and
JSON clients work.

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: 0

🧹 Nitpick comments (5)
src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/UserGitubAutoPushResponse.java (3)

15-18: repo/branch 필드 추가는 방향성 OK. 다만 null 응답 처리와 스웨거 명세 보완 권장

  • 사용자 설정 전(레포/브랜치 미선택)에는 repo/branch가 null일 가능성이 높습니다. 현재 구조라면 null이 그대로 직렬화될 수 있으므로, 응답 일관성을 위해 아래 중 하나를 권장합니다.
    • Jackson에서 null 필드 제외: 클래스 레벨에 @JsonInclude(Include.NON_NULL) 추가
    • 스웨거 명세에 nullable 표시: 필드 @Schema(nullable = true) 지정

추가 예시(클래스 상단/필드에 적용):

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
@JsonInclude(Include.NON_NULL)
@Schema(description = "깃허브 자동 Push 여부 변경 후 응답")
public class UserGitubAutoPushResponse {
    @Schema(description = "메세지", example = "변경되었습니다")
    private final String message;

    @Schema(description = "현재 상태", example = "true")
    private final boolean gitPushStatus;

    @Schema(description = "선택된 레포", example = "myRepo", nullable = true)
    private final String githubRepoName;

    @Schema(description = "선택된 브랜치", example = "dev", nullable = true)
    private final String branch;
}

15-18: 생성자 파라미터 증가(4개)로 인한 실수 방지: @builder 도입 고려

현재 시그니처가 (String, boolean, String, String)으로 오버로드/순서 실수 여지가 있습니다. Lombok @Builder를 추가하면 호출부 가독성과 안전성이 좋아집니다.

호출부 변경은 서비스 코드 코멘트에 diff로 제안해두었습니다. DTO에는 아래와 같이 @Builder만 추가하면 됩니다:

import lombok.Builder;

@Getter
@AllArgsConstructor
@Builder
public class UserGitubAutoPushResponse {
  // ...
}

15-18: 네이밍 오타 정정: UserGitubAutoPushResponseUserGithubAutoPushResponse
현재 서비스·컨트롤러에서는 Github로 통일되어 있으나, 해당 DTO 클래스명만 ‘Gitub’ 오타가 있어 혼란을 초래합니다. 클래스/파일명과 전역 참조를 아래와 같이 일괄 수정해주세요.

  • DTO 클래스명/파일명 변경
    • src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/UserGitubAutoPushResponse.java
      → UserGithubAutoPushResponse.java (클래스명도 public class UserGithubAutoPushResponse로 수정)
  • UserGithubController 수정 (import 및 메소드 시그니처)
    • import org.ezcode.codetest.application.usermanagement.user.dto.response.UserGitubAutoPushResponse;
      → UserGithubAutoPushResponse
    • public ResponseEntity … → ResponseEntity
  • UserGithubService 수정 (import, 반환 타입, 생성자 호출)
    • import org.ezcode.codetest.application.usermanagement.user.dto.response.UserGitubAutoPushResponse;
      → UserGithubAutoPushResponse
    • public UserGitubAutoPushResponse changeAutoPushSetting(…) → public UserGithubAutoPushResponse changeAutoPushSetting(…)
    • return new UserGitubAutoPushResponse(…) → new UserGithubAutoPushResponse(…)

API 스펙에는 영향이 없으므로 서버 내부 호환성 정리를 위해 여유가 있을 때 적용을 권장드립니다.

src/main/java/org/ezcode/codetest/domain/user/service/UserGithubService.java (2)

127-127: 호출부 업데이트 OK. repo/branch 미설정(null) 시 응답 계약만 명확히

현재 선택된 레포/브랜치가 없는 사용자의 경우 getRepo()/getBranch()가 null일 수 있습니다. DTO 쪽에서 @JsonInclude(NON_NULL) 또는 @Schema(nullable = true)로 명세/직렬화를 정리하면 충분합니다.

또한, DTO에 @Builder를 도입한다면 가독성과 안전성을 위해 아래와 같이 호출부를 변경하는 것을 추천합니다.

아래 diff는 @builder 도입 시 적용 예시입니다.

-        return new UserGitubAutoPushResponse("변경되었습니다", user.getGitPushStatus(), userGithubInfo.getRepo(), userGithubInfo.getBranch());
+        return UserGitubAutoPushResponse.builder()
+            .message("변경되었습니다")
+            .gitPushStatus(user.getGitPushStatus())
+            .githubRepoName(userGithubInfo.getRepo())
+            .branch(userGithubInfo.getBranch())
+            .build();

136-136: 자동 푸시 상태 조회 응답도 동일 패턴으로 통일 권장

위와 동일하게 DTO의 빌더를 사용하면 인자 순서 실수를 줄이고 필드 매핑이 명확해집니다. 또한 null 처리 전략(필드 생략/nullable 표시)도 DTO 레벨에서 일괄 적용하는 것이 유지보수에 유리합니다.

빌더 도입 시 예시:

-        return new UserGitubAutoPushResponse("현재 githubAutoPush 설정", user.getGitPushStatus(),userGithubInfo.getRepo(), userGithubInfo.getBranch());
+        return UserGitubAutoPushResponse.builder()
+            .message("현재 githubAutoPush 설정")
+            .gitPushStatus(user.getGitPushStatus())
+            .githubRepoName(userGithubInfo.getRepo())
+            .branch(userGithubInfo.getBranch())
+            .build();
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 0a98011 and 0ab6727.

📒 Files selected for processing (2)
  • src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/UserGitubAutoPushResponse.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/user/service/UserGithubService.java (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (1)
src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/UserGitubAutoPushResponse.java (1)

15-18: 2-인자 생성자 호출 없음 확인
UserGitubAutoPushResponse에 대한 2-인자 호출은 발견되지 않았으며, 모든 호출부가 새로 추가된 4-인자 생성자를 사용하고 있습니다. 추가 검증이나 수정 없이 머지해도 안전합니다.

@minjee2758 minjee2758 self-assigned this Aug 19, 2025
@minjee2758 minjee2758 merged commit d16d016 into dev Aug 19, 2025
2 checks passed
@minjee2758 minjee2758 deleted the refactor/user-infos branch August 19, 2025 12:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants