Skip to content

T-17 이미지 업로드 (presigned URL) - #71

Open
ff1451 wants to merge 1 commit into
feat/t16-activity-public-apifrom
feat/t17-image-upload
Open

T-17 이미지 업로드 (presigned URL)#71
ff1451 wants to merge 1 commit into
feat/t16-activity-public-apifrom
feat/t17-image-upload

Conversation

@ff1451

@ff1451 ff1451 commented Aug 20, 2026

Copy link
Copy Markdown

요약

  • POST /v1/admin/images/presigned-url : ImageAssetis_confirmed=false로 먼저 만들고 S3 PUT presigned URL을 발급한다(ADR-009 — 서버는 이미지 바이트를 경유하지 않는다).
  • POST /v1/admin/images/{id}/complete : 브라우저의 S3 PUT이 끝난 뒤 호출해 is_confirmed를 true로 바꾼다. 호출 전에는 라이브러리에 노출되지 않는다(AC-4.4).
  • GET /v1/admin/images?purpose= : confirmed된 이미지만 최신순 페이지네이션.
  • 확장자(png|jpg|jpeg|webp|svg)·5MB 검증은 PresignedUrlRequest의 bean validation(@Pattern, @Max)으로 끝낸다(AC-4.1, AC-4.2) — 다른 도메인의 @Min/@Max 검증과 동일한 방식.

설계 메모

  • image_asset은 콘텐츠 테이블과 FK로 묶지 않는다(ADR-009) — 이미지 행을 지워도 콘텐츠가 깨지지 않게.
  • ImageAssetupdated_at 컬럼이 없어 BaseTimeEntity를 쓰지 않고 @CreatedDate만 직접 매핑했다(ActivityImage와 같은 이유).
  • S3Propertiesbucket, publicBaseUrl을 추가했다(app.s3.bucket, app.s3.public-base-url, 각각 env var로 오버라이드 가능).
  • 테스트에서는 S3Presigner@MockitoBean으로 대체했다 — 실제 AWS 자격증명 없이 presign 호출 경로를 검증한다. S3ConfigS3Client/S3Presigner 빈은 조건 없이 항상 로드되는데(메일 전송의 SES 빈과 달리), 실제 서명 호출 시점에만 자격증명이 필요해서 그동안은 문제가 없었지만 이번에 실제로 호출하는 코드가 생겨 목이 필요해졌다.

검증

  • ./gradlew test 전체 통과, 3회 연속.
  • AC-4.1(5MB 초과 400), AC-4.2(허용 외 확장자 400), AC-4.3/AC-4.4(complete 전후 라이브러리 노출), 존재하지 않는 이미지 complete 404 — 통합 테스트로 커버.

남은 항목

  • T-19(기술스택 아이콘 S3 업로드)에서 이 API를 실제로 사용한다.
  • webp 자동 변환은 1차 제외(ADR-009).

Refs #29 (프로젝트), 브랜치는 feat/t16-activity-public-api(#70) 위에 스택 — T-14~T-16까지 포함된 상태입니다.

머지하지 않습니다. 부원 리뷰 후 직접 머지 예정입니다.

- POST /v1/admin/images/presigned-url : ImageAsset를 is_confirmed=false로 먼저 만들고
  S3Presigner로 PUT presigned URL을 발급한다(ADR-009, 서버는 바이트를 경유하지 않는다).
- POST /v1/admin/images/{id}/complete : 브라우저의 S3 PUT이 끝난 뒤 호출해 is_confirmed를
  true로 바꾼다. 이걸 안 하면 라이브러리에 노출되지 않는다(AC-4.4).
- GET /v1/admin/images?purpose= : confirmed=true만, 최신순 페이지네이션.
- 확장자(png|jpg|jpeg|webp|svg)·5MB 검증은 PresignedUrlRequest의 bean validation으로
  끝낸다(AC-4.1, AC-4.2) — 다른 도메인의 @Min/@max 검증과 같은 방식.
- ImageAsset는 image_asset 테이블에 updated_at이 없어 BaseTimeEntity를 쓰지 않고
  @CreatedDate만 직접 매핑했다(ActivityImage와 같은 이유). 콘텐츠 테이블과 FK로 묶지
  않는다(ADR-009) — 이미지 행 삭제가 콘텐츠를 깨뜨리지 않게.
- S3Properties에 bucket·publicBaseUrl 추가(app.s3.bucket, app.s3.public-base-url).
- 테스트에서 S3Presigner는 @MockitoBean으로 대체했다 — 실제 AWS 자격증명 없이도
  presignPutObject() 호출 경로를 검증할 수 있게.

검증: ./gradlew test 전체 통과, 3회 연속.
@ff1451

ff1451 commented Aug 20, 2026

Copy link
Copy Markdown
Author

Refs #29

@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7b52fd48-1f4f-4528-af64-064b3987428c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

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