Skip to content

Conversation

@eraser502
Copy link
Contributor

Overview

  • 실서버 배포

Related Issue

  • Closes

PR Checklist

  • [ ]

Additional Information

@coderabbitai
Copy link

coderabbitai bot commented Dec 14, 2025

Walkthrough

이 PR은 세 가지 주요 변경사항을 도입합니다: (1) Jakarta Bean Validation 애노테이션(@Valid, @NotNull)을 다양한 컨트롤러 문서 및 서비스에 추가하여 요청 검증을 강화; (2) 새로운 ImageUrlResolver 유틸리티 클래스를 도입하여 저장된 경로, CDN 공개 URL, S3 키 간의 이미지 URL 변환을 관리; (3) 파일 업로드 엔드포인트의 @PostMapping 애노테이션을 업데이트하여 multipart/form-data 콘텐츠 타입을 명시적으로 지정합니다. 관련 서비스, DTO, S3Manager 클래스들이 새로운 ImageUrlResolver를 활용하도록 수정되었습니다.

Sequence Diagram(s)

sequenceDiagram
    participant Client as 클라이언트
    participant Controller as 컨트롤러
    participant Service as 서비스
    participant Resolver as ImageUrlResolver
    participant Storage as ImageStorageService
    participant S3 as S3Manager
    participant DB as 데이터베이스
    
    Client->>Controller: 프로필 이미지 업로드<br/>(multipart/form-data)
    Controller->>Service: uploadProfileImage()
    
    rect rgb(200, 220, 255)
    Note over Service,Resolver: 새 이미지 경로 해석
    Service->>Resolver: toStoredPath(newImageUrl)
    Resolver-->>Service: 변환된 저장 경로
    end
    
    rect rgb(255, 220, 200)
    Note over Service,Storage: 기존 이미지 삭제 (필요시)
    Service->>Service: deleteOldProfileImageUrlIfChanged()
    Service->>Resolver: toS3Key(oldImageUrl)
    Resolver-->>Service: S3 키
    Service->>Storage: deleteImage(S3 키)
    Storage->>S3: deleteObject()
    S3-->>Storage: 삭제 완료
    end
    
    rect rgb(220, 255, 220)
    Note over Service,DB: 프로필 정보 업데이트
    Service->>DB: 새 이미지 경로로 엔티티 업데이트
    DB-->>Service: 업데이트 완료
    end
    
    rect rgb(255, 240, 200)
    Note over Service,Resolver: 응답 생성 (CDN URL 포함)
    Service->>Resolver: toPublicUrl(storedPath)
    Resolver-->>Service: 공개 CDN URL
    Service-->>Controller: 응답 (CDN URL 포함)
    Controller-->>Client: HTTP 200 + 응답
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

주의가 필요한 영역:

  • ImageUrlResolver 유틸리티 클래스: 새로운 추상화로, CDN 베이스 URL 처리, 경로 정규화, 외부 URL 감지 로직이 포함되어 있으므로 면밀한 검토 필요
  • S3Manager 및 ImageStorageService 변경: S3 키 처리 로직이 변경되었으며, 기존 흐름과의 호환성 확인 필요
  • 다중 서비스의 ImageUrlResolver 통합: AdminService, CMSMemberService, SiteSettingService, RedotAppService 등 여러 서비스에서 일관되게 ImageUrlResolver가 적용되었는지 확인
  • DTO 팩토리 메서드 시그니처 변경: AdminResponse.from(), CMSMemberResponse.fromEntity(), RedotMemberResponse.fromEntity(), SiteSettingResponse.fromEntity() 등 여러 팩토리 메서드의 시그니처가 변경되어 모든 호출 지점이 올바르게 업데이트되었는지 확인
  • 검증 애노테이션 일관성: 30개 이상의 파일에서 @Valid/@NotNull이 추가되었으므로, 모든 엔드포인트에서 검증 애노테이션이 일관되게 적용되었는지 확인

Possibly related PRs

  • Feature / s3 / image path #128: 동일한 ImageUrlResolver, AdminResponse.from(...) 등 이미지 URL 처리 코드를 수정하므로 직접적으로 관련됨
  • Deploy / Merge to Main #115: 동일한 컨트롤러 및 컨트롤러 문서 API(AdminController/AdminControllerDocs 등)를 수정하므로 코드 레벨에서 관련됨
  • Feature / site setting to style info #61: SiteSettingResponse.fromEntity() 팩토리 메서드의 파라미터/시그니처를 변경하므로 직접적으로 관련됨

Suggested labels

feature, validation, image-handling, refactoring

Poem

🐰 이미지 경로는 혼란스러웠지만,
ImageUrlResolver가 질서를 가져왔네!
CDN 베이스와 S3 키를 다루며,
검증은 곳곳에 스며들고,
업로드 엔드포인트는 명확해졌으니,
우리의 수정은 우아하고 깔끔하도다! ✨

Pre-merge checks and finishing touches

❌ Failed checks (3 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning PR 제목이 변경 사항의 실제 내용을 반영하지 못하고 있습니다. 'Deploy / Merge to Main'은 너무 일반적이며, 실제로는 이미지 URL 처리(ImageUrlResolver), 유효성 검사 어노테이션(@Valid, @NotNull), 멀티파트 폼 데이터 처리 등 광범위한 기능 개선이 포함되어 있습니다. PR 제목을 구체적으로 변경하여 실제 주요 변경 사항을 반영하도록 하세요. 예: 'Add ImageUrlResolver and validation annotations for image URL handling'
Description check ⚠️ Warning PR 설명이 템플릿 구조를 따르고 있지만, 실질적인 내용이 거의 없습니다. Overview는 '실서버 배포'라는 한 줄이고, Related Issue와 Additional Information은 작성되지 않아 검토자가 변경 사항을 이해하기 어렵습니다. PR 설명을 상세히 작성하세요. ImageUrlResolver 추가, 유효성 검사 어노테이션 적용, 멀티파트 폼 데이터 처리 등 주요 변경 사항과 이유, 관련 이슈를 명시하고 검토 시 주의할 사항을 추가하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 5.10% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✨ 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 develop

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

🧹 Nitpick comments (4)
src/main/java/redot/redot_server/domain/cms/site/style/controller/docs/StyleInfoControllerDocs.java (1)

27-31: 검증 실패 시 400 응답 문서화를 고려하세요.

@Valid 애노테이션을 사용하면 검증 실패 시 자동으로 400 Bad Request 응답이 반환됩니다. API 문서의 완성도를 높이기 위해 @ApiResponse를 추가하는 것을 고려해보세요.

예시:

@ApiResponse(responseCode = "400", description = "잘못된 요청 (검증 실패)")
src/main/java/redot/redot_server/domain/auth/controller/docs/RedotMemberAuthControllerDocs.java (1)

55-58: 소셜 로그인 URL 조회 메소드의 파라미터 검증을 고려해 보세요.

getSocialLoginUrl 메소드의 String 파라미터들(provider, redirectUri, failureUri)에도 검증 애노테이션을 추가하면 더 견고한 API가 됩니다.

예를 들어:

  • provider: @NotBlank 추가
  • redirectUri, failureUri: @NotBlank@Pattern (URL 형식 검증) 추가

적용 예시:

 ResponseEntity<SocialLoginUrlResponse> getSocialLoginUrl(
-        @Parameter(description = "소셜 로그인 제공자", example = "google") String provider,
-        @Parameter(description = "인가 완료 후 리다이렉트될 URI", example = "https://app.redot.com/auth/callback") String redirectUri,
-        @Parameter(description = "실패 시 리다이렉트될 URI", example = "https://app.redot.com/auth/fail") String failureUri);
+        @Parameter(description = "소셜 로그인 제공자", example = "google") @NotBlank String provider,
+        @Parameter(description = "인가 완료 후 리다이렉트될 URI", example = "https://app.redot.com/auth/callback") @NotBlank String redirectUri,
+        @Parameter(description = "실패 시 리다이렉트될 URI", example = "https://app.redot.com/auth/fail") @NotBlank String failureUri);

