Skip to content

[Refactor] S3 잔재 완전 제거 및 GCS 정합성 맞추기#210

Merged
gaeunee2 merged 2 commits into
devfrom
refactor/209-remove-s3-remnants
May 18, 2026
Merged

[Refactor] S3 잔재 완전 제거 및 GCS 정합성 맞추기#210
gaeunee2 merged 2 commits into
devfrom
refactor/209-remove-s3-remnants

Conversation

@gaeunee2
Copy link
Copy Markdown
Collaborator

@gaeunee2 gaeunee2 commented May 18, 2026

📌 관련 이슈

🏷️ PR 타입

  • ✨ 기능 추가 (Feature)
  • 🐛 버그 수정 (Bug Fix)
  • ♻️ 리팩토링 (Refactoring)
  • 📝 문서 수정 (Documentation)
  • 🎨 스타일 변경 (Style)
  • ✅ 테스트 추가 (Test)

📝 작업 내용

S3 잔재 완전 제거 및 GCS 정합성 맞추기
AWS S3에서 GCP GCS로 마이그레이션 완료 후 남아있는 S3 잔재를 완전히 제거하고, DB 스키마와 엔티티간 정합성을 맞춰 prod 배포 시 Schema validation 오류를 해결했습니다.

주요 변경사항:

  • V32 마이그레이션 추가: s3_keyobject_key, thumbnail_s3_keythumbnail_object_key
  • Asset 엔티티 필드명 변경: s3KeyobjectKey, thumbnailS3KeythumbnailObjectKey
  • DB 컬럼명과 엔티티 필드명 매핑을 위한 @Column(name="...") 추가
  • 모든 서비스, DTO, 테스트 코드에서 새 필드명으로 업데이트

수정된 파일들:

  • Asset.java - 엔티티 필드명 변경 및 컬럼 매핑 추가
  • V32 마이그레이션 파일 생성 - DB 컬럼명 변경
  • 11개 서비스 및 DTO 파일 - 필드 참조 업데이트

📸 스크린샷

빌드 성공 확인: ./gradlew clean build 통과
11개 파일 변경, 1개 마이그레이션 파일 생성

✅ 체크리스트

  • 코드 리뷰를 받을 준비가 완료되었습니다
  • 컴파일 테스트를 통과했습니다
  • 관련 이슈와 연결했습니다
  • 코드 스타일 가이드를 준수했습니다
  • 셀프 리뷰를 완료했습니다

📎 기타 참고사항

  • 기존 Flyway 마이그레이션 파일(V0~V31) 절대 수정 금지 (체크섬 오류 방지)
  • GCS 관련 코드/설정은 이미 정상 작동하므로 그대로 유지
  • prod 프로파일의 ddl-auto: validate와 100% 호환되도록 수정
  • 이제 prod 환경에서 Schema-validation 오류 없이 정상 부팅 가능

Summary by CodeRabbit

릴리스 노트

  • Refactor
    • 에셋 관리, 대화, 사용자 서비스 전반에서 스토리지 키 명명 규칙을 업데이트했습니다.
    • 데이터베이스 스키마를 스토리지 키 용어 변경에 맞게 업데이트했습니다.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

Warning

Rate limit exceeded

@gaeunee2 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 43 minutes and 40 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 713ca012-22f6-4536-8f36-9541858f7da4

📥 Commits

Reviewing files that changed from the base of the PR and between 0aa2fab and aabd4fe.

📒 Files selected for processing (4)
  • src/main/java/com/proovy/domain/asset/service/AssetsServiceImpl.java
  • src/main/java/com/proovy/domain/note/service/NoteServiceImpl.java
  • src/main/java/com/proovy/domain/storage/service/StorageService.java
  • src/main/java/com/proovy/domain/user/service/UserService.java
📝 Walkthrough

Walkthrough

Asset 엔티티의 S3 잔재 필드(s3Key, thumbnailS3Key)를 GCS 통일 필드(objectKey, thumbnailObjectKey)로 변경하고, DB 마이그레이션과 함께 모든 서비스/DTO 계층에서 해당 필드를 참조하는 게터 호출을 일관되게 업데이트합니다.

Changes

S3 필드명 제거 및 GCS 정합성 통일

Layer / File(s) Summary
Asset 엔티티 필드 및 메서드 시그니처 변경
src/main/java/com/proovy/domain/asset/entity/Asset.java
s3KeythumbnailS3Key 필드를 objectKeythumbnailObjectKey로 변경하고, 생성자 파라미터와 updateThumbnail 메서드 시그니처를 업데이트합니다.
데이터베이스 스키마 마이그레이션
src/main/resources/db/migration/V32__rename_s3_columns_to_object_keys.sql
Flyway V32 마이그레이션에서 assets 테이블의 s3_keyobject_key, thumbnail_s3_keythumbnail_object_key 컬럼명을 변경하여 엔티티와 DB 정합성을 맞춥니다.
에셋 서비스의 저장소 작업
src/main/java/com/proovy/domain/asset/service/AssetsServiceImpl.java
업로드 URL 생성, 다운로드 URL 생성, 업로드 확인, 썸네일 생성/URL 조회, 에셋 삭제 시 getObjectKey()getThumbnailObjectKey() 게터를 사용하도록 변경합니다.
응답 DTO 및 채팅/대화 서비스 통합
src/main/java/com/proovy/domain/conversation/dto/response/UploadConfirmResponse.java, src/main/java/com/proovy/domain/conversation/dto/response/CanvasImageUploadResponse.java, src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java, src/main/java/com/proovy/domain/conversation/service/ConversationQueryServiceImpl.java
응답 필드 매핑과 Asset 빌더 호출에서 새 필드명 게터를 사용하고, presigned URL 생성 시 getObjectKey()를 참조하도록 업데이트합니다.
노트 및 스토리지 서비스의 필드 참조
src/main/java/com/proovy/domain/note/service/NoteServiceImpl.java, src/main/java/com/proovy/domain/storage/service/StorageService.java
노트 삭제, 썸네일/다운로드 URL 생성, 스토리지 사용량 조회 시 새 필드명 게터로 변경합니다.
사용자 데이터 삭제 흐름
src/main/java/com/proovy/domain/user/service/UserService.java
deleteUserData 메서드에서 삭제 대상 키 수집과 afterCommit 삭제 실행 시 getObjectKey(), getThumbnailObjectKey()를 사용하고 로그 메시지를 GCS 기준으로 업데이트합니다.
테스트 데이터 업데이트
src/test/java/com/proovy/domain/storage/service/StorageServiceTest.java
테스트 에셋의 빌더 호출에서 s3Key(...) 대신 objectKey(...)를 사용하도록 변경합니다.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • Team-Proovy/Proovy-server#146: ChatServiceImpl의 convertAssetIdsToUrls 메서드에서 presigned 다운로드 URL 생성 시 asset.getS3Key()asset.getObjectKey() 변경이 직접적으로 연결됩니다.
  • Team-Proovy/Proovy-server#139: 썸네일 생성/갱신 흐름에서 Asset의 썸네일 키(updateThumbnail 메서드 및 응답 매핑)를 다루므로 코드 레벨에서 교차합니다.
  • Team-Proovy/Proovy-server#141: 업로드 확인 시 UploadConfirmResponseAssetsServiceImpl 썸네일 키 매핑이 교차합니다.

Suggested labels

refactor, 📚Assets

Suggested reviewers

  • haein45
  • Ncs89

Poem

