Skip to content

Feature/file upload download#30

Closed
NNIIE wants to merge 9 commits intomainfrom
feature/File_Upload_Download
Closed

Feature/file upload download#30
NNIIE wants to merge 9 commits intomainfrom
feature/File_Upload_Download

Conversation

@NNIIE
Copy link
Owner

@NNIIE NNIIE commented Jun 26, 2025

Summary by CodeRabbit

  • 신규 기능

    • 관리자 페이지에서 상품 이미지 업로드 및 썸네일 저장 기능이 추가되었습니다.
    • S3에 직접 이미지를 업로드할 수 있는 프리사인드 URL 발급 API가 제공됩니다.
    • 이미지 업로드 후, 썸네일 및 이미지 정보가 자동으로 데이터베이스에 저장됩니다.
    • 이미지 관련 신규 엔티티 및 관리 기능이 도입되었습니다.
  • 버그 수정

    • 없음
  • 문서

    • 이미지 업로드/다운로드 방식 및 쇼핑몰 부하 테스트 시나리오 문서가 추가되었습니다.
  • 기타

    • AWS S3, SQS 연동을 위한 설정이 추가되었습니다.
    • 보안 설정이 일부 경로에 대해 개선되었습니다.

@NNIIE NNIIE self-assigned this Jun 26, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jun 26, 2025

Walkthrough

이번 변경 사항은 상품 이미지 업로드 및 저장 기능을 위한 전체 백엔드 인프라를 도입합니다. S3 업로드용 프리사인드 URL 발급, 이미지 메타데이터 저장, 썸네일 관리, AWS SQS 메시지 큐 연동, 관련 JPA 엔티티 및 레포지토리, 예외 처리, 그리고 상세한 부하 테스트 및 파일 업로드/다운로드 설계 문서가 추가되었습니다.

Changes