필요한 경우 다음 import도 추가하세요:

import jakarta.validation.constraints.NotBlank;
src/main/java/redot/redot_server/global/s3/service/ImageStorageService.java (1)

34-40: 삭제 로직 개선: 방어적 검사 추가됨

Line 36의 s3Manager.exists(key) 검사는 존재하지 않는 파일 삭제를 시도하여 발생하는 오류를 방지합니다. 이는 추가적인 S3 API 호출 비용이 발생하지만, 견고성을 위한 합리적인 트레이드오프입니다.

빈 키 검사(!StringUtils.hasText(key))와 함께 사용되어 안전한 삭제 작업을 보장합니다.

성능이 중요한 경우, S3 삭제 작업이 존재하지 않는 키에 대해 idempotent하다면 exists() 검사를 제거하고 삭제 실패를 무시하도록 고려할 수 있습니다:

 public void delete(String imageUrl) throws ImageUploadException {
     String key = imageUrlResolver.toS3Key(imageUrl);
-    if (!StringUtils.hasText(key) || !s3Manager.exists(key)) {
+    if (!StringUtils.hasText(key)) {
         return;
     }
-    s3Manager.deleteFile(key);
+    try {
+        s3Manager.deleteFile(key);
+    } catch (Exception e) {
+        // Log and ignore if file doesn't exist
+    }
 }
src/main/java/redot/redot_server/global/s3/util/ImageUrlResolver.java (1)

78-80: isExternalUrl 메서드의 URL 스킴 범위를 확인하세요.

현재는 http://https://만 외부 URL로 인식합니다. 이미지 URL 용도로는 충분하지만, 향후 다른 스킴(예: data:, blob:)을 지원해야 할 가능성이 있다면 고려해주세요.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between df9838b and 4878dc3.

📒 Files selected for processing (37)
  • src/main/java/redot/redot_server/domain/admin/controller/AdminController.java (2 hunks)
  • src/main/java/redot/redot_server/domain/admin/controller/docs/AdminConsultationControllerDocs.java (2 hunks)
  • src/main/java/redot/redot_server/domain/admin/controller/docs/AdminControllerDocs.java (4 hunks)
  • src/main/java/redot/redot_server/domain/admin/controller/docs/AdminRedotAppControllerDocs.java (2 hunks)
  • src/main/java/redot/redot_server/domain/admin/dto/response/AdminResponse.java (2 hunks)
  • src/main/java/redot/redot_server/domain/admin/service/AdminService.java (5 hunks)
  • src/main/java/redot/redot_server/domain/auth/controller/docs/AdminAuthControllerDocs.java (3 hunks)
  • src/main/java/redot/redot_server/domain/auth/controller/docs/AdminImpersonationControllerDocs.java (2 hunks)
  • src/main/java/redot/redot_server/domain/auth/controller/docs/CMSAuthControllerDocs.java (3 hunks)
  • src/main/java/redot/redot_server/domain/auth/controller/docs/EmailVerificationControllerDocs.java (2 hunks)
  • src/main/java/redot/redot_server/domain/auth/controller/docs/RedotMemberAuthControllerDocs.java (3 hunks)
  • src/main/java/redot/redot_server/domain/auth/service/AdminAuthService.java (3 hunks)
  • src/main/java/redot/redot_server/domain/auth/service/CMSAuthService.java (3 hunks)
  • src/main/java/redot/redot_server/domain/auth/service/RedotMemberAuthService.java (4 hunks)
  • src/main/java/redot/redot_server/domain/cms/inquiry/controller/docs/RedotAppInquiryControllerDocs.java (2 hunks)
  • src/main/java/redot/redot_server/domain/cms/member/controller/CMSMemberController.java (2 hunks)
  • src/main/java/redot/redot_server/domain/cms/member/controller/docs/CMSMemberControllerDocs.java (5 hunks)
  • src/main/java/redot/redot_server/domain/cms/member/dto/response/CMSMemberResponse.java (2 hunks)
  • src/main/java/redot/redot_server/domain/cms/member/service/CMSMemberService.java (5 hunks)
  • src/main/java/redot/redot_server/domain/cms/site/page/controller/docs/CMSSitePageControllerDocs.java (2 hunks)
  • src/main/java/redot/redot_server/domain/cms/site/setting/controller/SiteSettingController.java (2 hunks)
  • src/main/java/redot/redot_server/domain/cms/site/setting/controller/docs/SiteSettingControllerDocs.java (2 hunks)
  • src/main/java/redot/redot_server/domain/cms/site/setting/dto/response/SiteSettingResponse.java (2 hunks)
  • src/main/java/redot/redot_server/domain/cms/site/setting/service/SiteSettingService.java (4 hunks)
  • src/main/java/redot/redot_server/domain/cms/site/style/controller/docs/StyleInfoControllerDocs.java (2 hunks)
  • src/main/java/redot/redot_server/domain/redot/app/controller/docs/RedotAppControllerDocs.java (2 hunks)
  • src/main/java/redot/redot_server/domain/redot/app/service/RedotAppCreationService.java (3 hunks)
  • src/main/java/redot/redot_server/domain/redot/app/service/RedotAppService.java (4 hunks)
  • src/main/java/redot/redot_server/domain/redot/consultation/controller/docs/ConsultationControllerDocs.java (2 hunks)
  • src/main/java/redot/redot_server/domain/redot/member/controller/RedotMemberController.java (2 hunks)
  • src/main/java/redot/redot_server/domain/redot/member/controller/docs/RedotMemberControllerDocs.java (2 hunks)
  • src/main/java/redot/redot_server/domain/redot/member/dto/response/RedotMemberResponse.java (2 hunks)
  • src/main/java/redot/redot_server/domain/redot/member/service/RedotMemberService.java (3 hunks)
  • src/main/java/redot/redot_server/domain/site/domain/controller/docs/DomainControllerDocs.java (2 hunks)
  • src/main/java/redot/redot_server/global/s3/service/ImageStorageService.java (2 hunks)
  • src/main/java/redot/redot_server/global/s3/util/ImageUrlResolver.java (1 hunks)
  • src/main/java/redot/redot_server/global/s3/util/S3Manager.java (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/redot/redot_server/global/s3/service/ImageStorageService.java (1)
src/main/java/redot/redot_server/global/s3/util/ImagePathGenerator.java (1)
  • ImagePathGenerator (8-35)
🔇 Additional comments (55)
src/main/java/redot/redot_server/domain/admin/controller/docs/AdminConsultationControllerDocs.java (2)

9-9: @Valid import 추가는 적절합니다.
컨트롤러 전반에 검증을 확장하려는 의도와 일관됩니다.


36-37: Docs 인터페이스에 붙인 @Valid가 “실제 검증”까지 보장되는지 확인 필요.
Spring MVC가 구현 컨트롤러 핸들러 메서드에서 인터페이스의 파라미터 애노테이션을 항상 반영하는지는 케이스가 갈릴 수 있어, 구현 메서드에도 @Valid(및 필요 시 클래스/메서드 레벨 @Validated)가 적용돼 있는지와 ConsultationUpdateRequest에 실제 제약 애노테이션이 존재하는지 확인 부탁드립니다.

src/main/java/redot/redot_server/domain/site/domain/controller/docs/DomainControllerDocs.java (1)

8-8: Bean Validation이 올바르게 구현되었습니다.

구현 컨트롤러(DomainController)의 메서드 파라미터에 @Valid 애노테이션이 올바르게 적용되어 있으며, SubdomainLookupRequest DTO에는 @NotBlank 제약조건이 정의되어 있습니다. 인터페이스와 구현 클래스 모두에서 검증이 일관되게 설정되어 있어 런타임 검증이 정상적으로 동작할 것입니다.

src/main/java/redot/redot_server/domain/admin/controller/docs/AdminRedotAppControllerDocs.java (2)

8-8: LGTM! 검증 지원을 위한 임포트 추가

Jakarta Bean Validation을 활성화하기 위해 @Valid 임포트가 올바르게 추가되었습니다.


19-19: LGTM! 요청 검증 강화

@Valid 애노테이션을 통해 RedotAppCreateRequest에 대한 빈 검증이 활성화되었습니다. 표준 Jakarta Bean Validation 패턴을 올바르게 따르고 있습니다.

src/main/java/redot/redot_server/domain/redot/app/controller/docs/RedotAppControllerDocs.java (3)

10-10: LGTM! 검증 지원 임포트 추가

Jakarta Bean Validation을 위한 @Valid 임포트가 올바르게 추가되었습니다.


40-41: LGTM! 앱 생성 요청 검증 추가

createRedotApp 메서드의 요청 파라미터에 @Valid 애노테이션이 추가되어 빈 검증이 활성화되었습니다. 표준 패턴을 올바르게 따르고 있습니다.


46-47: LGTM! 매니저 생성 요청 검증 추가

createManager 메서드의 요청 파라미터에도 @Valid 애노테이션이 추가되어 일관된 검증 패턴이 적용되었습니다.

src/main/java/redot/redot_server/domain/redot/consultation/controller/docs/ConsultationControllerDocs.java (2)

8-8: LGTM: 검증 애노테이션 import가 올바르게 추가되었습니다.

Jakarta Bean Validation의 @Valid import가 적절하게 추가되어 요청 검증을 지원합니다.


19-19: @Valid 애노테이션이 올바르게 적용되었습니다.

  • ConsultationCreateRequest에는 @NotBlank, @Email, @Size, @NotNull 등의 검증 애노테이션이 완벽하게 정의되어 있습니다.
  • 실제 컨트롤러 구현(ConsultationController)에도 @Valid 애노테이션이 이미 적용되어 있습니다.
  • 문서화 인터페이스에 @Valid를 추가하여 API 계약을 명확히 하는 것은 모범 사례입니다.
src/main/java/redot/redot_server/domain/auth/controller/docs/CMSAuthControllerDocs.java (1)

11-11: 인증 엔드포인트 검증 강화 승인

요청에 @Valid 애노테이션을 추가한 것은 입력 검증을 강화하는 우수한 구현입니다. 특히 로그인 및 비밀번호 재설정과 같은 보안에 민감한 엔드포인트에 적합합니다.

검증 인프라가 이미 적절히 구성되어 있음을 확인했습니다:

  • SignInRequestPasswordResetConfirmRequest DTO에 @NotBlank, @Email 등의 필요한 검증 제약조건이 정의되어 있습니다.
  • GlobalExceptionHandler에서 @ExceptionHandler(MethodArgumentNotValidException.class)로 검증 실패 시 통일된 에러 응답(INVALID_INPUT_VALUE)을 반환하고 있습니다.
src/main/java/redot/redot_server/domain/auth/controller/docs/EmailVerificationControllerDocs.java (1)

8-8: 요청 검증이 올바르게 구현되었습니다.

@Valid 애노테이션 추가를 통해 이메일 인증 요청에 대한 검증을 활성화한 것은 좋은 변경사항입니다.

확인 결과:

  • EmailVerificationSendRequestEmailVerificationVerifyRequest 모두 @Email, @NotBlank, @NotNull 등의 검증 제약사항이 정의되어 있습니다.
  • GlobalExceptionHandler에서 MethodArgumentNotValidExceptionBindException을 처리하는 예외 핸들러가 구현되어 있습니다.
  • spring-boot-starter-validation 의존성이 build.gradle에 포함되어 있습니다.

모든 필수 구성 요소가 정상적으로 갖춰져 있으므로 검증 기능이 예상대로 작동할 것입니다.

src/main/java/redot/redot_server/domain/auth/controller/docs/AdminAuthControllerDocs.java (1)

10-10: 입력 검증 강화 구현 완료 확인됨

요청 DTO에 @Valid 애노테이션을 추가하여 입력 검증을 강화한 변경사항이 완전하게 구현되었습니다:

  1. ✓ 실제 컨트롤러 구현체(AdminAuthController)의 모든 세 메서드(signIn, createAdmin, confirmPasswordReset)에 @Valid 애노테이션이 적용됨
  2. ✓ 모든 요청 DTO에 적절한 검증 제약조건 정의됨:
    • SignInRequest: @NotBlank, @Email
    • AdminCreateRequest: @NotBlank, @Email
    • PasswordResetConfirmRequest: @NotBlank, @Email
  3. ✓ 글로벌 예외 핸들러(GlobalExceptionHandler)에서 MethodArgumentNotValidExceptionBindException 처리 설정 완료

실서버 배포에 적합한 상태입니다.

src/main/java/redot/redot_server/domain/redot/member/controller/docs/RedotMemberControllerDocs.java (1)

9-9: 좋습니다!

@NotNull 애노테이션을 사용하기 위한 import 추가가 적절합니다.

src/main/java/redot/redot_server/domain/cms/site/style/controller/docs/StyleInfoControllerDocs.java (1)

10-10: LGTM! 검증 애노테이션 추가가 올바릅니다.

Jakarta Bean Validation 임포트와 @Valid 애노테이션이 올바르게 추가되어 요청 검증이 활성화됩니다.

Also applies to: 31-31

src/main/java/redot/redot_server/domain/auth/controller/docs/AdminImpersonationControllerDocs.java (1)

10-10: 입력 검증 지원을 위한 올바른 import 추가

Jakarta Bean Validation의 표준 @Valid import가 올바르게 추가되었습니다.

src/main/java/redot/redot_server/global/s3/util/S3Manager.java (3)

9-9: LGTM!

입력 검증을 위한 StringUtils 임포트 추가가 적절합니다.


86-88: LGTM!

exists 메서드에 null/empty 검증 추가가 적절합니다. deleteFile의 검증 패턴과 일관성 있게 구현되었습니다.


59-62: uploadFiledeleteFile 간의 경로 형식 불일치 가능성을 검증하세요.

uploadFile 메서드가 선행 슬래시가 포함된 경로를 반환하고 deleteFile이 정규화 과정 없이 key를 직접 사용하는 경우, uploadFile의 반환값을 deleteFile에 직접 전달하는 호출 지점에서 S3 키 불일치가 발생할 수 있습니다. 두 메서드 간의 경로 형식 일관성과 실제 호출 지점에서의 사용 패턴을 확인하세요.

src/main/java/redot/redot_server/domain/cms/inquiry/controller/docs/RedotAppInquiryControllerDocs.java (2)

10-10: LGTM! 입력 검증 강화

요청 본문에 대한 @Valid 애노테이션 추가는 Jakarta Bean Validation을 활용한 적절한 입력 검증 방식입니다. 이를 통해 RedotAppInquiryCreateRequest에 정의된 제약 조건이 자동으로 검증됩니다.

Also applies to: 28-29


44-47: 이 주석은 부정확합니다. 수정이 필요하지 않습니다.

RedotAppInquirySearchCondition에는 검증 애노테이션이 정의되어 있지 않으므로, @Valid를 추가할 필요가 없습니다. 또한 createInquiry@Valid는 요청 본문(request body)에 적용되지만, getAllInquiriessearchCondition은 쿼리 파라미터이므로 다른 용도입니다. 코드베이스 전체에서 검색 조건 파라미터는 @ParameterObject를 사용하며 @Valid를 적용하지 않는 일관된 패턴을 따르고 있습니다.

src/main/java/redot/redot_server/domain/redot/member/controller/RedotMemberController.java (1)

5-5: LGTM: 명시적인 콘텐츠 타입 지정

멀티파트 업로드 엔드포인트에 consumes = MediaType.MULTIPART_FORM_DATA_VALUE를 명시적으로 지정한 것은 API 계약을 명확히 하고 더 나은 클라이언트 오류 메시지를 제공하는 좋은 관행입니다.

Also applies to: 42-42

src/main/java/redot/redot_server/domain/redot/member/service/RedotMemberService.java (3)

20-20: ImageUrlResolver 의존성 추가 확인

새로운 ImageUrlResolver가 서비스에 추가되어 URL 변환을 담당합니다. 이는 저장 경로와 공개 URL 간의 일관된 변환을 제공하는 아키텍처 개선입니다.

Also applies to: 32-32


84-89: 삭제 로직 메서드 시그니처 개선됨

메서드가 이제 전체 요청 객체 대신 변환된 저장 경로를 직접 받도록 변경되어 책임이 더 명확해졌습니다. Line 86의 비교 로직은 저장 경로끼리 비교하므로 정확합니다.


75-82: 데이터베이스에 저장된 URL 형식 일관성 검증 필요

Line 75에서 요청의 공개 URL을 저장 경로로 변환하여 데이터베이스에 저장하고, Line 86에서 이전 URL과 비교합니다.

문제는 imageUrlResolver.toStoredPath()의 동작에 있습니다:

  • CDN URL (예: https://cdn.example.com/redot/member/profile/1/image.jpg)을 받으면 CDN 부분을 제거하여 /redot/member/profile/1/image.jpg 형식으로 변환합니다.
  • 하지만 이전 데이터가 업로드 API에서 반환된 공개 URL 형식으로 저장되어 있다면, 형식 불일치로 인해 비교가 실패하여 불필요한 이미지 삭제 이벤트가 발생할 수 있습니다.

특히 uploadProfileImage가 반환하는 공개 URL (ImageStorageService.upload의 toPublicUrl 결과)을 다시 updateRedotMemberInfo에 전달할 때 이 문제가 발생합니다. 같은 이미지에 대한 업데이트인데도 형식 차이로 인해 삭제 로직이 작동하게 됩니다.

CMSMemberService도 동일한 패턴을 사용하므로 같은 문제가 존재합니다.

src/main/java/redot/redot_server/domain/cms/site/setting/controller/SiteSettingController.java (1)

6-6: LGTM: 멀티파트 엔드포인트에 명시적 콘텐츠 타입 지정

로고 업로드 엔드포인트에 consumes = MediaType.MULTIPART_FORM_DATA_VALUE를 명시하여 API 명세를 명확히 했습니다. 다른 업로드 엔드포인트들과 일관된 패턴입니다.

Also applies to: 42-42

src/main/java/redot/redot_server/domain/admin/dto/response/AdminResponse.java (1)

14-22: LGTM: 응답 DTO에서 공개 URL 변환

AdminResponse 팩토리 메서드가 ImageUrlResolver를 받아 저장 경로를 공개 URL로 변환합니다. 이는 저장소에는 경로를 저장하고 클라이언트에는 공개 URL을 노출하는 깔끔한 분리를 제공합니다.

src/main/java/redot/redot_server/domain/auth/service/AdminAuthService.java (1)

17-17: LGTM: ImageUrlResolver 통합

AdminAuthServiceImageUrlResolver가 추가되어 응답 생성 시 이미지 URL 변환을 수행합니다. 다른 인증 서비스들과 일관된 패턴입니다.

Also applies to: 33-33, 73-73

src/main/java/redot/redot_server/global/s3/service/ImageStorageService.java (1)

14-14: LGTM: 이미지 업로드 시 URL 변환 추가

ImageStorageService가 업로드 후 저장 경로를 공개 URL로 변환하여 반환합니다. 이는 클라이언트가 즉시 사용 가능한 CDN URL을 받을 수 있도록 합니다.

Also applies to: 24-24, 30-31

src/main/java/redot/redot_server/domain/auth/service/RedotMemberAuthService.java (1)

18-18: LGTM: RedotMember 응답에 ImageUrlResolver 통합

회원가입 및 현재 회원 정보 조회 시 ImageUrlResolver를 응답 생성에 전달하여 프로필 이미지 URL 변환을 일관되게 처리합니다. AdminAuthService와 동일한 패턴을 따릅니다.

Also applies to: 34-34, 58-58, 112-112

src/main/java/redot/redot_server/domain/admin/controller/AdminController.java (1)

9-9: LGTM: 관리자 프로필 이미지 업로드 엔드포인트 개선

consumes = MediaType.MULTIPART_FORM_DATA_VALUE를 명시하여 멀티파트 요청만 허용하도록 설정했습니다. RedotMemberController, SiteSettingController와 일관된 패턴으로 코드베이스 전체의 업로드 엔드포인트가 통일되었습니다.

Also applies to: 95-95

src/main/java/redot/redot_server/domain/cms/member/controller/CMSMemberController.java (1)

119-127: multipart consumes 명시는 👍, 다만 파라미터 검증(@NotNull) 실제 적용 여부 확인 필요
consumes = MediaType.MULTIPART_FORM_DATA_VALUE 추가는 좋습니다. @RequestPart에 붙은 @NotNull이 런타임에 기대대로 400으로 떨어지려면 컨트롤러/전역의 method validation 설정(@validated 등)이 갖춰져 있는지 확인해 주세요.

src/main/java/redot/redot_server/domain/redot/app/service/RedotAppCreationService.java (1)

41-92: ImageUrlResolver 빈 등록/Null-safe 계약만 확실히 하면 흐름은 깔끔합니다
초기화 응답 생성 경로에서 resolver를 일관되게 주입하는 변경은 좋아 보입니다. 다만 toPublicUrl(null) 같은 입력 계약이 명확하지 않으면 여기서도 초기화 API가 터질 수 있어 resolver 정책(또는 호출부 방어)을 확정해 주세요.

src/main/java/redot/redot_server/domain/auth/service/CMSAuthService.java (1)

23-82: 응답 생성 위임은 좋고, toPublicUrl 입력 계약(null 포함)만 재확인하면 됩니다
CMSMemberResponse.fromEntity(..., imageUrlResolver)로 일원화한 건 좋습니다. cmsMember.getProfileImageUrl()이 null일 수 있는지만(그리고 그때 resolver가 안전한지) 확인해 주세요.

src/main/java/redot/redot_server/domain/cms/site/setting/controller/docs/SiteSettingControllerDocs.java (1)

10-35: [Your rewritten review comment text here]
[Exactly ONE classification tag]

src/main/java/redot/redot_server/domain/admin/controller/docs/AdminControllerDocs.java (1)

10-69: Docs 인터페이스의 검증 애노테이션과 실제 구현의 일치성 확인 필요

AdminControllerDocs에 적용된 @Valid@NotNull 애노테이션이 실제 AdminController 구현체에도 동일하게 적용되어 있고, 클래스 또는 전역 수준의 @Validated 설정을 통해 런타임에 강제되는지 확인하세요. 특히 uploadProfileImage 메서드의 MultipartFile image 파라미터에 대한 @NotNull 검증이 실제로 유효하지 않은 요청에 400 에러를 반환하는지 확인해 주세요.

src/main/java/redot/redot_server/domain/cms/site/setting/dto/response/SiteSettingResponse.java (1)

14-22: 원본 리뷰 코멘트 불필요 - ImageUrlResolver.toPublicUrl()은 이미 null/blank 입력을 안전하게 처리함

ImageUrlResolver.toPublicUrl() 메서드는 31-33줄에서 !StringUtils.hasText(pathOrUrl) 조건으로 null/blank 입력을 명시적으로 처리하고 null을 반환합니다. 따라서 siteSetting.getLogoUrl()이 null이거나 빈 값이어도 예외가 발생하지 않으며, 현재 구현이 이미 안전합니다. 제안된 방어 코드는 불필요합니다.

src/main/java/redot/redot_server/domain/cms/member/dto/response/CMSMemberResponse.java (1)

17-27: 이미 null 안전성이 보장됨

ImageUrlResolver.toPublicUrl()은 이미 null을 안전하게 처리합니다. 메서드 내에서 StringUtils.hasText(pathOrUrl)을 통해 null이나 공백 문자열을 체크한 후 null을 반환하므로, cmsMember.getProfileImageUrl()이 null이어도 예외가 발생하지 않습니다. 추가 방어 코드는 불필요합니다.

src/main/java/redot/redot_server/domain/redot/member/dto/response/RedotMemberResponse.java (1)

14-26: 조건부 null 검사는 불필요합니다. ImageUrlResolver.toPublicUrl()는 명시적인 null-안전 계약을 가지고 있으며, null 입력에 대해 null을 반환합니다(예외 발생 없음). 동일한 패턴이 CMSMemberResponse, SiteSettingResponse, AdminResponse 등 코드베이스 전반에서 일관되게 사용되고 있습니다.

Likely an incorrect or invalid review comment.

src/main/java/redot/redot_server/domain/cms/site/setting/service/SiteSettingService.java (3)

24-35: ImageUrlResolver 의존성 주입이 올바르게 적용되었습니다.

@requiredargsconstructor를 통한 생성자 주입 패턴이 정상적으로 적용되었습니다.


60-66: 삭제 로직이 올바르게 개선되었습니다.

변환된 URL을 직접 비교하도록 메서드 시그니처가 개선되었으며, null 체크와 이벤트 기반 삭제가 적절하게 구현되었습니다.


48-58: null 처리는 의도된 설계입니다.

imageUrlResolver.toStoredPath()가 null을 반환할 수 있으며, siteSetting.updateLogoUrl()이 이를 받아 저장하는 것은 정상입니다. SiteSetting의 logoUrl 필드는 선택적 필드이고, null 값은 로고가 없는 상태를 나타내는 올바른 설계입니다. deleteOldLogoUrlIfChanged() 메서드에서도 null을 안전하게 처리하고 있으므로 추가 조치가 필요하지 않습니다.

src/main/java/redot/redot_server/domain/redot/app/service/RedotAppService.java (2)

41-58: ImageUrlResolver 의존성이 올바르게 추가되었습니다.

표준 의존성 주입 패턴이 적용되었습니다.


78-80: 응답 생성 시 ImageUrlResolver 전달이 일관성 있게 적용되었습니다.

getRedotAppInfogetRedotAppList 모두에서 resolver를 응답 빌더에 전달하는 패턴이 일관되게 적용되었습니다.

src/main/java/redot/redot_server/domain/cms/member/controller/docs/CMSMemberControllerDocs.java (2)

11-12: Jakarta Bean Validation 의존성이 올바르게 추가되었습니다.

표준 검증 프레임워크를 사용하는 것은 적절한 선택입니다.


35-96: 입력 검증 애노테이션이 적절하게 추가되었습니다.

요청 DTO에 @Valid, 파일 파라미터에 @NotNull을 추가하여 입력 검증을 강화했습니다. DTO 클래스들이 다음과 같이 적절한 검증 제약조건을 정의하고 있습니다:

  • CMSMemberCreateRequest: @NotBlank (name, password), @Email @NotNull (email), @NotNull (role)
  • CMSMemberRoleRequest: @NotNull (role)
  • CMSMemberUpdateRequest: @NotBlank (name)

모든 검증 애노테이션이 올바르게 적용되어 있습니다.

src/main/java/redot/redot_server/global/s3/util/ImageUrlResolver.java (4)

16-25: CDN 베이스 URL 초기화 로직이 안전하게 구현되었습니다.

trailing slash 제거와 trim 처리가 적절하며, hasCdnBaseUrl 플래그를 통한 최적화도 잘 적용되었습니다.


30-44: toPublicUrl 메서드가 올바르게 구현되었습니다.

외부 URL과 내부 경로를 적절히 구분하고, CDN 베이스 URL을 안전하게 추가합니다. normalizePath 후 재검증 로직(lines 40-42)이 방어적 코딩을 강화합니다.


49-58: toStoredPath 메서드가 올바르게 구현되었습니다.

CDN URL을 내부 저장 경로로 변환하는 로직이 멱등성(idempotent)을 보장하며, 외부 URL은 그대로 보존합니다.


63-69: toS3Key 메서드가 올바르게 구현되었습니다.

S3 키 형식에 맞게 선행 슬래시를 제거하고, 외부 URL을 적절히 필터링합니다.

src/main/java/redot/redot_server/domain/admin/service/AdminService.java (3)

24-36: ImageUrlResolver 의존성이 올바르게 추가되었습니다.

표준 의존성 주입 패턴이 적용되었습니다.


84-100: 관리자 업데이트 로직이 올바르게 개선되었습니다.

URL 변환 후 삭제 체크와 업데이트를 수행하는 일관된 패턴이 적용되었으며, 메서드 시그니처도 명확하게 개선되었습니다.


47-59: 프로필 이미지 URL 변환 로직이 올바르게 적용되었습니다.

imageUrlResolver.toStoredPath()의 반환값이 Admin.create()에 전달되며, Admin 엔티티의 profileImageUrl 필드는 nullable하게 설계되어 있어 null 값을 안전하게 처리합니다. create() 메서드는 전달된 값을 builder 패턴으로 그대로 설정하기 때문에 null 처리에 문제가 없습니다.

src/main/java/redot/redot_server/domain/cms/member/service/CMSMemberService.java (2)

28-42: ImageUrlResolver 의존성이 올바르게 추가되었습니다.

표준 의존성 주입 패턴이 적용되었습니다.


53-107: ImageUrlResolver 통합이 일관성 있게 적용되었습니다.

모든 응답 생성 메서드와 업데이트 플로우에서 resolver를 올바르게 사용하고 있으며, AdminService와 동일한 패턴을 따르고 있습니다.

Comment on lines 22 to 24
ResponseEntity<TokenResponse> impersonateAsCMSAdmin(@Parameter(hidden = true) HttpServletRequest request,
CMSAdminImpersonationRequest cmsAdminImpersonationRequest,
@Valid CMSAdminImpersonationRequest cmsAdminImpersonationRequest,
@Parameter(hidden = true) JwtPrincipal jwtPrincipal);
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

# Step 1: Find the implementing controller for AdminImpersonationControllerDocs
echo "=== Finding implementing controller ==="
fd -e java | xargs rg -l "implements AdminImpersonationControllerDocs" | head -5

# Step 2: Check CMSAdminImpersonationRequest for validation constraints
echo -e "\n=== Checking CMSAdminImpersonationRequest DTO ==="
fd -e java | xargs rg -l "CMSAdminImpersonationRequest" | head -5

Repository: redotlabs/server

Length of output: 604


🏁 Script executed:

#!/bin/bash

# Step 1: Examine the implementing controller's impersonateAsCMSAdmin method
echo "=== AdminImpersonationController - impersonateAsCMSAdmin method ==="
rg -B 2 -A 10 "impersonateAsCMSAdmin" src/main/java/redot/redot_server/domain/auth/controller/AdminImpersonationController.java

# Step 2: Examine CMSAdminImpersonationRequest DTO structure and validation constraints
echo -e "\n=== CMSAdminImpersonationRequest DTO ==="
cat src/main/java/redot/redot_server/domain/auth/dto/request/CMSAdminImpersonationRequest.java

Repository: redotlabs/server

Length of output: 1104


🏁 Script executed:

#!/bin/bash

# Verify the exact state of AdminImpersonationControllerDocs.java around lines 22-24
echo "=== AdminImpersonationControllerDocs.java (lines 20-26) ==="
sed -n '20,26p' src/main/java/redot/redot_server/domain/auth/controller/docs/AdminImpersonationControllerDocs.java

Repository: redotlabs/server

Length of output: 613


문서화 인터페이스에 @RequestBody 애노테이션도 추가 필요

@Valid 애노테이션 추가는 적절하나, 실제 구현 컨트롤러와 일치시키기 위해 @RequestBody 애노테이션도 함께 추가되어야 합니다.

현재 문서화 인터페이스:

@Valid CMSAdminImpersonationRequest cmsAdminImpersonationRequest

실제 구현 컨트롤러:

@Valid @RequestBody CMSAdminImpersonationRequest cmsAdminImpersonationRequest

문서화 인터페이스에 @RequestBody를 추가하여 실제 구현과 일치시켜야 합니다. CMSAdminImpersonationRequest DTO는 @NotNull 검증 제약조건이 적절하게 정의되어 있습니다.

🤖 Prompt for AI Agents
In
src/main/java/redot/redot_server/domain/auth/controller/docs/AdminImpersonationControllerDocs.java
around lines 22 to 24, the documented method parameter uses only @Valid for the
CMSAdminImpersonationRequest but the real controller declares @Valid
@RequestBody; update the docs interface to add the @RequestBody annotation to
the cmsAdminImpersonationRequest parameter so it matches the implementation
(retain @Valid) and ensures the generated API docs reflect request body binding
and validation.

import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n src/main/java/redot/redot_server/domain/auth/controller/docs/RedotMemberAuthControllerDocs.java

Repository: redotlabs/server

Length of output: 4189


🏁 Script executed:

# Find the actual controller implementation
find . -path ./target -prune -o -name "*MemberAuthController.java" -type f -print | grep -v docs | head -5

Repository: redotlabs/server

Length of output: 148


🏁 Script executed:

cat -n ./src/main/java/redot/redot_server/domain/auth/controller/RedotMemberAuthController.java

Repository: redotlabs/server

Length of output: 9253


🏁 Script executed:

# Check DTOs for constraint annotations
echo "=== RedotMemberCreateRequest ==="
rg -n "class RedotMemberCreateRequest" -A 30

echo -e "\n=== RedotMemberSignInRequest ==="
rg -n "class RedotMemberSignInRequest" -A 20

echo -e "\n=== PasswordResetConfirmRequest ==="
rg -n "class PasswordResetConfirmRequest" -A 20

Repository: redotlabs/server

Length of output: 163


🏁 Script executed:

# Find DTO files
echo "=== Finding DTOs ==="
fd "RedotMemberCreateRequest.java" -o "RedotMemberSignInRequest.java" -o "PasswordResetConfirmRequest.java"

Repository: redotlabs/server

Length of output: 275


🏁 Script executed:

# Find DTO files
echo "=== Finding RedotMemberCreateRequest ==="
fd "RedotMemberCreateRequest\.java"

echo -e "\n=== Finding RedotMemberSignInRequest ==="
fd "RedotMemberSignInRequest\.java"

echo -e "\n=== Finding PasswordResetConfirmRequest ==="
fd "PasswordResetConfirmRequest\.java"

Repository: redotlabs/server

Length of output: 459


🏁 Script executed:

echo "=== RedotMemberCreateRequest ==="
cat -n src/main/java/redot/redot_server/domain/redot/member/dto/request/RedotMemberCreateRequest.java

echo -e "\n=== RedotMemberSignInRequest ==="
cat -n src/main/java/redot/redot_server/domain/auth/dto/request/RedotMemberSignInRequest.java

echo -e "\n=== PasswordResetConfirmRequest ==="
cat -n src/main/java/redot/redot_server/domain/auth/dto/request/PasswordResetConfirmRequest.java

Repository: redotlabs/server

Length of output: 1847


🏁 Script executed:

# Verify if @Validated is used anywhere in the controller or if it's a standard pattern
echo "=== Checking for @Validated in similar controller classes ==="
fd "Controller.java" -x grep -l "@Validated" | head -5

Repository: redotlabs/server

Length of output: 218


🏁 Script executed:

# Check how @Validated is used in that controller
grep -n "@Validated\|@RestController\|class.*Controller" ./src/main/java/redot/redot_server/domain/cms/site/setting/controller/SiteSettingController.java | head -10

Repository: redotlabs/server

Length of output: 169


컨트롤러 클래스에 @validated 애노테이션이 누락되었습니다.

@Valid 애노테이션을 추가한 것은 좋은 개선이지만, 이를 실제로 동작하게 하려면 RedotMemberAuthController 클래스에 @Validated 애노테이션이 필요합니다. 현재 상태에서는 Spring이 메소드 레벨의 검증을 수행하지 않습니다.

필요한 변경:

  • RedotMemberAuthController 클래스에 @Validated 애노테이션 추가 (라인 37-39 부근)
  • 예: @RestController 다음 줄에 @Validated 추가

확인된 사항:

  • 세 개의 요청 DTO (RedotMemberCreateRequest, RedotMemberSignInRequest, PasswordResetConfirmRequest)는 모두 @Email, @NotBlank 등의 제약 애노테이션이 올바르게 정의되어 있습니다. ✓

추가 제안 (선택사항):

  • getSocialLoginUrl 메소드의 provider, redirectUri, failureUri 파라미터에 @NotBlank 제약을 추가하면 더욱 견고한 검증이 가능합니다.
🤖 Prompt for AI Agents
In
src/main/java/redot/redot_server/domain/auth/controller/docs/RedotMemberAuthControllerDocs.java
around lines 37 to 39, the controller class is missing the @Validated annotation
so method-level @Valid checks are not triggered; add @Validated directly below
the @RestController declaration in the RedotMemberAuthController class to enable
Spring validation on request DTOs, and optionally annotate the getSocialLoginUrl
method parameters (provider, redirectUri, failureUri) with @NotBlank to
strengthen validation.

import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
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

🧩 Analysis chain

🏁 Script executed:

# 1) Find and examine AppVersionCreateRequest
fd -t f "AppVersionCreateRequest.java" src/main/java

Repository: redotlabs/server

Length of output: 154


🏁 Script executed:

# 2) Search for AppVersionCreateRequest usage and validation constraints
rg -n "AppVersionCreateRequest" src/main/java --type java -A 5

Repository: redotlabs/server

Length of output: 7681


🏁 Script executed:

# 3) Find the actual controller implementation for createVersion
rg -n "createVersion" src/main/java --type java -B 2 -A 5

Repository: redotlabs/server

Length of output: 4206


🏁 Script executed:

# 4) Check for @RequestBody, @ModelAttribute, @Valid patterns in the cms.site.page.controller area
rg -n "@RequestBody|@ModelAttribute|@Valid" src/main/java/redot/redot_server/domain/cms/site/page --type java

Repository: redotlabs/server

Length of output: 777


Docs 인터페이스에 @RequestBody 추가 필요 - 실제 컨트롤러 구현과 일치시키기

실제 컨트롤러 구현(CMSSitePageController.java:51)은 @Valid @RequestBody AppVersionCreateRequest request로 올바르게 구성되어 있으나, Docs 인터페이스(CMSSitePageControllerDocs.java:48)는 현재 @Valid AppVersionCreateRequest request만 있습니다. 구현과 문서 시그니처를 일치시키기 위해 @RequestBody 애노테이션을 추가하세요. 참고로 AppVersionCreateRequest DTO는 이미 @NotNull, @Valid 등의 제약 조건이 적절히 설정되어 있습니다.

🤖 Prompt for AI Agents
In
src/main/java/redot/redot_server/domain/cms/site/page/controller/docs/CMSSitePageControllerDocs.java
around lines 10 and 48, the Docs interface method parameter is missing
@RequestBody and thus does not match the actual controller signature; update the
method parameter to add @RequestBody before the @Valid AppVersionCreateRequest
request and add the corresponding import for
org.springframework.web.bind.annotation.RequestBody at the top of the file so
the Docs interface and controller signatures are identical.

content = @Content(schema = @Schema(implementation = StyleInfoResponse.class)))
ResponseEntity<StyleInfoResponse> updateStyleInfo(@Parameter(hidden = true) Long redotAppId,
StyleInfoUpdateRequest request);
@Valid StyleInfoUpdateRequest request);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find StyleInfoController implementation (not the Docs interface)
echo "=== Finding StyleInfoController implementation ==="
find . -type f -name "*StyleInfoController.java" ! -name "*Docs.java" | head -20

# Find StyleInfoUpdateRequest
echo ""
echo "=== Finding StyleInfoUpdateRequest ==="
find . -type f -name "*StyleInfoUpdateRequest.java"

Repository: redotlabs/server

Length of output: 340


🏁 Script executed:

#!/bin/bash
# Search for updateStyleInfo method in actual controller
echo "=== Checking for updateStyleInfo method in controller implementation ==="
rg -n -B 5 -A 5 'updateStyleInfo.*StyleInfoUpdateRequest' --type java -g '!*Docs.java' | head -50

# Search for @Valid annotations on request parameters
echo ""
echo "=== Looking for @Valid annotations on StyleInfoUpdateRequest ==="
rg -n '@Valid.*StyleInfoUpdateRequest' --type java | head -20

Repository: redotlabs/server

Length of output: 3433


🏁 Script executed:

cat src/main/java/redot/redot_server/domain/cms/site/style/dto/request/StyleInfoUpdateRequest.java

Repository: redotlabs/server

Length of output: 290


StyleInfoUpdateRequest DTO에 검증 제약 조건이 정의되지 않았습니다.

구현 컨트롤러에는 @Valid 애노테이션이 올바르게 적용되어 있으나, StyleInfoUpdateRequest 레코드에는 검증 제약 조건(@NotNull, @NotBlank 등)이 전혀 없습니다. 따라서 현재 @Valid 애노테이션은 실질적인 검증 효과를 가지지 못합니다. DTO의 필드들에 적절한 검증 애노테이션을 추가하세요. 예를 들어:

public record StyleInfoUpdateRequest(
        @NotBlank String color,
        @NotBlank String font,
        @NotNull Theme theme
) {
}
🤖 Prompt for AI Agents
In
src/main/java/redot/redot_server/domain/cms/site/style/controller/docs/StyleInfoControllerDocs.java
around line 31, the controller uses @Valid on StyleInfoUpdateRequest but the DTO
has no validation annotations; add javax.validation annotations to the
StyleInfoUpdateRequest record fields (e.g., @NotBlank, @NotNull, @Size where
appropriate) to enforce non-null/format constraints, import the validation
annotations, and ensure any enums or nested objects are validated (use @Valid on
nested types) so @Valid on the controller method actually triggers validation.

content = @Content(schema = @Schema(implementation = UploadedImageUrlResponse.class)))
ResponseEntity<UploadedImageUrlResponse> uploadProfileImage(@Parameter(hidden = true) JwtPrincipal jwtPrincipal,
MultipartFile image);
@NotNull MultipartFile image);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "RedotMemberControllerDocs.java" -type f