🐰 S3의 유령들을 쫓아내고
GCS의 이름으로 통일하니
스키마와 코드가 손잡고 춤을 춘다네
마이그레이션 완성, 해피엔딩!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.09% 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
Title check ✅ Passed PR 제목은 주요 변경사항인 S3 잔재 제거 및 GCS 정합성 맞추기를 명확하게 요약하고 있습니다.
Linked Issues check ✅ Passed PR이 이슈 #209의 모든 요구사항을 충족합니다: S3 잔재 제거, 엔티티 필드명 변경, DB 스키마 정합성, 마이그레이션 파일 추가, 빌드 통과 확인 완료.
Out of Scope Changes check ✅ Passed 모든 변경사항이 이슈 #209의 범위 내에 있으며, S3 잔재 제거 및 GCS 정합성 맞추기 목표에 직결되어 있습니다.
Description check ✅ Passed PR 설명이 템플릿의 대부분 필수 섹션을 포함하고 있으며, 작업 내용, 변경사항, 체크리스트가 충실하게 작성되어 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/209-remove-s3-remnants

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 and usage tips.

@gaeunee2 gaeunee2 changed the title refactor: S3 잔재 완전 제거 및 GCS 정합성 맞추기 [Refactor] S3 잔재 완전 제거 및 GCS 정합성 맞추기 May 18, 2026
@gaeunee2 gaeunee2 requested a review from chowon442 May 18, 2026 14:20
@gaeunee2 gaeunee2 self-assigned this May 18, 2026
Copy link
Copy Markdown

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

🧹 Nitpick comments (4)
src/main/java/com/proovy/domain/asset/service/AssetsServiceImpl.java (1)

97-97: ⚡ Quick win

변경한 경로에서는 로컬 이름도 objectKey 기준으로 맞춰 주세요.

여기서는 이미 getObjectKey() / getThumbnailObjectKey()를 쓰고 있는데 로컬 변수명이 아직 s3Key, thumbnailS3Key입니다. 이번 PR 목표가 S3 잔재 제거라면, 최소한 수정한 라인에서는 이름도 같이 정리해 두는 편이 이후 검색/추적에서 덜 헷갈립니다.

♻️ Suggested cleanup
-                .objectKey(s3Key)
+                .objectKey(objectKey)
...
-        final String s3Key = asset.getObjectKey();
+        final String objectKey = asset.getObjectKey();
...
-        final String s3Key = asset.getObjectKey();
-        final String thumbnailS3Key = asset.getThumbnailObjectKey();
+        final String objectKey = asset.getObjectKey();
+        final String thumbnailObjectKey = asset.getThumbnailObjectKey();

Also applies to: 220-220, 296-297

🤖 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/proovy/domain/asset/service/AssetsServiceImpl.java` at line
97, Rename the local variables `s3Key` and `thumbnailS3Key` to `objectKey` and
`thumbnailObjectKey` respectively in AssetsServiceImpl where you already call
`getObjectKey()` / `getThumbnailObjectKey()` so local names match the accessor
names; update all usages in the method(s) (e.g., the builder call
`.objectKey(s3Key)` and analogous thumbnail usage) to use the new variable names
to keep naming consistent and remove S3-specific remnants.
src/main/java/com/proovy/domain/storage/service/StorageService.java (1)

69-77: ⚡ Quick win

벌크 삭제에서도 같은 object key를 두 번 넣을 수 있습니다.

썸네일이 원본 fallback인 asset이면 여기서 objectKeythumbnailObjectKey가 동일해질 수 있습니다. 삭제 대상에는 원본과 다른 썸네일 key만 넣어 중복 호출을 막는 편이 좋겠습니다.

♻️ Suggested fix
         for (Asset asset : assets) {
             // 원본 파일
             s3KeysToDelete.add(asset.getObjectKey());
             totalFileSize += asset.getFileSize();

             // 썸네일 파일
-            if (asset.getThumbnailObjectKey() != null) {
+            if (asset.getThumbnailObjectKey() != null
+                    && !asset.getThumbnailObjectKey().equals(asset.getObjectKey())) {
                 s3KeysToDelete.add(asset.getThumbnailObjectKey());
             }
         }
🤖 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/proovy/domain/storage/service/StorageService.java` around
lines 69 - 77, In StorageService (inside the loop that builds s3KeysToDelete),
avoid adding duplicate S3 keys by skipping the thumbnail when
asset.getThumbnailObjectKey() equals asset.getObjectKey(); alternatively replace
the collection with a Set (e.g., use a HashSet for s3KeysToDelete) so
add(asset.getThumbnailObjectKey()) will be a no-op for duplicates — ensure
totalFileSize still sums original asset.getFileSize() only once and that
downstream code expecting a list handles conversion from the Set if needed.
src/main/java/com/proovy/domain/user/service/UserService.java (1)

