Skip to content

Conversation

@jjeongdong
Copy link
Member

@jjeongdong jjeongdong commented Feb 2, 2025

#️⃣ 요약 설명

커리어 작성 API에 이미지 업로드 기능 추가

📝 작업 내용

커리어 작성 API에 이미지 업로드 기능 추가했습니다.

동작 확인

image

💬 리뷰 요구사항(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?

Summary by CodeRabbit

  • 새로운 기능

    • 이미지 첨부 기능이 추가되어 답변 작성 시 파일 업로드가 가능합니다.
    • AWS S3 연동을 통해 이미지 저장이 지원됩니다.
  • API 개선

    • 답변 등록 및 수정 API가 파일 업로드와 데이터 전송을 동시에 처리하도록 개선되었습니다.
    • 응답 데이터의 그룹화 및 필드 명칭이 업데이트되어 가독성이 향상되었습니다.
  • 문서화 개선

    • 다양한 도메인을 분류하는 API 태그가 추가되어 문서화와 탐색이 용이해졌습니다.
  • 기타

    • 일부 오류 코드가 정리되어 메시지 전달이 간소화되었습니다.

@jjeongdong jjeongdong added the feat 기능 추가 label Feb 2, 2025
@jjeongdong jjeongdong self-assigned this Feb 2, 2025
@coderabbitai
Copy link

coderabbitai bot commented Feb 2, 2025

Walkthrough

이 PR은 AWS S3 서비스 사용을 위한 SDK 종속성 추가와 함께, Answer 도메인의 엔티티, 컨트롤러, DTO, Converter, 서비스 등 전반에 걸쳐 필드 재정렬, 파라미터 단순화, 네이밍 수정 및 파일 업로드 기능 추가 등의 변경을 포함합니다. 또한, API 문서화를 위한 @Tag 어노테이션이 여러 컨트롤러에 추가되었으며, S3 클라이언트 설정과 이미지 업로드 로직이 새로운 클래스에 구현되었습니다. 마지막으로, 불필요한 에러 코드가 제거되었습니다.

Changes

파일 경로 변경 요약
build.gradle AWS S3 SDK 종속성 추가 (aws-java-sdk-s3:1.12.611, aws-sdk-s3:2.30.2)
src/main/java/UMC/.../Answer.java JPA 어노테이션 및 Lombok import 추가, sequence 필드 재정렬 (이전 선언 제거 후 새로운 선언 추가)
src/main/java/UMC/.../AnswerController.java API 메서드 시그니처 수정: @RequestBody@RequestPart, 파라미터 및 JSON 필드 명 변경, 멀티파트 파일 업로드 지원 추가
src/main/java/UMC/.../AnswerConverter.java 메서드 파라미터 단순화 (DTO에서 String으로 변경), 변수 네이밍 (answerListanswerInfoDTOList) 및 import 조정
src/main/java/UMC/.../AnswerCreateOrUpdateDTO.java
src/main/java/UMC/.../AnswerInfoListDTO.java
내부 record 명칭 변경 및 파라미터 이름 수정 (answerListanswerGroupDTOList, answerInfoListanswerInfoDTOList)
src/main/java/UMC/.../AnswerCommandService.java 이미지 파일 처리 로직 추가 및 메서드 시그니처 수정 (MultipartFile 리스트 파라미터 추가)
`src/main/java/UMC/.../(Content Job
src/main/java/UMC/.../CommonErrorCode.java 불필요한 에러 코드 NOT_FOUND_JOB 제거
src/main/java/UMC/.../s3/config/S3Config.java AWS S3 클라이언트 설정을 위한 새로운 설정 클래스 추가 (accessKey, secretKey, region 주입 및 클라이언트 빈 등록)
src/main/java/UMC/.../s3/service/S3Uploader.java S3 이미지 업로드 기능을 제공하는 업로더 클래스 추가, 업로드 후 URL 반환

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant AnswerController
    participant AnswerCommandService
    participant S3Uploader
    participant "AWS S3"
    
    Client->>AnswerController: 이미지 포함 응답 전송 요청
    AnswerController->>AnswerCommandService: 멀티파트 파라미터 전달 (데이터 + 이미지 파일 리스트)
    AnswerCommandService->>S3Uploader: uploadImage(file) 호출
    S3Uploader->>AWS S3: 파일 업로드 (putObject)
    AWS S3-->>S3Uploader: 업로드 URL 응답
    S3Uploader-->>AnswerCommandService: URL 반환
    AnswerCommandService-->>AnswerController: 응답 데이터에 URL 포함 처리
Loading

Suggested reviewers

  • veronees
  • KSLEE19
  • jpark0506

Poem

깡총깡총 코드 숲 속 길,
S3 클라이언트 날개 달고 뛰네.
DTO와 컨트롤러 새로 물들어
API 태그 빛나는 밤,
토끼의 발자취처럼 가볍게 변화하네 🐇
코드의 숲에 봄바람 불어오길!
깔깔, 즐거운 개발의 노래♪


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e05fef2 and 34869a2.

📒 Files selected for processing (1)
  • .github/workflows/deploy.yml (0 hunks)
💤 Files with no reviewable changes (1)
  • .github/workflows/deploy.yml

🪧 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.
    • Generate unit testing code for this file.
    • 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. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

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

Documentation and Community

  • 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: 5

🔭 Outside diff range comments (1)
src/main/java/UMC/career_mate/domain/answer/Answer.java (1)

50-52: 내용 업데이트 메소드에 유효성 검증이 필요합니다.

updateContent 메소드에서 content의 null 체크나 빈 문자열 검증이 없습니다. 다음과 같은 검증을 추가하는 것이 좋습니다:

 public void updateContent(String content) {
+    if (content == null || content.trim().isEmpty()) {
+        throw new IllegalArgumentException("Content cannot be null or empty");
+    }
     this.content = content;
 }
🧹 Nitpick comments (8)
src/main/java/UMC/career_mate/domain/recruit/controller/RecruitController.java (1)

70-72: @deprecated 어노테이션에 대한 상세한 설명 필요

@deprecated 어노테이션이 사용된 메서드에 대해 더 자세한 설명이 필요합니다. 대체 방법이나 제거 예정 시기를 명시하면 좋을 것 같습니다.

 @Deprecated
-@Operation(summary = "채용 공고 db 저장 api", description = "테스트 개발용입니다")
+@Operation(
+    summary = "채용 공고 db 저장 api",
+    description = """
+        테스트 개발용입니다.
+        이 API는 향후 제거될 예정이며, 프로덕션 환경에서 사용하지 마세요.
+        대체 방법: [대체 방법 설명]
+        제거 예정 시기: [제거 예정 시기]
+        """
+)
src/main/java/UMC/career_mate/domain/member/controller/MemberController.java (1)

23-45: API 문서의 가독성 개선 필요

현재 Operation 설명이 텍스트 블록으로 되어 있어 가독성이 떨어집니다. Swagger UI에서 더 보기 좋게 표현하기 위해 마크다운 형식으로 변경하면 좋을 것 같습니다.

 @Operation(summary = "프로필 설정 API",
     description =
         """
-        아래 두가지 요소에는 반드시 목록 중 하나를 입력해주세요
-        educationLevel(학력) 에 들어가야 할 목록입니다.
-        1. MIDDLE (중학교 이하) \s
-        2. HIGH (고등학교) \s
-        3. JUNIOR_COLLEGE (전문대학) \s
-        4. UNIVERSITY (대학교) \s
-        5. MASTER (석사) \s
-        6. DOCTOR (박사) \s
+        ## 필수 입력 사항
+        아래 두 가지 요소는 반드시 목록 중 하나를 선택해야 합니다.
 
-        educationStatus(수료 상태)에 들어가야 할 목록입니다.
-        1. ENROLLED (재학) \s
-        2. ON_LEAVE (휴학) \s
-        3. GRADUATED (졸업) \s
-        4. COMPLETED (수료) \s
+        ### educationLevel (학력)
+        - MIDDLE: 중학교 이하
+        - HIGH: 고등학교
+        - JUNIOR_COLLEGE: 전문대학
+        - UNIVERSITY: 대학교
+        - MASTER: 석사
+        - DOCTOR: 박사
-        직무는 직무 id를 넣어주세요.\s
-        ex) "job" : 1
+        ### educationStatus (수료 상태)
+        - ENROLLED: 재학
+        - ON_LEAVE: 휴학
+        - GRADUATED: 졸업
+        - COMPLETED: 수료
+        
+        ### job (직무)
+        직무 ID를 입력해주세요. 예: `"job": 1`
         """
src/main/java/UMC/career_mate/domain/answer/service/AnswerCommandService.java (3)

30-31: 멤버 리포지토리 의존성 주의
memberRepository 필드가 주입되어 있지만 현재 메서드 로직에서 직접 사용되지 않는 것으로 보입니다. 이후 기능 확장 대비라면 문제 없으나, 불필요하다면 제거하거나 활용 방안을 재검토하길 권장합니다.


33-63: 이미지 리스트와 AnswerGroup의 매핑 로직 확인 권장
answerGroupDTOList와 imageUrlList 크기가 일치하지 않을 경우, 특정 시퀀스에 이미지가 null로 매핑될 수 있습니다. 예상된 동작이라면 괜찮으나, 사용자 혼란을 줄이기 위해 입력 데이터 개수를 검증하는 로직을 추가하는 것을 고려해보세요.


65-102: 중복 제거 및 재사용성 향상을 위한 메서드 분리 제안
updateAnswerList 메서드가 saveAnswerList와 유사 로직을 갖고 있으므로, 공통 부문을 별도의 메서드나 유틸 클래스로 추출하여 코드 중복을 줄이는 방안을 고려해볼 수 있습니다.

src/main/java/UMC/career_mate/global/s3/service/S3Uploader.java (1)

1-34: S3 업로더 구현이 단순 명확하며 확장 가능성도 충분합니다.
UUID로 파일 충돌을 방지하고, ObjectMetadata를 설정하여 메타정보를 기록하는 방식이 적절합니다. 향후 접근 제어 정책(ACL)이나 버킷 폴더 구조가 필요하다면 putObject 과정에서 추가 설정을 고려해보시길 바랍니다.

src/main/java/UMC/career_mate/global/s3/config/S3Config.java (1)

23-33: S3 클라이언트 구성이 적절합니다.

AmazonS3Client 빈 설정이 올바르게 구현되어 있습니다. 다만, 다음 사항을 고려해보세요:

  • 타임아웃 설정 추가
  • 재시도 정책 구성
  • 에러 핸들링 추가
src/main/java/UMC/career_mate/domain/answer/Answer.java (1)

39-40: 시퀀스 필드의 유효성 검증이 필요합니다.

sequence 필드가 non-null로 설정되어 있지만, 추가적인 제약조건이 필요할 수 있습니다:

  • 최소값 검증 (@min(1))
  • 최대값 검증 (@max)
  • 중복 시퀀스 처리 로직
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba3b624 and 6a0a9b3.

📒 Files selected for processing (19)
  • build.gradle (1 hunks)
  • src/main/java/UMC/career_mate/domain/answer/Answer.java (2 hunks)
  • src/main/java/UMC/career_mate/domain/answer/controller/AnswerController.java (6 hunks)
  • src/main/java/UMC/career_mate/domain/answer/converter/AnswerConverter.java (3 hunks)
  • src/main/java/UMC/career_mate/domain/answer/dto/request/AnswerCreateOrUpdateDTO.java (1 hunks)
  • src/main/java/UMC/career_mate/domain/answer/dto/response/AnswerInfoListDTO.java (1 hunks)
  • src/main/java/UMC/career_mate/domain/answer/service/AnswerCommandService.java (2 hunks)
  • src/main/java/UMC/career_mate/domain/answer/service/AnswerQueryService.java (2 hunks)
  • src/main/java/UMC/career_mate/domain/content/controller/ContentController.java (2 hunks)
  • src/main/java/UMC/career_mate/domain/job/controller/JobController.java (2 hunks)
  • src/main/java/UMC/career_mate/domain/member/controller/MemberController.java (1 hunks)
  • src/main/java/UMC/career_mate/domain/planner/controller/PlannerController.java (1 hunks)
  • src/main/java/UMC/career_mate/domain/recruit/controller/RecruitController.java (2 hunks)
  • src/main/java/UMC/career_mate/domain/recruitScrap/controller/RecruitScrapController.java (2 hunks)
  • src/main/java/UMC/career_mate/domain/template/controller/TemplateController.java (2 hunks)
  • src/main/java/UMC/career_mate/global/response/exception/code/CommonErrorCode.java (0 hunks)
  • src/main/java/UMC/career_mate/global/s3/config/S3Config.java (1 hunks)
  • src/main/java/UMC/career_mate/global/s3/service/S3Uploader.java (1 hunks)
  • src/main/java/UMC/career_mate/global/security/SecurityController.java (2 hunks)
💤 Files with no reviewable changes (1)
  • src/main/java/UMC/career_mate/global/response/exception/code/CommonErrorCode.java
✅ Files skipped from review due to trivial changes (5)
  • src/main/java/UMC/career_mate/domain/job/controller/JobController.java
  • src/main/java/UMC/career_mate/domain/planner/controller/PlannerController.java
  • src/main/java/UMC/career_mate/domain/answer/dto/response/AnswerInfoListDTO.java
  • src/main/java/UMC/career_mate/global/security/SecurityController.java
  • src/main/java/UMC/career_mate/domain/recruitScrap/controller/RecruitScrapController.java
🔇 Additional comments (9)
src/main/java/UMC/career_mate/domain/template/controller/TemplateController.java (1)

10-10: Swagger 문서화가 적절히 추가되었습니다.

API 문서화를 위한 @tag 어노테이션이 일관성 있게 적용되었습니다.

Also applies to: 21-21

src/main/java/UMC/career_mate/domain/recruit/controller/RecruitController.java (1)

15-16: Swagger 문서화가 적절히 추가되었습니다.

API 문서화를 위한 @tag 어노테이션이 일관성 있게 적용되었습니다.

Also applies to: 28-28

src/main/java/UMC/career_mate/domain/member/controller/MemberController.java (1)

11-11: Swagger 문서화가 적절히 추가되었습니다.

API 문서화를 위한 @tag 어노테이션이 일관성 있게 적용되었습니다.

Also applies to: 17-17

src/main/java/UMC/career_mate/domain/content/controller/ContentController.java (2)

13-13: Swagger 문서화가 적절히 추가되었습니다.

API 문서화를 위한 @tag 어노테이션이 일관성 있게 적용되었습니다.

Also applies to: 21-21


28-46: API 문서의 요청 예시가 잘 작성되었습니다.

JSON 예시를 포함한 API 문서화가 매우 명확하게 작성되었습니다. 다른 API들도 이와 같은 형식으로 예시를 추가하면 좋을 것 같습니다.

src/main/java/UMC/career_mate/domain/answer/service/AnswerQueryService.java (1)

10-10: 사용되지 않는 import 확인 필요

현재 MemberRepository import가 추가되었지만 서비스 내에서 실제로 사용되지 않고 있습니다. 향후 구현을 위해 추가된 것인지 확인이 필요합니다.

다음 스크립트를 실행하여 MemberRepository의 사용 여부를 확인할 수 있습니다:

✅ Verification successful

사용되지 않는 MemberRepository import 관련 확인 결과

  • AnswerQueryService.java 파일 내에서 import된 UMC.career_mate.domain.member.repository.MemberRepository는 사용되지 않는 것으로 확인되었습니다.
  • 저장소 내 다른 파일(MemberService.java)에서는 해당 리포지토리가 사용되고 있으나, 리뷰 대상 파일에서는 별도로 참조되고 있지 않습니다.
  • 추후 사용 계획이 없다면 해당 import를 제거하거나, 현재 진행 중인 구현 사항과 관련된 주석 등으로 향후 작성될 기능임을 명시하는 것을 권장합니다.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# MemberRepository 사용 현황 확인
rg -l "memberRepository\." src/

Length of output: 104

src/main/java/UMC/career_mate/domain/answer/service/AnswerCommandService.java (1)

6-7: 새로 추가된 import 문들이 정상적으로 반영되었습니다.
새로운 DTO와 S3Uploader, MultipartFile, IOException 등이 적절히 import되어 있어 보입니다. 현재 코드 상에서 의도에 맞는 의존성만 추가된 것으로 판단됩니다.

Also applies to: 10-10, 15-15, 19-19, 21-23

src/main/java/UMC/career_mate/domain/answer/dto/request/AnswerCreateOrUpdateDTO.java (1)

5-5: DTO 구조 개편으로 가독성과 명확성이 증가했습니다.
AnswerList → AnswerGroupDTO, AnswerInfo → AnswerInfoDTO 등 이름이 더 직관적으로 개선되었으며, record를 통한 간결성도 유지되어 있습니다. 이 변경 사항은 데이터를 다뤄야 하는 서비스 로직에서도 확실한 의도를 전달할 수 있을 것으로 보입니다.

Also applies to: 7-7, 10-10

build.gradle (1)

70-74: 🛠️ Refactor suggestion

중복 및 버전 불일치 가능성 검토 필요

새롭게 추가된 AWS S3 관련 의존성 부분에서 동일한 라이브러리인 software.amazon.awssdk:s3가 두 가지 다른 버전(2.20.79와 2.30.2)으로 포함되어 있습니다. 이로 인해 의도하지 않은 버전 충돌이나 예기치 않은 동작이 발생할 수 있으므로, 실제로 두 버전을 모두 사용해야 하는지 검토하시기 바랍니다. 또한, com.amazonaws:aws-java-sdk-s3와 함께 사용할 경우, AWS SDK의 버전 간 충돌 가능성도 있으니 의존성 관리를 재점검하는 것이 좋겠습니다.

Comment on lines +16 to 23
public static Answer toAnswer(String content, Member member, Question question, long sequence) {
return Answer.builder()
.content(answerInfo.content())
.content(content)
.member(member)
.question(question)
.sequence(sequence)
.build();
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

toAnswer 메소드의 매개변수 유효성 검증이 필요합니다.

매개변수에 대한 null 체크가 없습니다. 다음과 같은 방어적 프로그래밍을 추가하는 것이 좋습니다:

 public static Answer toAnswer(String content, Member member, Question question, long sequence) {
+    Objects.requireNonNull(member, "Member cannot be null");
+    Objects.requireNonNull(question, "Question cannot be null");
+    if (content == null) {
+        content = "";
+    }
     return Answer.builder()
             .content(content)
             .member(member)
             .question(question)
             .sequence(sequence)
             .build();
 }
📝 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
public static Answer toAnswer(String content, Member member, Question question, long sequence) {
return Answer.builder()
.content(answerInfo.content())
.content(content)
.member(member)
.question(question)
.sequence(sequence)
.build();
}
public static Answer toAnswer(String content, Member member, Question question, long sequence) {
Objects.requireNonNull(member, "Member cannot be null");
Objects.requireNonNull(question, "Question cannot be null");
if (content == null) {
content = "";
}
return Answer.builder()
.content(content)
.member(member)
.question(question)
.sequence(sequence)
.build();
}

Comment on lines +231 to 234
@RequestPart("data") @Valid AnswerCreateOrUpdateDTO answerCreateOrUpdateDTO,
@RequestPart(value = "image", required = false) List<MultipartFile> imageFileList) throws IOException {
answerCommandService.updateAnswerList(member, answerCreateOrUpdateDTO, imageFileList);
return ApiResponse.onSuccess(UPDATE_ANSWER_LIST);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

updateAnswerList 메소드도 동일한 파일 검증이 필요합니다.

saveAnswerList와 동일한 파일 업로드 보안 및 유효성 검증을 적용해야 합니다.

Comment on lines +124 to +126
@RequestPart(value = "data") @Valid AnswerCreateOrUpdateDTO answerCreateOrUpdateDTO,
@RequestPart(value = "image", required = false) List<MultipartFile> imageFileList) throws IOException {
answerCommandService.saveAnswerList(member, answerCreateOrUpdateDTO, imageFileList);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

파일 업로드 처리에 대한 보안 및 유효성 검증이 필요합니다.

이미지 파일 업로드 처리 시 다음 사항들을 고려해야 합니다:

  1. 파일 크기 제한
  2. 허용된 파일 형식 검증
  3. 악성 파일 검사
  4. IOException에 대한 구체적인 예외 처리

다음과 같은 검증 로직을 추가하는 것을 권장합니다:

+    @PostMapping
+    public ApiResponse<CommonResultCode> saveAnswerList(
+        @LoginMember Member member,
+        @RequestPart(value = "data") @Valid AnswerCreateOrUpdateDTO answerCreateOrUpdateDTO,
+        @RequestPart(value = "image", required = false) List<MultipartFile> imageFileList) throws IOException {
+        
+        if (imageFileList != null) {
+            for (MultipartFile file : imageFileList) {
+                validateImageFile(file);
+            }
+        }
+        
+        answerCommandService.saveAnswerList(member, answerCreateOrUpdateDTO, imageFileList);
+        return ApiResponse.onSuccess(CREATE_ANSWER_LIST);
+    }
+
+    private void validateImageFile(MultipartFile file) {
+        if (file.getSize() > 5_000_000) { // 5MB
+            throw new IllegalArgumentException("File size exceeds maximum limit");
+        }
+        String contentType = file.getContentType();
+        if (contentType == null || !contentType.startsWith("image/")) {
+            throw new IllegalArgumentException("Only image files are allowed");
+        }
+    }
📝 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
@RequestPart(value = "data") @Valid AnswerCreateOrUpdateDTO answerCreateOrUpdateDTO,
@RequestPart(value = "image", required = false) List<MultipartFile> imageFileList) throws IOException {
answerCommandService.saveAnswerList(member, answerCreateOrUpdateDTO, imageFileList);
@PostMapping
public ApiResponse<CommonResultCode> saveAnswerList(
@LoginMember Member member,
@RequestPart(value = "data") @Valid AnswerCreateOrUpdateDTO answerCreateOrUpdateDTO,
@RequestPart(value = "image", required = false) List<MultipartFile> imageFileList) throws IOException {
if (imageFileList != null) {
for (MultipartFile file : imageFileList) {
validateImageFile(file);
}
}
answerCommandService.saveAnswerList(member, answerCreateOrUpdateDTO, imageFileList);
return ApiResponse.onSuccess(CREATE_ANSWER_LIST);
}
private void validateImageFile(MultipartFile file) {
if (file.getSize() > 5_000_000) { // 5MB
throw new IllegalArgumentException("File size exceeds maximum limit");
}
String contentType = file.getContentType();
if (contentType == null || !contentType.startsWith("image/")) {
throw new IllegalArgumentException("Only image files are allowed");
}
}

Copy link
Contributor

@jpark0506 jpark0506 left a comment

Choose a reason for hiding this comment

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

LGTM~ 고생 많으셨습니다

Comment on lines +36 to +43
List<String> imageUrlList = new ArrayList<>();
if (imageFileList != null && !imageFileList.isEmpty()) {
for (MultipartFile imageFile : imageFileList) {
if (!imageFile.isEmpty()) {
imageUrlList.add(s3Uploader.uploadImage(imageFile)); // S3 업로드 후 URL 리스트에 저장
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

S3 업로드를 구현하시느라 수고 많으셨습니다!

개인마다 스타일이 다르긴 하지만, 저 같은 경우에는 여러 파일을 업로드할 때 uploadImageList 메서드를 따로 만들어 Uploader 클래스에 위임하는 방식을 선호하는데요
이렇게 하면 단일 업로드와 다중 업로드의 역할이 명확하게 분리되고, SRP와 코드의 가독성과 유지보수성이 향상되는 장점이 있습니다.

현재 방식도 충분히 깔끔하지만, 만약 Uploader 클래스에서 단일 파일 업로드(uploadImage)와 리스트 업로드(uploadImageList)를 분리하면 재사용성이 높아질 것 같습니다!
현재 코드도 이미 잘 작성되어 있지만, 유지보수를 고려한다면 이런 방식도 한 번 고려해 보시면 좋을 것 같습니다.

고생 많으셨습니다~!

Copy link
Member Author

Choose a reason for hiding this comment

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

넵! 좋은 생각인 거 같습니다! 추후에 코드 리팩토링할 때, 반영하겠습니다!😀

Copy link
Member

@momuzzi momuzzi left a comment

Choose a reason for hiding this comment

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

LGTM~~ 전체 도메인 스웨거 tag 설정 고생하셨습니다!

@jjeongdong jjeongdong merged commit 723f6a5 into develop Feb 2, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants