Skip to content

[FEAT] S3 파일 저장 구조를 Object Key 기반으로 변경 - #166

Merged
p1001q merged 5 commits into
developfrom
feat/#160-object-key-storage
Aug 8, 2026
Merged

[FEAT] S3 파일 저장 구조를 Object Key 기반으로 변경#166
p1001q merged 5 commits into
developfrom
feat/#160-object-key-storage

Conversation

@on1yoneprivate

@on1yoneprivate on1yoneprivate commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

📍 개요

S3 녹음 파일 저장 구조를 Raw URL 기반에서 Object Key 기반으로 변경하고,
Object Key를 이용한 Presigned GET URL 조회 기능을 추가했습니다.

⛓️‍💥 관련 이슈


🛠️ 작업 내용

  • 녹음 파일 저장 값을 Raw URL → S3 Object Key로 변경
  • Flyway 마이그레이션을 추가하여 기존 recording_file_url 데이터를 recording_object_key로 변환
  • ValidatedFile을 Object Key 기반 DTO로 변경하고 Raw URL 의존 로직 제거
  • S3 Object Key를 이용한 Presigned GET URL 생성 기능 추가
  • 연주 상세 조회, 분석 결과 조회, 분석 Context 조회, 히스토리 조회 API에서 Presigned GET URL을 반환하도록 수정
  • 관련 테스트 코드 수정 및 검증

🔥 리뷰 요청 사항

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

  • Flyway 마이그레이션의 URL → Object Key 변환 로직이 적절한지 확인 부탁드립니다.
  • Presigned GET URL 생성 위치(Service 계층)와 DTO 변경 방향이 적절한지 리뷰 부탁드립니다.

✅ 체크리스트

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

📎 참고 사항

  • 기존 DB에는 Raw URL이 저장되어 있어 Flyway를 통해 Object Key로 마이그레이션하도록 구현했습니다.
  • 조회 API에서는 DB에 저장된 Object Key를 이용해 Presigned GET URL을 생성하여 응답합니다.

Summary by CodeRabbit

  • 개선 사항

    • 녹음 파일을 안전한 사용자별 다운로드 링크로 제공하도록 개선했습니다.
    • 연주 상세, 분석 결과, 분석 컨텍스트 및 히스토리 화면에서 녹음 파일 링크를 정상적으로 확인할 수 있습니다.
    • 기존 녹음 데이터도 새로운 저장 방식으로 자동 전환됩니다.
  • 테스트

    • 녹음 파일 링크 생성 및 응답 반영에 대한 검증을 강화했습니다.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@on1yoneprivate, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 31 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 609d7153-6192-4089-903d-904b4bc83339

📥 Commits

Reviewing files that changed from the base of the PR and between 344690a and 4b9b2ca.

📒 Files selected for processing (3)
  • src/main/java/com/mr/domain/playing/entity/Playing.java
  • src/main/java/com/mr/domain/playing/exception/PlayingErrorStatus.java
  • src/main/resources/db/migration/V5__migrate_recording_file_url_to_object_key.sql
📝 Walkthrough

Walkthrough

녹음 파일 저장값을 Raw URL에서 S3 Object Key로 변경합니다. 조회 시 S3FileService가 사용자별 presigned 다운로드 URL을 생성합니다. 관련 응답 DTO, 데이터 마이그레이션, 서비스 테스트를 갱신합니다.

Changes

녹음 파일 Object Key 전환

Layer / File(s) Summary
Object Key 저장 계약과 데이터 마이그레이션
src/main/java/com/mr/domain/playing/entity/Playing.java, src/main/java/com/mr/global/file/s3/dto/ValidatedFile.java, src/main/java/com/mr/global/file/s3/service/S3FileService.java, src/main/resources/db/migration/V5__migrate_recording_file_url_to_object_key.sql
PlayingValidatedFile이 Object Key를 사용합니다. S3 Raw URL 생성 및 Object Key 추출 로직을 제거합니다. 기존 DB 값을 Object Key로 변환합니다.
녹음 저장과 연주 조회 URL 생성
src/main/java/com/mr/domain/playing/service/PlayingService.java, src/test/java/com/mr/domain/playing/entity/PlayingTest.java, src/test/java/com/mr/domain/playing/service/PlayingServiceTest.java
연주 완료 시 Object Key를 저장합니다. 연주 상세와 분석 컨텍스트 조회 시 presigned 다운로드 URL을 생성합니다. 관련 테스트가 Object Key와 호출 검증을 사용합니다.
분석 및 히스토리 응답 URL 전달
src/main/java/com/mr/domain/analysis/..., src/main/java/com/mr/domain/history/..., src/main/java/com/mr/domain/playing/dto/res/..., src/test/java/com/mr/domain/analysis/service/AnalysisServiceTest.java, src/test/java/com/mr/domain/history/service/HistoryServiceTest.java, src/test/java/com/mr/global/file/s3/service/RecordingObjectKeyGeneratorTest.java
분석 및 히스토리 서비스가 Object Key로 URL을 생성합니다. 응답 DTO는 서비스가 전달한 URL을 사용합니다. 서비스 테스트는 presigned URL 생성과 반환값을 검증합니다.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant PlayingService
  participant AnalysisService
  participant HistoryService
  participant S3FileService
  participant ResponseDTO

  Client->>PlayingService: 연주 상세 또는 분석 컨텍스트 조회
  PlayingService->>S3FileService: Object Key로 presigned URL 생성
  S3FileService-->>PlayingService: recordingFileUrl
  PlayingService->>ResponseDTO: URL을 응답에 전달

  Client->>AnalysisService: 분석 결과 조회
  AnalysisService->>S3FileService: Object Key로 presigned URL 생성
  S3FileService-->>AnalysisService: recordingFileUrl
  AnalysisService->>ResponseDTO: URL을 응답에 전달

  Client->>HistoryService: 히스토리 상세 조회
  HistoryService->>S3FileService: Object Key로 presigned URL 생성
  S3FileService-->>HistoryService: recordingFileUrl
  HistoryService->>ResponseDTO: URL을 응답에 전달
Loading

Possibly related PRs

  • Musereview/BE#159: S3FileService.createPresignedDownload를 도입한 변경입니다.
  • Musereview/BE#116: 녹음 파일 Object Key 생성 및 S3 처리 계약과 직접 연결됩니다.
  • Musereview/BE#44: 분석 결과 응답에 녹음 파일 정보를 전달하는 흐름과 연결됩니다.

Poem

URL은 떠나고 Key가 남아
S3가 서명한 길을 열어
Playing은 키를 품고
DTO는 링크를 받아
안전한 녹음이 다시 재생돼 🎵

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 Raw URL 기반 저장을 Object Key 기반으로 변경하는 PR의 핵심 내용을 직접 설명하며, 간결하고 구체적입니다.
Linked Issues check ✅ Passed 변경 사항은 [#160]의 Object Key 저장, ValidatedFile 변경, Raw URL 로직 제거, Presigned URL 조회, 마이그레이션, 테스트 요구를 모두 반영합니다.
Out of Scope Changes check ✅ Passed 모든 변경은 [#160]의 저장 구조 전환, 조회 URL 생성, 데이터 마이그레이션, 테스트 범위에 포함됩니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#160-object-key-storage

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.

@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 (1)
src/main/java/com/mr/domain/history/service/HistoryService.java (1)

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

Presigned URL 반환 계약을 성공 경로 테스트로 고정하세요.

이 변경은 recordingObjectKeyuserIdcreatePresignedDownload에 전달하고, 생성된 URL을 HistoryDetailResponseDTO에 넣습니다. 성공 테스트가 호출 인자와 response.recordingFileUrl()을 검증하지 않으면 잘못된 사용자 ID나 Object Key를 사용해도 통과할 수 있습니다.

HistoryServiceTestgetHistoryDetail 성공 케이스와 PlayingServiceTestgetPlayingDetail·getAnalysisContext 성공 케이스에서 Object Key와 예상 URL을 stub하고, verify와 응답 필드 assertion을 추가하세요. Presigned URL은 사용자와 Object Key의 결합이므로 이 보안 경계를 테스트로 고정하세요. AWS S3 presigned URL 공식 문서도 함께 확인하세요.

✅ 성공 케이스 보강 예시
+when(playing.getRecordingObjectKey())
+        .thenReturn(RECORDING_OBJECT_KEY);
+when(s3FileService.createPresignedDownload(
+        userId,
+        RECORDING_OBJECT_KEY
+)).thenReturn(RECORDING_FILE_URL);
...
+assertThat(response.recordingFileUrl())
+        .isEqualTo(RECORDING_FILE_URL);
+verify(s3FileService)
+        .createPresignedDownload(userId, RECORDING_OBJECT_KEY);
🤖 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/domain/history/service/HistoryService.java` around lines
74 - 80, HistoryServiceTest의 getHistoryDetail 성공 케이스와 PlayingServiceTest의
getPlayingDetail·getAnalysisContext 성공 케이스를 보강하세요. 각 테스트에서 recordingObjectKey와
예상 presigned URL을 stub하고, createPresignedDownload가 올바른 userId와 Object Key로 호출되는지
verify하며 response.recordingFileUrl()이 예상 URL인지 검증하세요.
🤖 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/domain/playing/entity/Playing.java`:
- Around line 97-98: Playing의 completeWithMidiData()에서 recordingObjectKey가 공백이
아니고 유효한 Object Key인지 검증한 뒤에만 저장하도록 수정하고, 빈 값은 저장하지 않게 하세요. 마이그레이션에서는 기존
recording_object_key의 쿼리 문자열과 URL 인코딩 변수를 제거하거나, 변경된 데이터가 있음을 로그로 남기세요.
createPresignedDownload() 조회 경로와 일관되게 처리하되, http://example.com/file.webm 같은 URL을
file/webm으로 변환하지 않도록 별도 방어 조건을 유지하세요.

In
`@src/main/resources/db/migration/V5__migrate_recording_file_url_to_object_key.sql`:
- Around line 16-24: Update the validation section of the migration to check
whether any rows match the invalid recording_object_key patterns, and raise an
exception when such rows exist before reaching COMMIT. Use an IF EXISTS check
around the existing playing query so Flyway aborts and rolls back on validation
failure, while preserving the successful zero-row commit path.

---

Nitpick comments:
In `@src/main/java/com/mr/domain/history/service/HistoryService.java`:
- Around line 74-80: HistoryServiceTest의 getHistoryDetail 성공 케이스와
PlayingServiceTest의 getPlayingDetail·getAnalysisContext 성공 케이스를 보강하세요. 각 테스트에서
recordingObjectKey와 예상 presigned URL을 stub하고, createPresignedDownload가 올바른
userId와 Object Key로 호출되는지 verify하며 response.recordingFileUrl()이 예상 URL인지 검증하세요.
🪄 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: e3011968-a621-4eb8-9304-ca2f838be950

📥 Commits

Reviewing files that changed from the base of the PR and between 587ef15 and 344690a.

📒 Files selected for processing (17)
  • src/main/java/com/mr/domain/analysis/dto/res/AnalysisResultResponseDTO.java
  • src/main/java/com/mr/domain/analysis/service/AnalysisService.java
  • src/main/java/com/mr/domain/history/dto/res/HistoryDetailResponseDTO.java
  • src/main/java/com/mr/domain/history/service/HistoryService.java
  • src/main/java/com/mr/domain/playing/dto/res/AnalysisContextResponse.java
  • src/main/java/com/mr/domain/playing/dto/res/PlayingDetailResponse.java
  • src/main/java/com/mr/domain/playing/entity/Playing.java
  • src/main/java/com/mr/domain/playing/service/PlayingService.java
  • src/main/java/com/mr/global/file/s3/dto/ValidatedFile.java
  • src/main/java/com/mr/global/file/s3/service/S3FileService.java
  • src/main/resources/db/migration/V5__migrate_recording_file_url_to_object_key.sql
  • src/test/java/com/mr/domain/analysis/service/AnalysisServiceTest.java
  • src/test/java/com/mr/domain/history/service/HistoryServiceTest.java
  • src/test/java/com/mr/domain/playing/entity/PlayingTest.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/S3FileServiceTest.java
💤 Files with no reviewable changes (3)
  • src/test/java/com/mr/global/file/s3/service/S3FileServiceTest.java
  • src/main/java/com/mr/global/file/s3/service/S3FileService.java
  • src/main/java/com/mr/global/file/s3/dto/ValidatedFile.java

Comment thread src/main/java/com/mr/domain/playing/entity/Playing.java
Comment thread src/main/resources/db/migration/V5__migrate_recording_file_url_to_object_key.sql 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.

전체적으로 리팩터 방향이 깔끔합니다.

  • 마이그레이션 검증:
    V5__migrate_recording_file_url_to_object_key.sql에서 URL→Object Key 변환 후 DO $$ ... IF EXISTS ... RAISE EXCEPTION으로 검증해서, 변환이 잘못된 값이 남아있으면 Flyway가 트랜잭션째로 롤백하도록 만들어두신 부분 좋습니다. 빈 문자열 케이스(btrim(...) = '')까지 같이 잡아주셔서 꼼꼼했어요.
  • Presigned URL 발급 전 소유권 검증:
    PlayingService/HistoryService/AnalysisService 세 곳 다 s3FileService.createPresignedDownload() 호출 전에 validatePlayingOwner/validateOwner로 소유권을 먼저 확인하고, S3FileService 내부에서도 objectKeyGenerator.belongsToOwner()로 한 번 더 검증하는 이중 방어가 되어 있습니다. 다른 유저의 recordingObjectKey를 조작해서 presigned URL을 받아가는 경로가 없네요.
  • 죽은 코드 정리:
    extractObjectKey/buildFileUrl 같은 URL 기반 레거시 로직을 깔끔하게 삭제하고, 관련 테스트(mockFileUrl 등)까지 같이 정리하신 점도 좋습니다. 관련 있는 서비스/DTO/테스트가 다 일관되게 같이 수정돼서 반쯤 남은 코드가 없어요.

이대로 머지하셔도 될 것 같습니다 👍

@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.

URL을 DB에 저장하던 기존 방식에서 Object Key 기반으로 뼈대를 바꾸고 조회 시점에 Presigned URL을 발급하도록 잘 변경한 것 같습니다!
수고하셨습니다!

@p1001q
p1001q merged commit 4de001e into develop Aug 8, 2026
2 checks passed
@kimyw1018
kimyw1018 deleted the feat/#160-object-key-storage branch August 9, 2026 12:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ Feature - S3 파일 저장 구조를 Object Key 기반으로 변경

3 participants