Repository: redotlabs/server

Length of output: 161


🏁 Script executed:

find . -name "RedotMemberController.java" -type f

Repository: redotlabs/server

Length of output: 152


🏁 Script executed:

cat -n ./src/main/java/redot/redot_server/domain/redot/member/controller/docs/RedotMemberControllerDocs.java

Repository: redotlabs/server

Length of output: 2359


🏁 Script executed:

cat -n ./src/main/java/redot/redot_server/domain/redot/member/controller/RedotMemberController.java

Repository: redotlabs/server

Length of output: 3054


🏁 Script executed:

rg -n "@Validated" --type java | head -20

Repository: redotlabs/server

Length of output: 261


🏁 Script executed:

find . -name "*Config.java" -o -name "*Configuration.java" | xargs grep -l "Validation" 2>/dev/null | head -10

Repository: redotlabs/server

Length of output: 42


RedotMemberController에 @validated 애노테이션 추가 필요

RedotMemberController 클래스에 @validated 애노테이션이 누락되어 있습니다. @NotNull 애노테이션이 uploadProfileImage 메서드의 image 파라미터에 올바르게 추가되었으나, @validated 없이는 런타임에 Bean Validation이 실제로 작동하지 않습니다.

같은 프로젝트의 SiteSettingController에서는 @validated를 사용하고 있으므로, 일관성을 위해 RedotMemberController 클래스 정의 위에 @validated 애노테이션을 추가하세요:

@RestController
@Validated
@RequiredArgsConstructor
@RequestMapping("/api/v1/redot/members")
public class RedotMemberController implements RedotMemberControllerDocs {
🤖 Prompt for AI Agents
In
src/main/java/redot/redot_server/domain/redot/member/controller/docs/RedotMemberControllerDocs.java
around line 30, the controller is missing the @Validated annotation so @NotNull
on the uploadProfileImage parameter won't trigger Bean Validation at runtime;
add @Validated to the RedotMemberController class definition (above the class
declaration, alongside @RestController and @RequiredArgsConstructor) to enable
method-level validation and keep it consistent with SiteSettingController.


/**
* 지정된 경로의 파일 삭제
* @param filePath 삭제할 파일 경로 (ex: /app/1/logo/image.png)
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

JavaDoc 파라미터 문서가 실제 코드와 불일치합니다.

JavaDoc에 @param filePath로 표기되어 있지만 실제 파라미터 이름은 key입니다. 문서를 업데이트해야 합니다.

다음 diff를 적용하여 문서를 수정하세요:

     /**
      * 지정된 경로의 파일 삭제
-     * @param filePath 삭제할 파일 경로 (ex: /app/1/logo/image.png)
+     * @param key 삭제할 S3 key (ex: app/1/logo/image.png)
      */
📝 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
* @param filePath 삭제할 파일 경로 (ex: /app/1/logo/image.png)
* @param key 삭제할 S3 key (ex: app/1/logo/image.png)
🤖 Prompt for AI Agents
In src/main/java/redot/redot_server/global/s3/util/S3Manager.java around line
57, the JavaDoc param name is incorrect: it documents @param filePath but the
actual method parameter is named key; update the JavaDoc to use @param key and
adjust the description (e.g., "@param key 삭제할 파일 경로 (ex:
/app/1/logo/image.png)") so the doc matches the code.

@eraser502 eraser502 merged commit fa8c7c4 into main Dec 14, 2025
2 checks passed
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