208-216: ⚡ Quick win

회원 탈퇴 후처리도 동일 key 중복 삭제를 피하는 게 좋습니다.

썸네일 fallback으로 thumbnailObjectKey == objectKey인 asset이 있으면 afterCommit에서 같은 파일을 연속으로 삭제하게 됩니다. 여기서도 썸네일 key는 원본과 다를 때만 수집해 두는 편이 안전합니다.

♻️ Suggested fix
         assetRepository.findAllByUserId(userId).forEach(asset -> {
             if (asset.getObjectKey() != null) {
                 objectKeys.add(asset.getObjectKey());
             }
-            if (asset.getThumbnailObjectKey() != null) {
+            if (asset.getThumbnailObjectKey() != null
+                    && !asset.getThumbnailObjectKey().equals(asset.getObjectKey())) {
                 objectKeys.add(asset.getThumbnailObjectKey());
             }
         });

Also applies to: 233-239

🤖 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/proovy/domain/user/service/UserService.java` around lines
208 - 216, The current collection loop in assetRepository.findAllByUserId(...)
collects both asset.getObjectKey() and asset.getThumbnailObjectKey()
unconditionally, which can cause duplicate deletes when thumbnailObjectKey
equals objectKey (e.g., during afterCommit deletion). Change the collection
logic so you only add thumbnailObjectKey when it is non-null AND not equal to
asset.getObjectKey(); ensure the same de-duplication is applied in the other
block mentioned (lines 233-239) and anywhere afterCommit performs deletes using
the objectKeys list to avoid double-deleting the same GCS key.
src/main/java/com/proovy/domain/note/service/NoteServiceImpl.java (1)

367-374: ⚡ Quick win

원본 fallback 썸네일까지 그대로 넣으면 삭제 키가 중복됩니다.

confirmUpload() 쪽에서 썸네일 생성 실패 시 thumbnailObjectKey를 원본 objectKey로 fallback 할 수 있어서, 여기서는 같은 asset의 key가 두 번 수집될 수 있습니다. 썸네일 key는 원본과 다를 때만 추가해 두는 편이 안전합니다.

♻️ Suggested fix
         for (Asset asset : assets) {
             if (asset.getObjectKey() != null) {
                 s3KeysToDelete.add(asset.getObjectKey());
                 freedStorageBytes += asset.getFileSize();
             }
-            if (asset.getThumbnailObjectKey() != null) {
+            if (asset.getThumbnailObjectKey() != null
+                    && !asset.getThumbnailObjectKey().equals(asset.getObjectKey())) {
                 s3KeysToDelete.add(asset.getThumbnailObjectKey());
             }
         }
🤖 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/proovy/domain/note/service/NoteServiceImpl.java` around
lines 367 - 374, In NoteServiceImpl inside the loop that collects s3KeysToDelete
and freedStorageBytes, avoid adding the thumbnail key when it equals the
original object key (because confirmUpload may have fallen back
thumbnailObjectKey to objectKey); change the logic that currently
unconditionally adds asset.getThumbnailObjectKey() to only add it when
thumbnailObjectKey is non-null AND not equal to asset.getObjectKey(), ensuring
s3KeysToDelete and freedStorageBytes are not duplicated for the same asset.
🤖 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.

Nitpick comments:
In `@src/main/java/com/proovy/domain/asset/service/AssetsServiceImpl.java`:
- Line 97: Rename the local variables `s3Key` and `thumbnailS3Key` to
`objectKey` and `thumbnailObjectKey` respectively in AssetsServiceImpl where you
already call `getObjectKey()` / `getThumbnailObjectKey()` so local names match
the accessor names; update all usages in the method(s) (e.g., the builder call
`.objectKey(s3Key)` and analogous thumbnail usage) to use the new variable names
to keep naming consistent and remove S3-specific remnants.

In `@src/main/java/com/proovy/domain/note/service/NoteServiceImpl.java`:
- Around line 367-374: In NoteServiceImpl inside the loop that collects
s3KeysToDelete and freedStorageBytes, avoid adding the thumbnail key when it
equals the original object key (because confirmUpload may have fallen back
thumbnailObjectKey to objectKey); change the logic that currently
unconditionally adds asset.getThumbnailObjectKey() to only add it when
thumbnailObjectKey is non-null AND not equal to asset.getObjectKey(), ensuring
s3KeysToDelete and freedStorageBytes are not duplicated for the same asset.

In `@src/main/java/com/proovy/domain/storage/service/StorageService.java`:
- Around line 69-77: In StorageService (inside the loop that builds
s3KeysToDelete), avoid adding duplicate S3 keys by skipping the thumbnail when
asset.getThumbnailObjectKey() equals asset.getObjectKey(); alternatively replace
the collection with a Set (e.g., use a HashSet for s3KeysToDelete) so
add(asset.getThumbnailObjectKey()) will be a no-op for duplicates — ensure
totalFileSize still sums original asset.getFileSize() only once and that
downstream code expecting a list handles conversion from the Set if needed.

In `@src/main/java/com/proovy/domain/user/service/UserService.java`:
- Around line 208-216: The current collection loop in
assetRepository.findAllByUserId(...) collects both asset.getObjectKey() and
asset.getThumbnailObjectKey() unconditionally, which can cause duplicate deletes
when thumbnailObjectKey equals objectKey (e.g., during afterCommit deletion).
Change the collection logic so you only add thumbnailObjectKey when it is
non-null AND not equal to asset.getObjectKey(); ensure the same de-duplication
is applied in the other block mentioned (lines 233-239) and anywhere afterCommit
performs deletes using the objectKeys list to avoid double-deleting the same GCS
key.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 92aa0870-22ce-40c2-b769-b64b4a4d7c46

📥 Commits

Reviewing files that changed from the base of the PR and between 99727fd and 0aa2fab.

📒 Files selected for processing (11)
  • src/main/java/com/proovy/domain/asset/dto/response/UploadConfirmResponse.java
  • src/main/java/com/proovy/domain/asset/entity/Asset.java
  • src/main/java/com/proovy/domain/asset/service/AssetsServiceImpl.java
  • src/main/java/com/proovy/domain/conversation/dto/response/CanvasImageUploadResponse.java
  • src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java
  • src/main/java/com/proovy/domain/conversation/service/ConversationQueryServiceImpl.java
  • src/main/java/com/proovy/domain/note/service/NoteServiceImpl.java
  • src/main/java/com/proovy/domain/storage/service/StorageService.java
  • src/main/java/com/proovy/domain/user/service/UserService.java
  • src/main/resources/db/migration/V32__rename_s3_columns_to_object_keys.sql
  • src/test/java/com/proovy/domain/storage/service/StorageServiceTest.java

Copy link
Copy Markdown
Member

@chowon442 chowon442 left a comment

Choose a reason for hiding this comment

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

이거 누가 작업한거죠? 코드래빗이 할 말을 잃어버렸네요... 🐇🐇🐇

@gaeunee2
Copy link
Copy Markdown
Collaborator Author

이거 누가 작업한거죠? 코드래빗이 할 말을 잃어버렸네요... 🐇🐇🐇

₍ᐢ..ᐢ₎♡̷̷̷ ༘☆ 코드가은

@gaeunee2 gaeunee2 merged commit 5fd81dd into dev May 18, 2026
2 checks passed
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 잔재 완전 제거 및 GCS 정합성 맞추기

2 participants