Conversation
로고와 피드 이미지 업로드 및 삭제 기능 구현 및 리팩터링
[fix] 동아리 수정 시 발생하는 필드 누락 문제 해결
Summary by CodeRabbit
Walkthrough이번 PR은 클럽 관련 API와 이미지 처리 기능에 대한 보안 강화, DTO 및 엔티티 구조 변경, 파일 유형 관리 개선, 업데이트 로직 수정 등을 포함합니다. 구체적으로는 컨트롤러에 보안 어노테이션이 추가되고, DTO의 필드명이 변경되며, 클럽 업데이트 로직이 기존 객체 수정 방식으로 전환되는 등 여러 모듈에서 내부 로직과 데이터 스키마가 갱신되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as 클라이언트
participant CIC as ClubImageController
participant CIS as ClubImageService
participant Storage as 파일 스토리지
Client->>CIC: uploadLogo(clubId, logo 파일) 호출
CIC->>CIS: uploadFile(clubId, 파일, FileType.LOGO) 실행
alt 기존 로고가 존재하는 경우
CIS->>CIS: deleteFile(club, 기존 로고 경로) 실행
end
CIS->>Storage: 파일 업로드 요청
Storage-->>CIS: 업로드 완료 및 URL 반환
CIS-->>CIC: URL 반환
CIC-->>Client: 업로드 결과 응답 반환
sequenceDiagram
participant Client as 클라이언트
participant CIC as ClubImageController
participant CIS as ClubImageService
Client->>CIC: putFeeds(clubId, FeedUpdateRequest) 호출
CIC->>CIS: updateFeeds(clubId, 새 Feed 이미지 목록) 실행
CIS->>CIS: 피드 이미지 유효성 검사 및 업데이트 처리
CIS-->>CIC: 처리 결과 반환
CIC-->>Client: 업데이트 결과 응답 반환
Suggested labels
Suggested reviewers
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 2
🔭 Outside diff range comments (1)
backend/src/main/java/moadong/club/payload/request/ClubInfoRequest.java (1)
6-20:⚠️ Potential issue필드명 표준화 적용
필드명 변경 (
clubId→id,clubPresidentName→presidentName,telephoneNumber→presidentPhoneNumber)과recruitmentForm추가는 PR 목적에 맞는 개선입니다. 하지만recruitmentStart,recruitmentEnd,recruitmentTarget필드가 제거된 것으로 보입니다.이 변경으로 인해
ClubRecruitmentInformation클래스의 첫 번째update메서드(69-77줄)에서 참조하는 필드들이 더 이상 존재하지 않는 문제가 발생했습니다.프론트엔드 팀에게 이 API 변경사항을 명확히 전달해야 합니다.
🧹 Nitpick comments (2)
backend/src/main/java/moadong/gcs/dto/FeedUpdateRequest.java (1)
1-6: 간결한 레코드 구현이 좋습니다만 문서화가 필요합니다.피드 업데이트 요청을 위한 레코드를 생성한 점이 좋습니다. 그러나 이 DTO의 목적과 사용처에 대한 주석이 추가되면 코드 유지보수성이 향상될 것입니다.
다음과 같이 주석을 추가하는 것을 권장합니다:
package moadong.gcs.dto; import java.util.List; +/** + * 클럽의 피드 이미지 목록을 업데이트하기 위한 DTO + */ public record FeedUpdateRequest(List<String> feeds) { }backend/src/main/java/moadong/gcs/service/ClubImageService.java (1)
105-124: 파일 삭제 로직 개선
deleteFile메서드가Club객체를 매개변수로 받아 로고 삭제 시 클럽 정보를 직접 업데이트할 수 있도록 개선되었습니다. 하지만 한 가지 개선점이 있습니다.파일 타입을 확인할 때 "logo" 문자열을 하드코딩하는 대신
FileType열거형을 사용하면 더 좋을 것 같습니다:- String fileType = filePath.split("/")[5]; - if (fileType.equals("logo")) { + String fileTypePath = filePath.split("/")[5]; + if (fileTypePath.equals(FileType.LOGO.getPath())) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
backend/src/main/java/moadong/club/controller/ClubController.java(3 hunks)backend/src/main/java/moadong/club/controller/ClubMetricController.java(2 hunks)backend/src/main/java/moadong/club/entity/Club.java(1 hunks)backend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.java(2 hunks)backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java(1 hunks)backend/src/main/java/moadong/club/payload/request/ClubDescriptionUpdateRequest.java(1 hunks)backend/src/main/java/moadong/club/payload/request/ClubInfoRequest.java(2 hunks)backend/src/main/java/moadong/club/service/ClubCommandService.java(1 hunks)backend/src/main/java/moadong/gcs/controller/ClubImageController.java(2 hunks)backend/src/main/java/moadong/gcs/domain/FileType.java(1 hunks)backend/src/main/java/moadong/gcs/dto/FeedUpdateRequest.java(1 hunks)backend/src/main/java/moadong/gcs/dto/ImageDeleteRequest.java(0 hunks)backend/src/main/java/moadong/gcs/service/ClubImageService.java(5 hunks)backend/src/main/java/moadong/global/config/SecurityConfig.java(2 hunks)backend/src/main/java/moadong/user/controller/UserController.java(3 hunks)
💤 Files with no reviewable changes (1)
- backend/src/main/java/moadong/gcs/dto/ImageDeleteRequest.java
🔇 Additional comments (29)
backend/src/main/java/moadong/gcs/domain/FileType.java (1)
5-17: 타입 안정성을 높인 열거형 구현이 좋습니다.문자열 대신 열거형을 사용하여 파일 타입을 관리하는 방식으로 변경한 것은 좋은 접근법입니다. 이를 통해 타입 안정성이 향상되고 오타로 인한 오류를 방지할 수 있습니다.
backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java (1)
52-52: 메서드 이름 변경으로 의미가 명확해졌습니다.
toString()대신getDescription()메서드를 사용하도록 변경한 것은 필드 이름 통일 작업의 일환으로 적절합니다. 이렇게 하면 사용자에게 보여줄 더 의미 있는 문자열을 얻을 수 있습니다.backend/src/main/java/moadong/club/controller/ClubMetricController.java (1)
4-4: 보안 강화를 위한 인증 어노테이션 추가가 적절합니다.컨트롤러 클래스에
@PreAuthorize("isAuthenticated()")및@SecurityRequirement(name = "BearerAuth")어노테이션을 추가하여 인증된 사용자만 API에 접근할 수 있도록 보안을 강화한 것은 매우 중요한 변경사항입니다.추가적으로, 이 변경사항을 프론트엔드 팀에게 명확히 전달하는 것이 중요합니다. 이제 API 호출 시 Bearer 토큰이 필요하므로 프론트엔드 코드도 이에 맞게 수정되어야 합니다.
Also applies to: 10-10, 20-21
backend/src/main/java/moadong/global/config/SecurityConfig.java (1)
33-38: 보안 스키마 추가가 잘 되었습니다!Swagger/OpenAPI 문서에 JWT 기반 인증 방식을 명시적으로 정의하여 API 보안 요구사항을 더 명확하게 문서화했습니다. 이는 API 문서화의 품질을 향상시키고 프론트엔드 팀이 인증이 필요한 엔드포인트를 쉽게 식별할 수 있게 해줍니다.
backend/src/main/java/moadong/user/controller/UserController.java (1)
60-63: 사용자 업데이트 엔드포인트에 보안 강화 조치가 적절히 추가되었습니다!
update메서드에@PreAuthorize("isAuthenticated()")어노테이션을 추가하여 인증된 사용자만 접근할 수 있도록 하고,@SecurityRequirement(name = "BearerAuth")를 통해 Swagger 문서에 보안 요구사항을 명시했습니다. 사용자 정보 수정은 민감한 작업이므로 이러한 보안 조치는 매우 중요합니다.backend/src/main/java/moadong/club/service/ClubCommandService.java (3)
31-33: 필드명 변경이 적절히 적용되었습니다!
clubId에서id로 필드명 변경이 잘 반영되었습니다. 이는 PR 목표와 일치하며 API 필드 이름의 일관성을 높여줍니다.
41-43: 필드명 변경이 적절히 적용되었습니다!여기서도
clubId에서id로 필드명 변경이 잘 반영되었습니다. 일관된 네이밍 컨벤션을 유지하는 것은 코드 가독성과 유지보수성을 높이는 데 중요합니다.
46-51: 모집 일정 스케줄링 로직이 적절히 이동되었습니다!모집 일정 관련 로직이
updateClubDescription메서드로 이동된 것은 PR 목표인 "데이터 전송과 수신의 분리"에 부합합니다. 이제 클럽 설명 업데이트와 함께 모집 일정도 함께 처리되어 관련 기능이 더 논리적으로 그룹화되었습니다.backend/src/main/java/moadong/club/payload/request/ClubDescriptionUpdateRequest.java (1)
3-10: DTO 구조 개선이 잘 이루어졌습니다!
ClubDescriptionUpdateRequest레코드의 변경 사항:
clubId에서id로 필드명 변경- 모집 관련 필드(
recruitmentStart,recruitmentEnd,recruitmentTarget) 추가이러한 변경은 API의 명확성과 일관성을 향상시키며, 모집 정보와 클럽 설명 정보를 함께 처리할 수 있게 되었습니다. PR 목표인 "API 필드 분리 및 이름 통일"에 잘 부합합니다.
프론트엔드 팀에 이러한 필드명 변경과 구조 변경에 대해 반드시 알려주어야 합니다.
backend/src/main/java/moadong/club/entity/Club.java (1)
68-68: 객체 지향적 설계로의 개선
clubRecruitmentInformation객체를 새로 생성하는 대신 기존 객체의update메서드를 호출하도록 변경한 것은 좋은 접근 방식입니다. 이는 책임 분리 원칙(SRP)과 객체 캡슐화를 더 잘 반영한 리팩토링입니다.backend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.java (1)
83-87: ClubDescriptionUpdateRequest의 필드 추가 반영
updateDescription메서드가 확장되어 모집 기간 및 대상 정보도 업데이트하도록 변경된 것은 긍정적입니다. 이는 PR 목적에 부합하는 변경사항입니다.backend/src/main/java/moadong/club/controller/ClubController.java (4)
45-47: 보안 강화: 인증 요구사항 추가
@PreAuthorize및@SecurityRequirement어노테이션을 추가하여 인증된 사용자만 클럽 생성 기능에 접근할 수 있도록 보안을 강화한 것은 매우 좋은 변경입니다.
53-56: 보안 강화 및 작업 설명 개선클럽 약력 수정 API에 인증 요구사항을 추가하고 작업 설명을 더 구체적으로 변경한 것은 좋은 개선입니다. 이는 API 문서의 명확성과 보안성을 향상시킵니다.
62-65: 보안 강화 및 작업 설명 개선클럽 모집정보 수정 API에 인증 요구사항을 추가하고 작업 설명을 "클럽 상세소개 수정"에서 "클럽 모집정보 수정"으로 변경한 것은 API의 목적을 더 명확히 표현하는 좋은 개선입니다.
57-57:Details
✅ Verification successful
API 필드 변경 확인 필요
updateClubInfo메서드에서request.id()를 사용하는지 확인해야 합니다. 이전에는request.clubId()를 사용했을 수 있습니다.
🏁 Script executed:
#!/bin/bash # ClubCommandService에서 updateClubInfo 메서드 구현 확인 rg -A 5 "updateClubInfo" --type javaLength of output: 1480
API 필드 변경: 확인 완료
ClubCommandService.java의 updateClubInfo 메서드가 request.id()를 올바르게 사용하고 있으므로, 클럽 정보 업데이트 시 문제될 부분이 없습니다.
- 검증 결과: request.id()를 사용하고 있음 (기존 request.clubId()에서 변경된 것으로 보임)
backend/src/main/java/moadong/gcs/controller/ClubImageController.java (6)
4-4: 새로운 import 추가에 대한 검토보안 요구사항, 파일 타입 열거형 및 새로운 DTO를 위한 import가 적절하게 추가되었습니다. 특히
SecurityRequirement,FileType,FeedUpdateRequest는 코드의 기능 향상을 위해 필요한 요소입니다.Also applies to: 6-9
28-29: 보안 강화를 위한 인증 요구사항 추가컨트롤러에
@PreAuthorize("isAuthenticated()")및@SecurityRequirement(name = "BearerAuth")어노테이션을 추가하여 인증된 사용자만 API에 접근할 수 있도록 보안 설정이 강화되었습니다. 이는 모든 클럽 이미지 관련 API에 적용되는 좋은 보안 관행입니다.
37-41: 로고 업로드 메서드 명확화메서드 이름이
uploadLogo로 명확하게 변경되어 해당 기능의 목적을 명확히 표현합니다. 이는 API 필드 분리 및 이름 통일이라는 PR 목표에 부합합니다.
43-48: 로고 삭제 기능 추가로고 이미지를 삭제하는 새로운 엔드포인트가 적절하게 추가되었습니다. RESTful 규약에 맞게 HTTP DELETE 메서드를 사용하고 명확한 경로와 응답을 제공합니다.
50-56: 피드 이미지 업로드 로직 개선단일 피드 이미지 업로드를 위한 메서드가 개선되었습니다. 특히
FileType.FEED열거형을 사용하여 파일 타입을 지정하는 것은 타입 안전성을 높이는 좋은 접근 방식입니다.
58-64: 피드 정보 갱신 기능 추가피드 이미지 목록을 갱신하는 새로운 엔드포인트가 추가되었습니다.
FeedUpdateRequestDTO를 사용하여 데이터를 캡슐화하는 방식은 API 데이터 분리라는 PR 목표에 적합합니다.프론트엔드 팀에 이 변경사항을 알려야 함을 기억하세요. PR 설명에 언급된 대로, 이 변경은 프론트엔드와의 통합에 영향을 미칠 수 있습니다.
backend/src/main/java/moadong/gcs/service/ClubImageService.java (8)
40-42: 기존 로고 삭제 로직 개선기존 로고가 존재할 경우 삭제하는 로직이 개선되었습니다.
deleteFile메서드에Club객체를 전달하여 상태 관리를 더욱 효율적으로 할 수 있게 되었습니다.
44-44: 타입 안전성 향상문자열 대신
FileType.LOGO열거형을 사용하여 타입 안전성이 향상되었습니다. 이는 오타로 인한 오류 가능성을 줄이고 코드의 일관성을 유지하는 데 도움이 됩니다.
50-58: 로고 삭제 기능 구현클럽 로고를 삭제하는 기능이 적절하게 구현되었습니다. 로고 존재 여부를 확인한 후 삭제 작업을 수행하는 방식은 안전하고 효율적입니다.
60-77: 피드 이미지 갱신 로직 구현피드 이미지 목록을 갱신하는 기능이 추가되었습니다. 빈 목록 검사와 최대 이미지 수 제한 검사를 포함하여 예외 처리가 잘 구현되어 있습니다.
79-87: 피드 목록 업데이트 헬퍼 메서드 추가기존 피드 목록과 새 피드 목록을 비교하여 제거된 이미지를 삭제하는 헬퍼 메서드가 추가되었습니다. 이는 코드의 모듈화와 재사용성을 향상시킵니다.
90-90: 파일 업로드 메서드 개선
uploadFile메서드가 문자열 대신FileType열거형을 매개변수로 받도록 개선되었습니다. 이는 타입 안전성과 코드 일관성을 높이는 좋은 변경입니다.
127-127: getBlobInfo 메서드 개선
getBlobInfo메서드가FileType열거형을 사용하도록 개선되었습니다. 이로써 파일 경로 생성 시 일관성과 타입 안전성이 향상되었습니다.
136-136: 파일 경로 생성 로직 개선파일 경로 생성 시
fileType.getPath()를 사용하여 일관된 경로 구조를 유지하도록 개선되었습니다. 이는 파일 관리의 일관성을 높이는 좋은 변경입니다.
| public void update(ClubInfoRequest request) { | ||
| this.introduction = request.introduction(); | ||
| this.presidentName = request.presidentName(); | ||
| this.presidentTelephoneNumber = request.presidentPhoneNumber(); | ||
| this.tags = request.tags(); | ||
| this.recruitmentForm = request.recruitmentForm(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
최신 필드명을 사용하는 update 메서드
이 메서드는 ClubInfoRequest의 최신 필드명을 올바르게 사용하고 있습니다. 하지만 위에서 언급한 대로 동일한 파라미터 타입을 가진 중복 메서드가 존재합니다. 두 메서드를 통합하여 중복을 제거하세요.
public void update(ClubInfoRequest request) {
this.introduction = request.introduction();
this.presidentName = request.presidentName();
this.presidentTelephoneNumber = request.presidentPhoneNumber();
this.tags = request.tags();
this.recruitmentForm = request.recruitmentForm();
+ // 필요한 경우 다른 필드도 업데이트
}Committable suggestion skipped: line range outside the PR's diff.
| public void update(ClubInfoRequest clubInfoRequest){ | ||
| this.recruitmentEnd = clubInfoRequest.recruitmentEnd(); | ||
| this.recruitmentStart = clubInfoRequest.recruitmentStart(); | ||
| this.presidentName = clubInfoRequest.clubPresidentName(); | ||
| this.presidentTelephoneNumber = clubInfoRequest.telephoneNumber(); | ||
| this.recruitmentTarget = clubInfoRequest.recruitmentTarget(); | ||
| this.introduction = clubInfoRequest.introduction(); | ||
| this.tags = clubInfoRequest.tags(); | ||
| } |
There was a problem hiding this comment.
동일한 파라미터 타입의 중복 메서드 발견
update(ClubInfoRequest) 메서드가 두 번 정의되어 있습니다 (69-77줄과 112-118줄). 두 메서드는 다른 필드를 갱신하며, 특히 첫 번째 메서드는 clubPresidentName과 telephoneNumber를 사용하지만 이는 ClubInfoRequest 클래스에서 업데이트된 필드명과 일치하지 않습니다.
다음과 같이 하나의 통합된 메서드로 수정하세요:
-public void update(ClubInfoRequest clubInfoRequest){
- this.recruitmentEnd = clubInfoRequest.recruitmentEnd();
- this.recruitmentStart = clubInfoRequest.recruitmentStart();
- this.presidentName = clubInfoRequest.clubPresidentName();
- this.presidentTelephoneNumber = clubInfoRequest.telephoneNumber();
- this.recruitmentTarget = clubInfoRequest.recruitmentTarget();
- this.introduction = clubInfoRequest.introduction();
- this.tags = clubInfoRequest.tags();
-}📝 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.
| public void update(ClubInfoRequest clubInfoRequest){ | |
| this.recruitmentEnd = clubInfoRequest.recruitmentEnd(); | |
| this.recruitmentStart = clubInfoRequest.recruitmentStart(); | |
| this.presidentName = clubInfoRequest.clubPresidentName(); | |
| this.presidentTelephoneNumber = clubInfoRequest.telephoneNumber(); | |
| this.recruitmentTarget = clubInfoRequest.recruitmentTarget(); | |
| this.introduction = clubInfoRequest.introduction(); | |
| this.tags = clubInfoRequest.tags(); | |
| } |
#️⃣연관된 이슈
📝작업 내용
🫡 참고사항