파일/경로 그룹 변경 요약
api-admin/build.gradle, file-storage/build.gradle, settings.gradle 파일 스토리지, AWS SQS/S3 연동 등 신규 의존성 및 모듈 추가, 멀티프로젝트 구성 갱신
api-admin/src/main/java/com/admin/AdminApplication.java 컴포넌트 스캔 범위에 com.s3 패키지 추가
api-admin/src/main/java/com/admin/config/SecurityConfig.java /admin/image/** 경로 인증 예외 허용
api-admin/src/main/java/com/admin/exception/... MESSAGE_QUEUE_ERROR 에러코드, ExternalException 클래스, 핸들러 메서드 추가
api-admin/src/main/java/com/admin/mq/... SQS 메시지 리스너, 메시지 DTO(레코드) 2종 추가
api-admin/src/main/java/com/admin/service/ImageService.java 이미지 업로드 URL 생성, 이미지/썸네일 저장 서비스 구현
api-admin/src/main/java/com/admin/web/controller/ImageController.java 이미지 업로드 URL 발급 API 컨트롤러 신설
api-admin/src/main/java/com/admin/web/request/image/... 이미지 저장/업로드 요청 DTO 클래스 2종 추가
api-admin/src/main/java/com/admin/web/response/image/... 이미지 저장/업로드 응답 DTO 레코드 2종 추가
api-admin/src/main/resources/application.yml AWS S3, SQS, 자격증명 등 환경설정 추가
data/src/main/java/com/relation/productimage/..., data/src/main/java/com/relation/productthumbnail/... 상품 이미지/썸네일 JPA 엔티티 및 레포지토리 2종 추가
data/src/main/resources/schema.sql product_image, product_thumbnail 테이블 및 인덱스 DDL 추가
file-storage/src/main/java/com/s3/service/S3UploadService.java S3 프리사인드 URL 생성 서비스 클래스 신설
support/src/main/java/com/support/response/PreSignedUrlResponse.java 프리사인드 URL 응답 레코드 추가
docs/study/load-test.md, docs/study/up-down-load.md 부하테스트 시나리오, 파일 업로드/다운로드 설계 문서 추가

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant ImageController
    participant ImageService
    participant S3UploadService

    Client->>ImageController: POST /admin/image/upload-url (UploadUrlRequest)
    ImageController->>ImageService: generateUploadUrl(request)
    ImageService->>S3UploadService: generateUploadUrl(productId, fileName, contentType)
    S3UploadService-->>ImageService: PreSignedUrlResponse
    ImageService-->>ImageController: PreSignedUrlResponse
    ImageController-->>Client: 200 OK (PreSignedUrlResponse)
Loading
sequenceDiagram
    participant S3 (Lambda)
    participant SQS
    participant ImageProcessListener
    participant ImageService
    participant ProductImageRepository
    participant ProductThumbnailRepository

    S3->>SQS: 이미지 처리 완료 메시지 전송
    SQS->>ImageProcessListener: 메시지 수신 (ImageProcessedMessage)
    ImageProcessListener->>ImageService: saveImages(message)
    ImageService->>ProductImageRepository: save(ProductImage)
    loop 썸네일별
        ImageService->>ProductThumbnailRepository: save(ProductThumbnail)
    end
Loading

Poem

🐰
새싹처럼 피어난 이미지 저장,
S3에 토끼가 URL을 뚝딱!
썸네일도 척척, 큐에서 척척,
DB에 저장해 흔적 남기네.
문서와 코드 모두 풍성해진 오늘,
개발자 토끼는 춤을 추네!
☁️📦🖼️

✨ Finishing Touches
  • 📝 Generate Docstrings

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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

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

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.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @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.

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

🔭 Outside diff range comments (1)
docs/study/load-test.md (1)

1-69: 마크다운 포맷 개선 필요

정적 분석 도구에서 여러 마크다운 포맷 이슈를 발견했습니다. 가독성 향상을 위해 헤딩 주변 공백 줄 추가와 리스트 포맷 개선이 필요합니다.

주요 개선사항:

  • 헤딩 앞뒤로 공백 줄 추가
  • 리스트 앞뒤로 공백 줄 추가
  • <br> 태그 대신 마크다운 표준 방식 사용
 # 부하테스트 상세 시나리오
+
 ## 일반 시나리오
+
 일반적인 상황의 쇼핑몰을 이용하는 전체 과정을 시뮬레이션
+
 ### 구성
+
 - 초기 사용자 수: 10명
 - 사용자 증가율: 5분동안 100명까지 증가
 - 지속시간: 10분
 - 최대 동시 사용자 수: 100명
+
 ### 상세
+
 - 회원가입 (10%)
   - `POST /user/signUp`
 - 로그인 (90%)
   - `POST /user/signIn`
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b9f09a and 3ddc8b0.

📒 Files selected for processing (27)
  • api-admin/build.gradle (1 hunks)
  • api-admin/src/main/java/com/admin/AdminApplication.java (1 hunks)
  • api-admin/src/main/java/com/admin/config/SecurityConfig.java (1 hunks)
  • api-admin/src/main/java/com/admin/exception/ErrorCode.java (1 hunks)
  • api-admin/src/main/java/com/admin/exception/ExternalException.java (1 hunks)
  • api-admin/src/main/java/com/admin/exception/GlobalExceptionHandler.java (1 hunks)
  • api-admin/src/main/java/com/admin/mq/ImageProcessListener.java (1 hunks)
  • api-admin/src/main/java/com/admin/mq/ImageProcessedMessage.java (1 hunks)
  • api-admin/src/main/java/com/admin/mq/ProductThumbnailMessage.java (1 hunks)
  • api-admin/src/main/java/com/admin/service/ImageService.java (1 hunks)
  • api-admin/src/main/java/com/admin/web/controller/ImageController.java (1 hunks)
  • api-admin/src/main/java/com/admin/web/request/image/SaveImageRequest.java (1 hunks)
  • api-admin/src/main/java/com/admin/web/request/image/UploadUrlRequest.java (1 hunks)
  • api-admin/src/main/java/com/admin/web/response/image/SaveImageResponse.java (1 hunks)
  • api-admin/src/main/java/com/admin/web/response/image/UploadUrlResponse.java (1 hunks)
  • api-admin/src/main/resources/application.yml (1 hunks)
  • data/src/main/java/com/relation/productimage/ProductImage.java (1 hunks)
  • data/src/main/java/com/relation/productimage/ProductImageRepository.java (1 hunks)
  • data/src/main/java/com/relation/productthumbnail/ProductThumbnail.java (1 hunks)
  • data/src/main/java/com/relation/productthumbnail/ProductThumbnailRepository.java (1 hunks)
  • data/src/main/resources/schema.sql (2 hunks)
  • docs/study/load-test.md (1 hunks)
  • docs/study/up-down-load.md (1 hunks)
  • file-storage/build.gradle (1 hunks)
  • file-storage/src/main/java/com/s3/service/S3UploadService.java (1 hunks)
  • settings.gradle (1 hunks)
  • support/src/main/java/com/support/response/PreSignedUrlResponse.java (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
api-admin/src/main/java/com/admin/service/ImageService.java (1)
file-storage/src/main/java/com/s3/service/S3UploadService.java (1)
  • Service (14-66)
file-storage/src/main/java/com/s3/service/S3UploadService.java (1)
api-admin/src/main/java/com/admin/service/ImageService.java (1)
  • Service (17-64)
🪛 GitHub Actions: PR CI Check
api-admin/src/main/java/com/admin/exception/ErrorCode.java

[error] 1-1: Gradle task ':api-admin:test' failed due to multiple test failures related to ApplicationContext initialization errors.

api-admin/src/main/java/com/admin/web/request/image/SaveImageRequest.java

[error] 1-1: Gradle task ':api-admin:test' failed due to multiple test failures related to ApplicationContext initialization errors.

api-admin/src/main/java/com/admin/mq/ProductThumbnailMessage.java

[error] 1-1: Gradle task ':api-admin:test' failed due to multiple test failures related to ApplicationContext initialization errors.

api-admin/src/main/resources/application.yml

[error] 1-1: Gradle task ':api-admin:test' failed due to multiple test failures related to ApplicationContext initialization errors.

api-admin/src/main/java/com/admin/config/SecurityConfig.java

[error] 1-1: Gradle task ':api-admin:test' failed due to multiple test failures related to ApplicationContext initialization errors.

api-admin/src/main/java/com/admin/web/response/image/UploadUrlResponse.java

[error] 1-1: Gradle task ':api-admin:test' failed due to multiple test failures related to ApplicationContext initialization errors.

api-admin/build.gradle

[error] 1-1: Gradle task ':api-admin:test' failed due to multiple test failures related to ApplicationContext initialization errors.

api-admin/src/main/java/com/admin/AdminApplication.java

[error] 1-1: Gradle task ':api-admin:test' failed due to multiple test failures related to ApplicationContext initialization errors.

api-admin/src/main/java/com/admin/exception/GlobalExceptionHandler.java

[error] 1-1: Gradle task ':api-admin:test' failed due to multiple test failures related to ApplicationContext initialization errors.

api-admin/src/main/java/com/admin/exception/ExternalException.java

[error] 1-1: Gradle task ':api-admin:test' failed due to multiple test failures related to ApplicationContext initialization errors.

api-admin/src/main/java/com/admin/service/ImageService.java

[error] 1-1: Gradle task ':api-admin:test' failed due to multiple test failures related to ApplicationContext initialization errors.

api-admin/src/main/java/com/admin/web/response/image/SaveImageResponse.java

[error] 1-1: Gradle task ':api-admin:test' failed due to multiple test failures related to ApplicationContext initialization errors.

api-admin/src/main/java/com/admin/web/request/image/UploadUrlRequest.java

[error] 1-1: Gradle task ':api-admin:test' failed due to multiple test failures related to ApplicationContext initialization errors.

api-admin/src/main/java/com/admin/mq/ImageProcessedMessage.java

[error] 1-1: Gradle task ':api-admin:test' failed due to multiple test failures related to ApplicationContext initialization errors.

api-admin/src/main/java/com/admin/web/controller/ImageController.java

[error] 1-1: Gradle task ':api-admin:test' failed due to multiple test failures related to ApplicationContext initialization errors.

api-admin/src/main/java/com/admin/mq/ImageProcessListener.java

[error] 1-1: Gradle task ':api-admin:test' failed due to multiple test failures related to ApplicationContext initialization errors.

🪛 markdownlint-cli2 (0.17.2)
docs/study/up-down-load.md

1-1: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


3-3: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)


10-10: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


10-10: Multiple top-level headings in the same document
null

(MD025, single-title, single-h1)


12-12: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)


19-19: Inline HTML
Element: br

(MD033, no-inline-html)


21-21: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


22-22: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


22-22: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


23-23: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)


28-28: Trailing spaces
Expected: 0 or 2; Actual: 1

(MD009, no-trailing-spaces)


34-34: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


36-36: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)


41-41: Inline HTML
Element: br

(MD033, no-inline-html)


43-43: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


44-44: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


44-44: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


45-45: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)


50-50: Inline HTML
Element: br

(MD033, no-inline-html)


52-52: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


52-52: Multiple top-level headings in the same document
null

(MD025, single-title, single-h1)


53-53: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)

docs/study/load-test.md

1-1: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


2-2: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


2-2: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


4-4: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


4-4: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


5-5: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)


8-8: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)


9-9: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


9-9: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


10-10: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)


26-26: Inline HTML
Element: br

(MD033, no-inline-html)


28-28: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


30-30: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


30-30: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


31-31: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)


32-32: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)


33-33: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


33-33: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


34-34: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)


46-46: Inline HTML
Element: br

(MD033, no-inline-html)


48-48: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


50-50: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


50-50: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


51-51: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)


53-53: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)


54-54: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


54-54: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


55-55: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)

🔇 Additional comments (26)
file-storage/build.gradle (2)

1-2: 라이브러리 모듈 설정이 올바르게 구성되었습니다.

bootJar.enabled = falsejar.enabled = true 설정은 공유 라이브러리 모듈에 적합한 구성입니다.


8-9: ```shell
#!/bin/bash

프로젝트에 사용된 Spring Boot 버전 조회

rg "id 'org.springframework.boot'" -n .
grep -R "springBootVersion" -n .


</details>
<details>
<summary>settings.gradle (1)</summary>

`6-6`: **file-storage 모듈 추가가 올바르게 구성되었습니다.**

멀티 모듈 Gradle 프로젝트에 새로운 `file-storage` 모듈을 추가하는 표준적인 방법입니다.

</details>
<details>
<summary>api-admin/src/main/java/com/admin/exception/ErrorCode.java (1)</summary>

`20-20`: **메시지 큐 오류 코드가 올바르게 추가되었습니다.**

`MESSAGE_QUEUE_ERROR` 오류 코드가 기존 패턴과 일치하며, SQS 메시지 큐 오류 처리를 위한 적절한 추가입니다.

</details>
<details>
<summary>api-admin/src/main/java/com/admin/AdminApplication.java (1)</summary>

`15-16`: **컴포넌트 스캔 설정이 올바르게 업데이트되었습니다.**

새로운 `file-storage` 모듈의 `"com.s3"` 패키지를 컴포넌트 스캔에 추가하여 S3UploadService 빈이 정상적으로 등록될 수 있도록 구성했습니다.



파이프라인 실패 해결을 위해 다음 스크립트로 모듈 통합 상태를 확인해주세요:

```shell
#!/bin/bash
# file-storage 모듈의 S3 관련 클래스들이 올바르게 구성되었는지 확인
echo "S3 관련 클래스 확인:"
fd -e java . file-storage/ --exec grep -l "com.s3"

echo "S3UploadService 클래스 존재 확인:"
fd "S3UploadService.java" file-storage/

echo "애플리케이션 설정에서 AWS 관련 설정 확인:"
fd "application" api-admin/src/main/resources/ --exec grep -l "aws\|s3\|sqs"
api-admin/build.gradle (2)

7-7: 새로운 파일 저장소 모듈 의존성 추가가 적절합니다.

:file-storage 모듈 의존성 추가가 올바르게 구성되었습니다.


12-13: 아래 스크립트를 실행해 application.yml에 정의된 AWS 관련 프로퍼티(Region, 자격 증명, SQS 설정 등)가 제대로 설정되어 있는지 확인해주세요:

#!/bin/bash
echo "=== AWS 관련 프로퍼티 상세 확인 (application.yml) ==="
grep -nE "spring\.cloud\.aws|region|accessKeyId|secretKey|sqs" api-admin/src/main/resources/application.yml || echo "AWS 설정 프로퍼티 없음"

이 결과를 토대로 누락된 설정 여부를 파악한 후, 필요 시 spring.cloud.aws.region.static 등 필수 프로퍼티를 추가해주세요.

api-admin/src/main/resources/application.yml (2)

6-9: AWS 자격 증명 설정이 올바르게 구성되었습니다.

환경 변수를 통한 AWS 자격 증명 설정이 보안 모범 사례를 따르고 있습니다.


6-17: 테스트 환경용 AWS 설정이 누락되어 파이프라인 실패가 발생할 수 있습니다.

현재 설정은 로컬 환경용으로만 구성되어 있어, 테스트 실행 시 AWS 설정이 누락되어 ApplicationContext 초기화 오류가 발생할 수 있습니다.

다음 스크립트로 테스트 설정을 확인하고 누락된 설정을 찾아보세요:

#!/bin/bash
# 테스트용 설정 파일 확인
echo "=== 테스트 설정 파일들 ==="
fd "application.*test" --type f

echo "=== 테스트에서 AWS 설정 Mock 여부 확인 ==="
rg -r '$1' '@MockBean.*[Ss]3|@TestConfiguration.*[Aa]ws' --type java

echo "=== 테스트 실행 시 필요한 환경 변수 확인 ==="
rg 'AWS_.*_KEY|AWS_.*_QUEUE' src/test --type yaml --type yml --type properties || echo "테스트용 AWS 설정 없음"
data/src/main/java/com/relation/productthumbnail/ProductThumbnailRepository.java (1)

5-6: 표준적인 JPA 리포지토리 구현이 적절합니다.

기본 CRUD 기능을 위한 표준적인 Spring Data JPA 패턴을 잘 따르고 있습니다.

향후 비즈니스 요구사항에 따라 다음과 같은 쿼리 메서드 추가를 고려해볼 수 있습니다:

  • findByProductId(Long productId)
  • findBySizeAndProductId(String size, Long productId)
  • deleteByProductId(Long productId)
support/src/main/java/com/support/response/PreSignedUrlResponse.java (1)

3-8: 잘 구조화된 응답 DTO입니다.

S3 프리사인드 URL 관련 데이터를 담는 record 구조가 적절합니다. 필드명도 명확하고 의미가 잘 전달됩니다.

참고: UploadUrlResponse와 동일한 구조를 가지고 있으니 향후 코드 중복 최적화를 고려해보세요.

api-admin/src/main/java/com/admin/exception/ExternalException.java (2)

5-14: 잘 설계된 커스텀 예외 클래스입니다.

외부 시스템 연동 오류를 위한 예외 클래스가 우수하게 구현되었습니다:

  • RuntimeException 확장으로 적절한 예외 계층 구조
  • ErrorCode 캡슐화를 통한 구조화된 오류 처리
  • final 키워드로 불변성 보장
  • Lombok 활용으로 코드 간소화

이 패턴은 메시지 큐 처리 등 외부 시스템 연동에서 발생할 수 있는 다양한 오류를 체계적으로 관리할 수 있게 해줍니다.


1-14: 파이프라인 실패 원인 조사가 필요합니다.

ApplicationContext 초기화 오류가 발생하고 있습니다. 이는 새로 추가된 컴포넌트들의 의존성 설정 문제일 가능성이 높습니다.

다음 스크립트로 테스트 실패 원인을 조사해보세요:

#!/bin/bash
# 설명: 새로 추가된 빈들의 의존성 문제 조사

# AWS 관련 설정 파일들 확인
fd "application.*\.yml" --exec cat {} \;

# 새로 추가된 컴포넌트들의 어노테이션 확인
rg -A 5 "@Component|@Service|@Repository|@Controller" --type java

# SQS 관련 설정 확인
rg -A 10 -B 5 "SQS|sqs" --type java
rg -A 10 -B 5 "aws" --type yaml

# 테스트 프로파일 설정 확인
fd "application-test.*" --exec cat {} \;

AWS SQS, S3 관련 설정이 테스트 환경에서 제대로 구성되지 않았을 가능성이 높습니다.

api-admin/src/main/java/com/admin/web/response/image/SaveImageResponse.java (1)

3-8: 코드가 적절하게 작성되었습니다

이미지 저장 응답을 위한 record 클래스가 간결하고 명확하게 정의되어 있습니다. 필드명과 주석이 명확하여 가독성이 좋습니다.

api-admin/src/main/java/com/admin/mq/ImageProcessedMessage.java (1)

7-23: 메시지 구조가 잘 설계되었습니다

Record 클래스와 @JsonProperty 어노테이션이 적절히 사용되어 JSON 직렬화/역직렬화가 원활하게 처리될 것입니다. 불변 객체로 설계되어 안전합니다.

data/src/main/java/com/relation/productthumbnail/ProductThumbnail.java (1)

11-42: JPA 엔티티가 적절하게 설계되었습니다

엔티티 구조가 잘 설계되어 있고, Lazy loading과 Builder 패턴이 적절히 적용되었습니다.

api-admin/src/main/java/com/admin/web/controller/ImageController.java (2)

26-34: 인증 비활성화로 인한 보안 위험을 검토하세요

현재 @CurrentAdmin이 주석처리되어 있어서 인증 없이 누구나 이미지 업로드 URL을 생성할 수 있습니다. 이는 다음과 같은 보안 위험을 초래할 수 있습니다:

  1. 무분별한 S3 리소스 사용
  2. 악의적인 파일 업로드 시도
  3. 시스템 리소스 남용

인증이 의도적으로 비활성화된 것인지 확인하고, 만약 그렇다면 다른 보안 조치를 고려해보세요:

 public ResponseEntity<PreSignedUrlResponse> generateUploadUrl(
     @RequestBody @Valid final UploadUrlRequest request
-//        @CurrentAdmin final Admin admin
+    @CurrentAdmin final Admin admin
 ) {
-    final PreSignedUrlResponse response = imageService.generateUploadUrl(request);
+    final PreSignedUrlResponse response = imageService.generateUploadUrl(request, admin);
     return ResponseEntity.ok(response);
 }

혹은 rate limiting, IP 제한, API 키 등의 대안적 보안 조치를 구현하세요.


1-36: 파이프라인 실패와 관련된 의존성 확인이 필요합니다

ApplicationContext 초기화 오류가 발생하고 있습니다. 이는 새로 추가된 ImageService나 관련 빈들의 의존성 문제일 수 있습니다.

다음 스크립트로 의존성 문제를 확인해보세요:

#!/bin/bash
# ImageService와 관련 빈들의 의존성 확인
echo "=== ImageService 클래스 찾기 ==="
fd -t f -e java -x grep -l "class ImageService" {} \;

echo "=== ImageService의 의존성 확인 ==="
rg -A 10 -B 2 "class ImageService"

echo "=== S3UploadService 의존성 확인 ==="  
rg -A 5 -B 2 "S3UploadService"

echo "=== 설정 파일에서 AWS 관련 설정 확인 ==="
fd -t f -e yml -e yaml -e properties -x grep -l "aws\|s3\|sqs" {} \;
data/src/main/java/com/relation/productimage/ProductImage.java (1)

34-45: 빌더 패턴 구현이 잘 되어 있습니다

엔티티의 빌더 패턴이 적절히 구현되어 있고, 필수 필드들이 생성자에 포함되어 있어 객체 생성 시 일관성을 보장합니다.

api-admin/src/main/java/com/admin/mq/ImageProcessListener.java (1)

1-1: 파이프라인 실패 원인 확인 필요

ApplicationContext 초기화 오류가 발생하고 있습니다. SQS 관련 설정이나 의존성 주입 문제일 가능성이 있습니다.

다음 스크립트로 SQS 관련 설정과 의존성을 확인해보겠습니다:

#!/bin/bash
# SQS 관련 설정 및 의존성 확인

# SQS 설정 확인
echo "=== SQS 설정 확인 ==="
fd -t f -e yml -e yaml -e properties | xargs grep -l "aws.sqs" 2>/dev/null || echo "SQS 설정 파일 없음"

# SQS 의존성 확인
echo "=== SQS 의존성 확인 ==="
fd -t f -n "build.gradle" | xargs grep -l "spring-cloud-aws\|aws-java-sdk" 2>/dev/null || echo "AWS 의존성 없음"

# 컴포넌트 스캔 설정 확인
echo "=== 컴포넌트 스캔 설정 확인 ==="
ast-grep --pattern '@ComponentScan($$$)' || echo "컴포넌트 스캔 설정 없음"
api-admin/src/main/java/com/admin/service/ImageService.java (1)

26-32: 업로드 URL 생성 로직이 적절합니다

S3UploadService를 통한 업로드 URL 생성 로직이 간결하고 명확합니다.

file-storage/src/main/java/com/s3/service/S3UploadService.java (2)

47-51: Presigned URL 생성 로직이 적절합니다

AWS SDK v2를 사용한 presigned URL 생성이 올바르게 구현되어 있고, 10분 만료 시간도 적절합니다.


26-27: 썸네일 버킷 변수 사용 검토

thumbnailBucket 변수가 선언되어 있지만 현재 메서드에서는 사용되지 않고 있습니다. 향후 썸네일 업로드 기능에서 사용될 예정인지 확인해보세요.

다음 스크립트로 썸네일 버킷 사용 현황을 확인해보겠습니다:

#!/bin/bash
# 썸네일 버킷 사용 현황 확인

echo "=== thumbnailBucket 사용 현황 ==="
rg -n "thumbnailBucket" --type java

echo "=== 썸네일 관련 메서드 확인 ==="
ast-grep --pattern 'public $_ $_($$$) {
  $$$
  thumbnailBucket
  $$$
}'
docs/study/load-test.md (2)

28-45: 관리자 대량 상품 시나리오가 잘 설계되어 있습니다

10명의 관리자가 각각 100개씩 상품을 등록하는 시나리오가 현실적이고 측정 가능한 메트릭으로 구성되어 있습니다. 대기 시간 설정도 적절합니다.


15-19: 구현되지 않은 API 확인 필요

문서에서 언급하는 유저용 상품 검색 및 상세 조회 API가 아직 개발되지 않았다고 명시되어 있습니다. 부하 테스트 실행 전에 이러한 API들의 구현이 완료되어야 합니다.

다음 스크립트로 현재 구현된 API 엔드포인트를 확인해보겠습니다:

#!/bin/bash
# 현재 구현된 API 엔드포인트 확인

echo "=== 유저 API 컨트롤러 확인 ==="
fd -t f -n "*Controller.java" | grep -v admin | head -5

echo "=== 상품 관련 API 엔드포인트 확인 ==="
ast-grep --pattern '@RequestMapping($$$)
class $_Controller {
  $$$
}'

echo "=== GET 매핑 메서드 확인 ==="
ast-grep --pattern '@GetMapping($$$)
public $_ $_($$$) {
  $$$
}'
data/src/main/resources/schema.sql (1)

101-112: ```shell
#!/bin/bash

product 테이블 생성 및 PK 정의 확인

rg -n "CREATE TABLE product" -A 5 data/src/main/resources/schema.sql


</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment on lines +3 to +4
public class SaveImageRequest {
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

빈 요청 클래스가 문제를 일으킬 수 있습니다.

현재 SaveImageRequest 클래스가 완전히 비어있어 Spring Boot 애플리케이션에서 다음과 같은 문제를 일으킬 수 있습니다:

  • JSON 직렬화/역직렬화 오류
  • REST 컨트롤러에서 사용 시 바인딩 실패
  • 파이프라인 실패의 ApplicationContext 초기화 오류와 연관 가능성

다음 중 하나의 해결책을 적용해주세요:

 public class SaveImageRequest {
+    // TODO: 필요한 필드들을 추가하거나 클래스 사용을 제거
 }

또는 실제 필드가 있다면:

 public class SaveImageRequest {
+    private String imageUrl;
+    private String originalFileName;
+    // getter, setter 등 필요한 메서드들
 }
🤖 Prompt for AI Agents
In api-admin/src/main/java/com/admin/web/request/image/SaveImageRequest.java at
lines 3 to 4, the SaveImageRequest class is currently empty, which can cause
JSON serialization/deserialization errors and binding failures in Spring Boot.
To fix this, either add the necessary fields with appropriate getters and
setters to represent the expected request data, or if no fields are needed, add
a comment or annotation to clarify its purpose and prevent framework errors.

Comment on lines +10 to +17
s3:
region: ap-northeast-2
bucket:
original: mini-shopping-images-origin
thumbnail: mini-shopping-images-thumbnail
sqs:
queue:
image-processed: https://sqs.ap-northeast-2.amazonaws.com/663530037219/mini-shopping-image-processed
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

하드코딩된 AWS 리소스 정보를 환경 변수로 변경해야 합니다.

SQS 큐 URL에 AWS 계정 ID가 하드코딩되어 있고, S3 버킷 이름도 고정되어 있습니다. 이는 보안상 위험하며 환경별 배포를 어렵게 만듭니다.

다음과 같이 수정하는 것을 권장합니다:

-  s3:
-    region: ap-northeast-2
-    bucket:
-      original: mini-shopping-images-origin
-      thumbnail: mini-shopping-images-thumbnail
-  sqs:
-    queue:
-      image-processed: https://sqs.ap-northeast-2.amazonaws.com/663530037219/mini-shopping-image-processed
+  s3:
+    region: ${AWS_REGION:ap-northeast-2}
+    bucket:
+      original: ${AWS_S3_BUCKET_ORIGINAL:mini-shopping-images-origin}
+      thumbnail: ${AWS_S3_BUCKET_THUMBNAIL:mini-shopping-images-thumbnail}
+  sqs:
+    queue:
+      image-processed: ${AWS_SQS_IMAGE_PROCESSED_QUEUE_URL}
📝 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
s3:
region: ap-northeast-2
bucket:
original: mini-shopping-images-origin
thumbnail: mini-shopping-images-thumbnail
sqs:
queue:
image-processed: https://sqs.ap-northeast-2.amazonaws.com/663530037219/mini-shopping-image-processed
s3:
region: ${AWS_REGION:ap-northeast-2}
bucket:
original: ${AWS_S3_BUCKET_ORIGINAL:mini-shopping-images-origin}
thumbnail: ${AWS_S3_BUCKET_THUMBNAIL:mini-shopping-images-thumbnail}
sqs:
queue:
image-processed: ${AWS_SQS_IMAGE_PROCESSED_QUEUE_URL}
🤖 Prompt for AI Agents
In api-admin/src/main/resources/application.yml around lines 10 to 17, the AWS
resource identifiers such as the SQS queue URL with the hardcoded AWS account ID
and the S3 bucket names are hardcoded, which poses security risks and reduces
deployment flexibility. Replace these hardcoded values with environment variable
placeholders so that the actual values can be injected at runtime from
environment variables, improving security and enabling easier
environment-specific configuration.

Comment on lines +1 to +6
package com.relation.productimage;

import org.springframework.data.jpa.repository.JpaRepository;

public interface ProductImageRepository extends JpaRepository<ProductImage, Long> {
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

기본적인 JPA 레포지토리 구조가 올바르게 구성되었습니다.

표준 Spring Data JPA 패턴을 따르고 있으며, 패키지 구조도 적절합니다. 향후 필요에 따라 커스텀 쿼리 메서드를 추가할 수 있습니다.

향후 확장성을 위해 다음과 같은 메서드들을 고려해볼 수 있습니다:

 public interface ProductImageRepository extends JpaRepository<ProductImage, Long> {
+    List<ProductImage> findByProductId(Long productId);
+    Page<ProductImage> findByProductIdOrderByCreatedAtDesc(Long productId, Pageable pageable);
+    boolean existsByOriginalFileName(String originalFileName);
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In data/src/main/java/com/relation/productimage/ProductImageRepository.java
lines 1 to 6, the basic JPA repository structure is correctly set up. To improve
future extensibility, consider adding custom query methods tailored to your
domain needs, such as finding images by product ID or filtering by image type.
Implement these methods by defining query method signatures following Spring
Data JPA conventions within this interface.

Comment on lines +55 to +56
"/admin/signUp",
"/admin/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

🧩 Verification correct

이미지 엔드포인트의 인증 없는 접근 허용에 대한 보안 검토가 필요합니다.

/admin/image/** 경로에 대한 인증 없는 접근을 허용하는 것은 보안상 위험을 초래할 수 있습니다. 특히 관리자 API에서 이미지 관련 모든 기능에 공개 접근을 허용하는 것은 과도할 수 있습니다.

더 세분화된 경로 설정을 고려해보세요:

-                "/admin/signUp",
-                "/admin/image/**"
+                "/admin/signUp",
+                "/admin/image/upload-url"

다음 스크립트로 실제 노출되는 이미지 관련 엔드포인트를 확인하세요:


🏁 Script executed:

#!/bin/bash
# 이미지 관련 컨트롤러와 엔드포인트 확인
echo "=== 이미지 관련 컨트롤러 확인 ==="
rg -r '$1' '@.*Mapping.*image|@RestController.*[Ii]mage' --type java -A 10

echo "=== /admin/image 경로 매핑 확인 ==="
rg -r '$1' '@.*Mapping.*"/admin/image' --type java -A 5

echo "=== 보안이 필요한 이미지 관련 메서드 확인 ==="
rg -r '$1' 'delete.*[Ii]mage|remove.*[Ii]mage|update.*[Ii]mage' --type java -A 3

Length of output: 2123


이미지 엔드포인트의 인증 없는 접근 허용에 대한 보안 검토가 필요합니다.

/admin/image/** 경로에 대한 인증 없는 접근을 허용하는 것은 보안상 위험을 초래할 수 있습니다. 특히 관리자 API에서 이미지 관련 모든 기능에 공개 접근을 허용하는 것은 과도할 수 있습니다.

더 세분화된 경로 설정을 고려해보세요:

-                "/admin/signUp",
-                "/admin/image/**"
+                "/admin/signUp",
+                "/admin/image/upload-url"

다음 스크립트로 실제 노출되는 이미지 관련 엔드포인트를 확인하세요:

#!/bin/bash
# 이미지 관련 컨트롤러와 엔드포인트 확인
echo "=== 이미지 관련 컨트롤러 확인 ==="
rg -r '$1' '@.*Mapping.*image|@RestController.*[Ii]mage' --type java -A 10

echo "=== /admin/image 경로 매핑 확인 ==="
rg -r '$1' '@.*Mapping.*"/admin/image' --type java -A 5

echo "=== 보안이 필요한 이미지 관련 메서드 확인 ==="
rg -r '$1' 'delete.*[Ii]mage|remove.*[Ii]mage|update.*[Ii]mage' --type java -A 3
🤖 Prompt for AI Agents
In api-admin/src/main/java/com/admin/config/SecurityConfig.java around lines 55
to 56, the current configuration allows unauthenticated access to the entire
/admin/image/** path, which poses a security risk. Review and identify the
specific image-related endpoints that truly require public access by using the
suggested script to find all image-related controllers and mappings. Then,
restrict the permitAll() configuration to only those necessary sub-paths, and
enforce authentication on sensitive image operations like delete, update, or
remove by adjusting the security rules accordingly.

Comment on lines +47 to +54
@ExceptionHandler(ExternalException.class)
public void handleExternalException(
final ExternalException ex,
final HttpServletRequest request
) {
log.error("External Error: {}, URI: {}",
ex.getMessage(), request.getRequestURI());
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

ExternalException 핸들러의 불일치하는 응답 처리 패턴을 수정해야 합니다.

다른 예외 핸들러들과 달리 ExternalException 핸들러는 HTTP 응답을 반환하지 않습니다. 이는 요청 처리 중 예외 발생 시 예상치 못한 동작을 야기할 수 있습니다.

만약 이 예외가 비동기 처리(SQS 메시지 처리 등)에서만 사용된다면, 메서드명과 용도를 명확히 하거나, 동기 요청에서도 사용될 수 있다면 일관된 응답을 반환하도록 수정해야 합니다:

 @ExceptionHandler(ExternalException.class)
-public void handleExternalException(
+public ResponseEntity<ExceptionResponse> handleExternalException(
     final ExternalException ex,
     final HttpServletRequest request
 ) {
     log.error("External Error: {}, URI: {}",
         ex.getMessage(), request.getRequestURI());
+    
+    final ExceptionResponse response = new ExceptionResponse(
+        ex.getErrorCode().getCode()
+    );
+    
+    return ResponseEntity
+        .status(INTERNAL_SERVER_ERROR)
+        .body(response);
 }

또는 비동기 전용이라면:

 @ExceptionHandler(ExternalException.class)
-public void handleExternalException(
+public void handleAsyncExternalException(
     final ExternalException ex,
-    final HttpServletRequest request
 ) {
-    log.error("External Error: {}, URI: {}",
-        ex.getMessage(), request.getRequestURI());
+    log.error("External Error in async processing: {}", ex.getMessage());
 }
📝 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
@ExceptionHandler(ExternalException.class)
public void handleExternalException(
final ExternalException ex,
final HttpServletRequest request
) {
log.error("External Error: {}, URI: {}",
ex.getMessage(), request.getRequestURI());
}
@ExceptionHandler(ExternalException.class)
- public void handleExternalException(
+ public ResponseEntity<ExceptionResponse> handleExternalException(
final ExternalException ex,
final HttpServletRequest request
) {
log.error("External Error: {}, URI: {}",
ex.getMessage(), request.getRequestURI());
+
+ final ExceptionResponse response = new ExceptionResponse(
+ ex.getErrorCode().getCode()
+ );
+
+ return ResponseEntity
+ .status(INTERNAL_SERVER_ERROR)
+ .body(response);
}
🤖 Prompt for AI Agents
In api-admin/src/main/java/com/admin/exception/GlobalExceptionHandler.java
around lines 47 to 54, the ExternalException handler currently logs the error
but does not return an HTTP response, unlike other exception handlers. To fix
this, modify the method to return a consistent HTTP response entity with an
appropriate status code and error message, ensuring uniform behavior for
synchronous requests. If this handler is only for asynchronous processing,
rename the method to clarify its purpose and avoid confusion.

Comment on lines +28 to +29
- 많이 사용되는 표준 크기의 썸네일들을 생성
6. 썸네일 S3 버킷 저장 후 Amazon Simple Queue 에 메시지 전송
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

후행 공백 제거

28번째 줄 끝에 불필요한 공백이 남아 있습니다(MD009). 저장 시 에디터 trim-on-save 옵션을 켜 두면 자동 해결됩니다.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

28-28: Trailing spaces
Expected: 0 or 2; Actual: 1

(MD009, no-trailing-spaces)

🤖 Prompt for AI Agents
In docs/study/up-down-load.md at line 28, remove the trailing whitespace at the
end of the line to comply with markdown lint rule MD009. Enable the editor's
trim-on-save option to automatically prevent trailing spaces in the future.

Comment on lines +19 to +20
<br>

Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

HTML <br> 태그 대신 Markdown 개행·구분선 활용 권장

라인 19, 41, 50에 <br> 태그가 삽입돼 있어 MD033 경고가 발생합니다.
단순 줄 간격 확보 목적이라면 빈 줄 두 줄, 시각적 구분이라면 ---(수평선) 사용을 권장합니다.

-<br>
+
+

Also applies to: 41-42, 50-51

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

19-19: Inline HTML
Element: br

(MD033, no-inline-html)

🤖 Prompt for AI Agents
In docs/study/up-down-load.md around lines 19 to 20, replace the HTML <br> tags
with appropriate Markdown syntax to avoid MD033 warnings. For simple line
breaks, use two consecutive blank lines, and for visual separation, use a
horizontal rule represented by three hyphens (---). Apply the same changes to
lines 41-42 and 50-51 as well.

Comment on lines +23 to +40
1. 클라이언트가 백엔드로 pre-signed URL 발급 요청
2. 백엔드가 S3 pre-signed URL 발급
3. 클라이언트가 pre-signed URL을 통해 원본 S3 버킷에 이미지 업로드
4. 원본 S3 버킷에 이미지 저장 및 트리거 -> Lambda 함수 실행
5. Lambda 함수가 원본 이미지로 썸네일 이미지 생성 -> 썸네일 S3 버킷에 저장
- 많이 사용되는 표준 크기의 썸네일들을 생성
6. 썸네일 S3 버킷 저장 후 Amazon Simple Queue 에 메시지 전송
- 원본/썸네일 이미지들의 url, 이름, 크기 등의 데이터
7. 백엔드 어플리케이션에서 메시지 수신
8. 원본/썸네일 이미지 정보를 데이터베이스에 저장

### 최적화
표준 크기들의 썸네일들을 미리 생성해두고 특별한 크기가 필요할 때 CloudFront 를 통해 실시간 리사이징을 사용하는 방식 채택
- 썸네일 생성
- Sharp / Canvas / Jimp 등을 사용하여 이미지 리사이징
- 실시간 리사이징
- 파라미터를 받아 CloudFront 를 통해 실시간 이미지 최적화

Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

보안·검증 단계 구체화 제안

업로드 Flow 3-4단계 사이에서 MIME 타입 스푸핑·압축 bomb 등을 막기 위한 S3 bucket policy(예: content-length-range, x-amz-meta-mime)와 Lambda 단의 추가 검증 절차 기술을 권장합니다. 문서 수준에서 언급해 두면 개발·보안팀 간 커뮤니케이션 비용이 줄어듭니다.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

23-23: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)


28-28: Trailing spaces
Expected: 0 or 2; Actual: 1

(MD009, no-trailing-spaces)


34-34: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


36-36: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)

🤖 Prompt for AI Agents
In docs/study/up-down-load.md around lines 23 to 40, the upload flow lacks
details on security and validation steps between steps 3 and 4. To fix this, add
a description of S3 bucket policies such as content-length-range and
x-amz-meta-mime to prevent MIME type spoofing and compression bomb attacks.
Also, include a note about implementing additional validation in the Lambda
function to verify file integrity and metadata. This will improve security
awareness and facilitate communication between development and security teams.

Comment on lines +1 to +18
# 클라이언트 -> S3
백엔드에서 인증/인가 후 짧은 유효기간의 pre-signed URL 발급하면(임시 업로드 권한) 프론트엔드가 S3에 직접 업로드
- 장점
- 백엔드 부하 감소
- 빠른 업로드
- 단점
- 이미지 검증/처리에 제한적
- 보안 통제가 상대적으로 약함

# 클라이언트 -> 백엔드 -> S3
모든 업로드가 백엔드를 거쳐 S3로 전송되는 방식
- 장점
- 보안 제어 용이
- 파일 검증 가능(타입, 크기, 내용 검사 등)
- 단점
- 백엔드 부하 증가
- 프론트 -> 백엔드 -> S3 경로로 인한 업로드 속도 저하

Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

헤더·리스트 앞뒤의 공백 누락 및 다중 H1 사용으로 MarkdownLint 다수 경고 발생

MD022, MD025, MD032 등이 지적하듯 ① 헤더 앞뒤 한 줄 공백, ② 리스트 앞뒤 한 줄 공백, ③ 문서 내 H1 중복이 모두 누락-혼재돼 있습니다.
가독성과 자동 문서화 도구 호환성을 위해 아래와 같이 수정 권장합니다.

-# 클라이언트 -> S3
-백엔드에서 인증/인가 후 ...
+# 파일 업로드/다운로드 전략            <!-- 단일 H1 -->
+
+## 클라이언트 → S3
+백엔드에서 인증/인가 후 ...

리스트 부분도 동일하게 빈 줄을 넣어 주세요.

📝 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
# 클라이언트 -> S3
백엔드에서 인증/인가 후 짧은 유효기간의 pre-signed URL 발급하면(임시 업로드 권한) 프론트엔드가 S3에 직접 업로드
- 장점
- 백엔드 부하 감소
- 빠른 업로드
- 단점
- 이미지 검증/처리에 제한적
- 보안 통제가 상대적으로 약함
# 클라이언트 -> 백엔드 -> S3
모든 업로드가 백엔드를 거쳐 S3로 전송되는 방식
- 장점
- 보안 제어 용이
- 파일 검증 가능(타입, 크기, 내용 검사 등)
- 단점
- 백엔드 부하 증가
- 프론트 -> 백엔드 -> S3 경로로 인한 업로드 속도 저하
# 파일 업로드/다운로드 전략 <!-- 단일 H1 -->
## 클라이언트 → S3
백엔드에서 인증/인가 후 짧은 유효기간의 pre-signed URL 발급하면(임시 업로드 권한) 프론트엔드가 S3에 직접 업로드
- 장점
- 백엔드 부하 감소
- 빠른 업로드
- 단점
- 이미지 검증/처리에 제한적
- 보안 통제가 상대적으로 약함
# 클라이언트 -> 백엔드 -> S3
모든 업로드가 백엔드를 거쳐 S3로 전송되는 방식
- 장점
- 보안 제어 용이
- 파일 검증 가능(타입, 크기, 내용 검사 등)
- 단점
- 백엔드 부하 증가
- 프론트 -> 백엔드 -> S3 경로로 인한 업로드 속도 저하
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

1-1: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


3-3: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)


10-10: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


10-10: Multiple top-level headings in the same document
null

(MD025, single-title, single-h1)


12-12: Lists should be surrounded by blank lines
null

(MD032, blanks-around-lists)

🤖 Prompt for AI Agents
In docs/study/up-down-load.md lines 1 to 18, the Markdown headers and lists lack
required blank lines before and after them, and there are multiple H1 headers
used in the document. To fix this, add a blank line before and after each header
and list to comply with MarkdownLint rules MD022, MD025, and MD032. Also,
replace the multiple H1 headers with a single H1 and use lower-level headers
(H2, H3) for subsequent sections to avoid duplicate top-level headings.

Comment on lines +114 to +123
CREATE TABLE product_thumbnail
(
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
product_id BIGINT UNSIGNED NOT NULL,
url VARCHAR(500) NOT NULL,
size_type VARCHAR(20) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

UNIQUE INDEX idx_product_thumbnail_001 (product_id, size_type)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

product_thumbnail 테이블: size_type 제약 및 FK 누락

size_type은 값 집합이 제한적(S, M, L 등)으로 보입니다. ENUM 또는 별도 테이블+FK로 제약을 추가하여 잘못된 값 유입을 방지하세요. 또한 product_id FK 누락 부분도 함께 보완해주세요.

 CREATE TABLE product_thumbnail (
   ...
-  size_type       VARCHAR(20)                          NOT NULL,
+  size_type       ENUM('SMALL', 'MEDIUM', 'LARGE')     NOT NULL,
+  CONSTRAINT fk_product_thumbnail_001
+    FOREIGN KEY (product_id) REFERENCES product(id)
+    ON DELETE CASCADE,
   ...
 );
📝 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
CREATE TABLE product_thumbnail
(
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
product_id BIGINT UNSIGNED NOT NULL,
url VARCHAR(500) NOT NULL,
size_type VARCHAR(20) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE INDEX idx_product_thumbnail_001 (product_id, size_type)
CREATE TABLE product_thumbnail
(
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
product_id BIGINT UNSIGNED NOT NULL,
url VARCHAR(500) NOT NULL,
- size_type VARCHAR(20) NOT NULL,
+ size_type ENUM('SMALL','MEDIUM','LARGE') NOT NULL,
+ CONSTRAINT fk_product_thumbnail_001
+ FOREIGN KEY (product_id) REFERENCES product(id)
+ ON DELETE CASCADE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE INDEX idx_product_thumbnail_001 (product_id, size_type)
);
🤖 Prompt for AI Agents
In data/src/main/resources/schema.sql around lines 114 to 123, the
product_thumbnail table lacks constraints on the size_type column and a foreign
key on product_id. To fix this, change size_type to use an ENUM type or create a
separate size_type reference table with a foreign key constraint to restrict
allowed values. Also, add a foreign key constraint on product_id referencing the
products table to ensure referential integrity.

@NNIIE NNIIE closed this Jun 26, 2025
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.

1 participant