Skip to content

[REFACTOR] S3 파일 업로드 모듈 범용화 및 미사용 코드 정리 - #157

Merged
on1yoneprivate merged 8 commits into
developfrom
refactor/#124-s3-file-upload
Aug 5, 2026
Merged

[REFACTOR] S3 파일 업로드 모듈 범용화 및 미사용 코드 정리#157
on1yoneprivate merged 8 commits into
developfrom
refactor/#124-s3-file-upload

Conversation

@on1yoneprivate

@on1yoneprivate on1yoneprivate commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

📍 개요

S3 파일 업로드 모듈을 Recording 도메인에서 분리하여 범용적으로 사용할 수 있도록 리팩토링했습니다.
또한 Playing 도메인의 S3 의존성을 줄이고, 미사용 API 및 코드를 제거하여 구조를 단순화했습니다.

⛓️‍💥 관련 이슈


🛠️ 작업 내용

S3 파일 업로드 모듈 리팩토링

  • RecordingUploadServiceS3FileService로 리팩토링
  • Recording 전용 Object Key 생성기를 범용 S3ObjectKeyGenerator로 변경
  • 범용 DTO 추가
    • FileUploadCommand
    • ValidatedFile
    • PresignedUrlUpload
  • 업로드 검증 로직을 validateUploadedFile()로 통합
  • S3 Object Key 생성 및 검증 로직 범용화

Playing 도메인 의존성 분리

  • Playing 전용 요청/응답 DTO 분리
    • RecordingUploadUrlRequest
    • RecordingUploadUrlResponse
  • Playing 도메인이 global.file.s3 DTO에 직접 의존하지 않도록 수정

코드 정리

  • 미사용 S3 업로드 완료 API(/api/s3/complete) 제거
  • 미사용 DTO 및 관련 메서드 제거
  • 중복 업로드 검증 로직 정리

테스트

  • S3FileServiceTest 리팩토링
  • PlayingServiceTest 수정
  • 변경된 DTO 및 서비스 구조에 맞게 테스트 코드 수정

🔥 리뷰 요청 사항

리뷰어가 중점적으로 확인해주었으면 하는 내용을 작성해주세요.

  • 예외 코드 처리 방식

✅ 체크리스트

  • 코드 컨벤션을 준수했습니다.
  • 불필요한 코드 및 import를 제거했습니다.
  • 예외 처리를 적용했습니다.
  • 테스트를 완료했습니다.
  • 관련 Issue를 연결했습니다.

📎 참고 사항

  • 기능 변경 없이 구조 개선을 위한 리팩토링입니다.
  • 향후 프로필 이미지, 백킹트랙 등 다른 파일 업로드 기능에서도 동일한 S3 모듈을 사용할 수 있도록 구조를 개선했습니다.

Summary by CodeRabbit

  • 변경 사항

    • 녹음 파일 업로드 URL 발급 기능이 연주 기능 내 API로 통합되었습니다.
    • 별도 업로드 완료 API가 제거되어 업로드 절차가 간소화되었습니다.
    • 업로드 파일의 소유권, 존재 여부, 크기 및 형식 검증이 강화되었습니다.
  • 개선 사항

    • 파일 확장자와 콘텐츠 형식을 보다 안정적으로 처리합니다.
    • 검증에 실패한 파일은 자동으로 정리되어 불필요한 저장을 방지합니다.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d6f4127e-510d-46ad-ab2f-a663d508d7f1

📥 Commits

Reviewing files that changed from the base of the PR and between e626bf7 and b2416a3.

📒 Files selected for processing (2)
  • src/main/java/com/mr/global/file/s3/service/S3FileService.java
  • src/main/java/com/mr/global/file/s3/service/S3ObjectKeyGenerator.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/com/mr/global/file/s3/service/S3FileService.java
  • src/main/java/com/mr/global/file/s3/service/S3ObjectKeyGenerator.java

📝 Walkthrough

Walkthrough

녹음 업로드 URL 발급을 Playing 도메인 전용 DTO와 공통 S3FileService 기반으로 변경했습니다. 기존 녹음 전용 컨트롤러와 업로드 완료 API를 제거하고, Object Key 생성·파일 검증·삭제 로직과 테스트를 범용 구조로 갱신했습니다.

Changes

S3 업로드 모듈 범용화

Layer / File(s) Summary
도메인 업로드 계약과 서비스 연결
src/main/java/com/mr/domain/playing/controller/PlayingController.java, src/main/java/com/mr/domain/playing/dto/req/RecordingUploadUrlRequest.java, src/main/java/com/mr/domain/playing/dto/res/RecordingUploadUrlResponse.java, src/main/java/com/mr/domain/playing/service/PlayingService.java, src/main/java/com/mr/global/file/s3/dto/*
Playing API가 새 요청·응답 DTO를 사용합니다. 요청 DTO는 FileUploadCommand로 변환됩니다. 응답 DTO는 PresignedUrlUpload에서 생성됩니다.
공통 S3 파일 처리와 Object Key 정책
src/main/java/com/mr/global/file/s3/service/S3FileService.java, src/main/java/com/mr/global/file/s3/service/S3ObjectKeyGenerator.java, src/main/java/com/mr/global/file/s3/controller/RecordingUploadController.java, src/main/java/com/mr/global/file/s3/service/RecordingUploadService.java, src/main/java/com/mr/global/file/s3/dto/req/*, src/main/java/com/mr/global/file/s3/dto/res/*
S3FileService가 Presigned PUT URL 생성, 업로드 객체 검증, 오류 변환, 객체 삭제를 처리합니다. S3ObjectKeyGenerator가 소유자 경로와 파일 형식을 처리합니다. 기존 녹음 전용 컨트롤러와 업로드 완료 흐름을 제거했습니다.
PlayingService 업로드 흐름 검증
src/test/java/com/mr/domain/playing/service/PlayingServiceTest.java
PlayingService 테스트가 새 S3 서비스 호출, DTO 변환, 파일 검증 결과와 업로드 URL 응답을 검증합니다.
S3 서비스와 Object Key 검증 테스트
src/test/java/com/mr/global/file/s3/service/S3FileServiceTest.java, src/test/java/com/mr/global/file/s3/service/RecordingObjectKeyGeneratorTest.java
Presigned URL 생성, 파일 검증·삭제, S3 오류 변환, 소유자 확인, 확장자 처리를 검증합니다. 기존 RecordingUploadServiceTest는 제거했습니다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • Musereview/BE#116: 기존 S3 녹음 업로드 컨트롤러, DTO, 서비스 흐름과 직접 연결됩니다.
  • Musereview/BE#123: PlayingControllerPlayingService의 녹음 업로드 URL 발급 흐름과 직접 연결됩니다.
  • Musereview/BE#136: RecordingObjectKeyGenerator의 확장자 및 Content-Type 검증 로직과 직접 연결됩니다.

Poem

S3 키는 새 이름을 얻고
DTO는 도메인 곁에 서고
검증 실패 파일은 삭제되고
테스트는 흐름을 비춘다
업로드 경로가 단단해진다 🎵

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 변경사항의 핵심을 정확히 반영합니다. S3 파일 업로드 모듈을 Recording 도메인에서 분리하여 범용화하고 미사용 코드를 정리하는 리팩토링이라는 주요 목표가 명확합니다.
Linked Issues check ✅ Passed 모든 링크된 이슈의 요구사항이 충족되었습니다. 사용하지 않는 /api/s3/complete API 제거, Recording 전용 클래스명 범용화, Playing 도메인의 global.file.s3 DTO 의존성 제거, S3 Object Key 생성·검증 로직 범용화, 중복 검증 로직 정리, 기존 기능 정상 동작이 모두 구현되었습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 리팩토링 범위 내에 있습니다. S3 모듈 범용화, DTO 분리, 클래스명 변경, 미사용 코드 제거, 테스트 수정이 issue #124의 요구사항과 정확히 일치합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/#124-s3-file-upload

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.

@on1yoneprivate on1yoneprivate changed the title [REFACTOR] [REFACTOR] S3 파일 업로드 모듈 범용화 및 미사용 코드 정리 Aug 5, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/test/java/com/mr/global/file/s3/service/RecordingObjectKeyGeneratorTest.java (1)

21-21: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

테스트 클래스 이름을 S3ObjectKeyGeneratorTest로 변경하세요.

프로덕션 클래스는 S3ObjectKeyGenerator로 변경되었습니다. 테스트 클래스는 RecordingObjectKeyGeneratorTest 이름을 유지합니다. 이 PR의 목표는 Recording 전용 이름을 범용 이름으로 바꾸는 것입니다. 테스트 이름이 남으면 검색과 추적이 어려워집니다.

Java에서는 public 여부와 무관하게 클래스 이름과 파일 이름을 함께 변경해야 합니다. 파일을 src/test/java/com/mr/global/file/s3/service/S3ObjectKeyGeneratorTest.java로 이동하세요.

메서드 이름 belongsToUser(Line 242)와 doesNotBelongToUser(Line 263)도 belongsToOwner 계약에 맞춰 belongsToOwner, doesNotBelongToOwner로 정리하면 좋습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/mr/global/file/s3/service/RecordingObjectKeyGeneratorTest.java`
at line 21, Rename the test class RecordingObjectKeyGeneratorTest to
S3ObjectKeyGeneratorTest and move the file to S3ObjectKeyGeneratorTest.java to
match the production class. Also rename belongsToUser and doesNotBelongToUser to
belongsToOwner and doesNotBelongToOwner to align with the belongsToOwner
contract.
src/test/java/com/mr/global/file/s3/service/S3FileServiceTest.java (1)

696-700: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

불필요한 S3Exception 캐스팅을 제거하세요.

S3Exception.builder().build()에서 생성된 객체는 S3Exception 타입이므로 (S3Exception) 캐스팅을 지우면 훨씬 clean합니다. 관련 문서는 AWS SDK for Java v2의 S3Exception.Builder 예시를 참고하세요.

两处 캐스팅은 다음과 같습니다.

  • src/test/java/com/mr/global/file/s3/service/S3FileServiceTest.java:696-700
  • src/test/java/com/mr/global/file/s3/service/S3FileServiceTest.java:725-729
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/java/com/mr/global/file/s3/service/S3FileServiceTest.java` around
lines 696 - 700, Remove the redundant S3Exception casts from both
S3Exception.builder() constructions in S3FileServiceTest, including the
instances around the 404 exception setup and the second builder setup. Keep the
existing statusCode, message, and exception behavior unchanged.
src/test/java/com/mr/domain/playing/service/PlayingServiceTest.java (1)

1096-1097: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

기대값을 운영 변환 로직과 분리해 주세요.

현재 테스트는 request.toCommand()RecordingUploadUrlResponse.from(presignedUpload)를 기대값 생성에 재사용합니다. 변환 로직이 잘못되어도 테스트가 같은 잘못된 값을 기대하면 통과할 수 있습니다. FileUploadCommand의 파일명, Content-Type, 파일 크기를 직접 구성하고, 응답도 공개 필드를 명시적으로 검증해 PlayingService와 S3 계약을 독립적으로 확인해 주세요.

Also applies to: 1107-1110

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/java/com/mr/domain/playing/service/PlayingServiceTest.java` around
lines 1096 - 1097, 테스트의 기대값 생성을 운영 변환 로직과 분리하세요. `PlayingService` 테스트에서
`request.toCommand()` 대신 파일명, Content-Type, 파일 크기를 직접 지정해 `FileUploadCommand`를
구성하고, `RecordingUploadUrlResponse.from(presignedUpload)`도 사용하지 말고 응답의 공개 필드를 직접
검증하여 S3 계약을 독립적으로 확인하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/com/mr/global/file/s3/service/S3FileService.java`:
- Around line 58-71: createPresignedUpload()는 command.fileSize()만 신뢰하므로, URL 발급
전후에 S3 최대 업로드 크기를 강제하도록 수정하세요. Presigned POST의 content-length-range 정책이나 적용 가능한
버킷 정책을 사용해 제한을 업로드 자체에 포함하고, 현재 PUT presigning 흐름과 호환되지 않으면 해당 방식으로 전환하세요.
validateUploadedFile()에 의존하지 않도록 하며, 필요하면 초과 또는 미완료 객체를 정리하는 Lifecycle 정책도
구성하세요.

In `@src/main/java/com/mr/global/file/s3/service/S3ObjectKeyGenerator.java`:
- Around line 84-91: S3ObjectKeyGenerator의 확장자 결정 흐름에서 파일명 확장자만
validateExtension으로 통과시키지 말고, resolveExtensionByContentType로 정규화한 contentType의
대표 확장자를 먼저 구한 뒤 originalFileName의 확장자와 일치하는지 검증하도록 update하세요. extractExtension,
validateExtension, resolveExtensionByContentType의 연결을 유지하되, recording.mp3와
audio/webm처럼 서로 다른 허용 조합이 통과하지 않도록 같은 형식일 때만 Object Key와 S3 메타데이터가 일치하게 만드세요.

---

Nitpick comments:
In `@src/test/java/com/mr/domain/playing/service/PlayingServiceTest.java`:
- Around line 1096-1097: 테스트의 기대값 생성을 운영 변환 로직과 분리하세요. `PlayingService` 테스트에서
`request.toCommand()` 대신 파일명, Content-Type, 파일 크기를 직접 지정해 `FileUploadCommand`를
구성하고, `RecordingUploadUrlResponse.from(presignedUpload)`도 사용하지 말고 응답의 공개 필드를 직접
검증하여 S3 계약을 독립적으로 확인하세요.

In
`@src/test/java/com/mr/global/file/s3/service/RecordingObjectKeyGeneratorTest.java`:
- Line 21: Rename the test class RecordingObjectKeyGeneratorTest to
S3ObjectKeyGeneratorTest and move the file to S3ObjectKeyGeneratorTest.java to
match the production class. Also rename belongsToUser and doesNotBelongToUser to
belongsToOwner and doesNotBelongToOwner to align with the belongsToOwner
contract.

In `@src/test/java/com/mr/global/file/s3/service/S3FileServiceTest.java`:
- Around line 696-700: Remove the redundant S3Exception casts from both
S3Exception.builder() constructions in S3FileServiceTest, including the
instances around the 404 exception setup and the second builder setup. Keep the
existing statusCode, message, and exception behavior unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c926a200-6eda-436a-8e10-161ef33f4bdd

📥 Commits

Reviewing files that changed from the base of the PR and between e388aa5 and e626bf7.

📒 Files selected for processing (17)
  • src/main/java/com/mr/domain/playing/controller/PlayingController.java
  • src/main/java/com/mr/domain/playing/dto/req/RecordingUploadUrlRequest.java
  • src/main/java/com/mr/domain/playing/dto/res/RecordingUploadUrlResponse.java
  • src/main/java/com/mr/domain/playing/service/PlayingService.java
  • src/main/java/com/mr/global/file/s3/controller/RecordingUploadController.java
  • src/main/java/com/mr/global/file/s3/dto/FileUploadCommand.java
  • src/main/java/com/mr/global/file/s3/dto/PresignedUrlUpload.java
  • src/main/java/com/mr/global/file/s3/dto/ValidatedFile.java
  • src/main/java/com/mr/global/file/s3/dto/req/RecordingUploadCompleteRequest.java
  • src/main/java/com/mr/global/file/s3/dto/res/RecordingUploadCompleteResponse.java
  • src/main/java/com/mr/global/file/s3/service/RecordingUploadService.java
  • src/main/java/com/mr/global/file/s3/service/S3FileService.java
  • src/main/java/com/mr/global/file/s3/service/S3ObjectKeyGenerator.java
  • src/test/java/com/mr/domain/playing/service/PlayingServiceTest.java
  • src/test/java/com/mr/global/file/s3/service/RecordingObjectKeyGeneratorTest.java
  • src/test/java/com/mr/global/file/s3/service/RecordingUploadServiceTest.java
  • src/test/java/com/mr/global/file/s3/service/S3FileServiceTest.java
💤 Files with no reviewable changes (5)
  • src/main/java/com/mr/global/file/s3/dto/req/RecordingUploadCompleteRequest.java
  • src/main/java/com/mr/global/file/s3/dto/res/RecordingUploadCompleteResponse.java
  • src/main/java/com/mr/global/file/s3/controller/RecordingUploadController.java
  • src/test/java/com/mr/global/file/s3/service/RecordingUploadServiceTest.java
  • src/main/java/com/mr/global/file/s3/service/RecordingUploadService.java

Comment on lines +58 to +71
PutObjectRequest putObjectRequest =
PutObjectRequest.builder()
.bucket(s3Properties.bucket())
.key(objectKey)
.contentType(normalizedContentType)
.build();

PutObjectPresignRequest presignRequest =
PutObjectPresignRequest.builder()
.signatureDuration(
s3Properties.presignedUrlExpiration()
)
.putObjectRequest(putObjectRequest)
.build();

@coderabbitai coderabbitai Bot Aug 5, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Presigned PUT에 업로드 크기 조건이 포함되는지와 미완료 객체 정리 정책을 확인합니다.
rg -n -C 4 \
  'PutObjectPresignRequest|PostObject|content-length-range|contentLength|maxFileSize|LifecycleConfiguration|deleteObject' \
  src

Repository: Musereview/BE

Length of output: 31061


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== S3FileService relevant sections =="
sed -n '1,260p' src/main/java/com/mr/global/file/s3/service/S3FileService.java
echo

echo "== S3Properties =="
sed -n '1,80p' src/main/java/com/mr/global/file/s3/config/S3Properties.java
echo

echo "== upload callers requesting presigned URL =="
rg -n -C 5 'generatePresignedUrlForUpload|presignUrlForUpload|presign|fileSize|objectKeyGenerator' src/main/java

Repository: Musereview/BE

Length of output: 26932


Denial of Service (CWE-400): Uncontrolled Resource Consumption

Reachability: External

Presigned URL 발급 전에 최대 업로드 크기를 강제하세요.

createPresignedUpload()command.fileSize()만 검증하고, 실제 Content-Length 검증은 validateUploadedFile() 뒤에서 실행됩니다. 사용자가 작은 fileSize로 URL을 받은 뒤 제한을 초과하는 객체를 업로드하는 경우, validateUploadedFile() 호출을 생략하면 초과 객체가 S3에 남습니다. Presigned POST의 content-length-range 정책, 버킷 정책, 또는 미완료 업로드 정리 Lifecycle 정책으로 URL 발급 전후 크기를 함께 제어하세요. AWS 업로드 정책 문서도 참고하면 좋습니다. 예: AWS S3 upload restrictions, presigned POST content-length range, Lifecycle rules.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/mr/global/file/s3/service/S3FileService.java` around lines
58 - 71, createPresignedUpload()는 command.fileSize()만 신뢰하므로, URL 발급 전후에 S3 최대
업로드 크기를 강제하도록 수정하세요. Presigned POST의 content-length-range 정책이나 적용 가능한 버킷 정책을 사용해
제한을 업로드 자체에 포함하고, 현재 PUT presigning 흐름과 호환되지 않으면 해당 방식으로 전환하세요.
validateUploadedFile()에 의존하지 않도록 하며, 필요하면 초과 또는 미완료 객체를 정리하는 Lifecycle 정책도
구성하세요.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Lifecycle 정책은 추후 도입 예정입니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@on1yoneprivate 확인했습니다. Lifecycle 정책을 별도 작업으로 도입하는 방향은 적절합니다.

다만 Lifecycle 정책은 남은 객체를 나중에 삭제합니다. Presigned PUT의 업로드 크기를 즉시 제한하지는 않습니다. 따라서 현재 지적 사항은 이 PR에서는 미해결 상태로 유지하겠습니다.

원하시면 S3 Lifecycle 정리 정책과 업로드 크기 제한 방안을 포함한 후속 GitHub 이슈를 생성하겠습니다. 🐇

You are interacting with an AI system.

Comment thread src/main/java/com/mr/global/file/s3/service/S3ObjectKeyGenerator.java Outdated

@p1001q p1001q left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

리팩토링 방향 좋습니다~ Playing 도메인이 S3 DTO에 직접 의존 안 하게 분리한 것도 깔끔하고, FileUploadCommand/ValidatedFile/PresignedUrlUpload로 범용 DTO 나눈 것도 프로필 이미지, 백킹트랙 업로드를 고려하는 좋은 구조라고 생각해요.
코멘트 확인 부탁드려요!

+코드래빗이 리뷰 달기 전에 작성해서 코드래빗과 중복되는 부분이 있을 수 있습니다... 중복된 부분이면 그냥 리졸브해서 넘겨주세요!

normalizedContentType
);

PutObjectRequest putObjectRequest =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 P1 — S3FileService.java (58~71줄, createPresignedUpload())

PutObjectRequest putObjectRequest = PutObjectRequest.builder()
    .bucket(s3Properties.bucket())
    .key(objectKey)
    .contentType(normalizedContentType)
    .build();

PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
    .signatureDuration(s3Properties.presignedUrlExpiration())
    .putObjectRequest(putObjectRequest)
    .build();

문제 상황: presigned URL 발급 시점엔 command.fileSize()만 검증하고, 실제 업로드된 파일의 크기 검증은 이후 validateUploadedFile() 호출 시점에야 이뤄져요.

왜 문제가 될 수 있는가?: 클라이언트가 작은 fileSize로 URL을 발급받고 실제로는 훨씬 큰 파일을 업로드한 뒤, 업로드 완료 흐름(validateUploadedFile())을 아예 호출하지 않으면 초과 용량 객체가 검증 없이 S3에 그대로 남아요. presigned PUT 자체엔 크기 제한을 강제하는 옵션이 없다 보니 저장 비용이나 악의적인 대용량 업로드에 취약할 수 있어요.

수정 방향: presigned PUT 대신 content-length-range 조건을 넣을 수 있는 presigned POST로 전환하거나, 버킷 정책 + Lifecycle 룰(미완료/초과 객체 자동 정리)로 업로드 자체에 제한을 거는 방향은 어떨까요?

혹시 이 부분은 리뷰 요청사항으로 남기신 "예외 코드 처리 방식"이랑 연결돼서 다음 PR에서 다루실 예정이라 남겨두신걸까요?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

지적해 주신 부분 이해했습니다.

다만 현재 프론트 연동 일정상 업로드 방식을 PUT에서 POST로 변경하기에는 영향 범위가 있어, 논의가 필요할 것 같습니다. 따라서 PR에서는 리팩토링 범위에 집중하고 Presigned PUT 방식을 유지하려고 합니다.

대신 후속 이슈로 미검증 객체에 대한 Lifecycle 정책을 적용하여 운영상 위험을 최소화하고,
Presigned POST 전환 및 content-length-range를 통한 실제 업로드 크기 강제는 디벨롭 기간에 반영하려고 합니다!

Comment thread src/main/java/com/mr/global/file/s3/service/S3ObjectKeyGenerator.java Outdated

@rkdehdrbs7885-oss rkdehdrbs7885-oss left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

수고하셨습니다!

Comment thread src/main/java/com/mr/global/file/s3/service/S3FileService.java Outdated
@on1yoneprivate
on1yoneprivate merged commit 8989c36 into develop Aug 5, 2026
2 checks passed
@kimyw1018
kimyw1018 deleted the refactor/#124-s3-file-upload branch August 9, 2026 12:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

♻️ Refactor - S3 파일 업로드 모듈 범용화 및 미사용 코드 정리

3 participants