refactor: 관리자 대학 이미지 업로드 경로 식별자 개선#782
Conversation
- 관리자 대학 생성/수정 API에서 multipart로 대학 정보와 이미지 파일을 함께 받도록 변경 - 이미지 URL을 요청 DTO에서 제거하고 서버에서 S3 업로드 결과를 저장하도록 변경 - 영문명 slug와 한글명 hash를 조합해 중복 영문명 간 S3 경로 충돌을 방지 - 업로드 또는 DB 저장 실패 시 새로 업로드된 이미지가 남지 않도록 보상 삭제 처리
- 대학 생성/수정 시 이미지 업로드 URL 저장과 기존 이미지 유지 동작 검증 - 한글명 기반 S3 경로 식별자와 중복 영문명 충돌 방지 검증 - 업로드 실패 보상 삭제와 삭제용 key 사용 여부 검증
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b029f6fae4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| @RequestPart("logoFile") MultipartFile logoFile, | ||
| @RequestPart("backgroundFile") MultipartFile backgroundFile |
There was a problem hiding this comment.
Raise multipart request limit for paired image uploads
This create endpoint now requires logoFile and backgroundFile in the same multipart request, but application.yml still caps spring.servlet.multipart.max-request-size at 10MB while each individual file is allowed up to 10MB. A logo and background that are each valid on their own, e.g. two ~6MB images, will be rejected by the servlet container before reaching this controller, so admins cannot create a university with two otherwise acceptable images unless the aggregate request limit is raised or uploads remain separate.
Useful? React with 👍 / 👎.
| if (uploadedFile == null) { | ||
| return; | ||
| } | ||
| deleteFile(uploadedFile.deletionKey()); |
There was a problem hiding this comment.
Delete the resized object during compensation
For images at or above the resize threshold, uploadFile stores deletionKey as original/... but returns fileUrl as resize/...webp; this compensation path deletes only the original key. If a later background upload or DB flush fails after the resize Lambda has already produced the resized logo/background, AdminHostUniversityService.deleteUploadedImages leaves the resize/... object orphaned even though the university was not saved. Delete both keys when they differ, or otherwise remove the generated resize key too.
Useful? React with 👍 / 👎.
| ); | ||
| hostUniversityRepository.flush(); | ||
| evictUnivApplyInfoDetailCaches(id); | ||
| return AdminHostUniversityDetailResponse.from(hostUniversity); |
There was a problem hiding this comment.
이미지 교체 성공 시 기존 S3 객체는 삭제하지 않는 걸로 보입니다!
실패 보상 삭제는 잘 들어갔지만, 수정 API에서 새 로고/배경 업로드가 성공하면 DB URL만 교체하고 이전 이미지는 남습니다. 기존 정책이 “고아 파일 허용”이면 괜찮지만, 비용/정리 관점에서는 누수입니다.
이 부분 확인 부탁드립니다!
There was a problem hiding this comment.
확인했습니다. 기존 이미지 교체 성공 후 old S3 객체 삭제는 현재 대학 이미지에는 적용되어 있지 않습니다.
다만 기존 로직을 확인해보니 프로필, 뉴스, 게시글 쪽은 old image 삭제를 수행하고 있지만, 모두 DB 트랜잭션 커밋 전 삭제하고 있습니다. 이 경우 DB 업데이트가 rollback되면 DB는 기존 URL을 유지하는데 S3 객체는 이미 삭제되는 문제가 생길 수 있습니다.
따라서 이 부분은 대학 이미지에만 즉시 추가하기보다, S3 객체 삭제 정책을 공통으로 정리하는 별도 PR에서 처리하는 것이 맞다고 판단했습니다. 후속 작업에서는 기존 이미지 삭제를 AFTER_COMMIT 기준으로 수행하도록 프로필/뉴스/게시글/대학 이미지 교체 로직을 함께 정리하겠습니다.
| backgroundFile, | ||
| UploadPath.ADMIN_UNIVERSITY_BACKGROUND, | ||
| directoryName | ||
| ); |
There was a problem hiding this comment.
이름 변경만 하고 이미지를 안 올리면 URL 경로와 새 이름 기반 디렉터리가 불일치할 수 있을 것 같습니다!
수정 시 이미지가 없으면 기존 URL을 유지합니다. 기능적으로는 자연스럽지만, 이번 PR 제목처럼 “경로 식별자 개선”이 목적이면 운영 데이터에서 같은 대학의 이름과 이미지 경로 식별자가 엇갈릴 수 있을 것으로 보이네요.. 확인 부탁드립니다!
There was a problem hiding this comment.
확인했습니다. 현재 경로 식별자는 이미지 업로드 시점의 저장 경로를 안정적으로 분리하기 위한 값이고 대학명 변경 시 기존 이미지를 자동 이동하지는 않습니다. 이름만 수정하는 경우 기존 이미지 URL을 유지하는 것이 의도된 동작입니다. S3 객체 rename은 실제로 copy+delete 작업이고 DB 커밋 이후 처리해야 하므로 수정 API에 암묵적으로 포함하면 실패 지점이 커질 수 있습니다. 이미지 경로를 새 이름 기준으로 맞추려면 새 이미지를 함께 업로드하도록 운영 정책을 두거나 별도 마이그레이션/정리 작업으로 다루는 편이 안전하다고 봅니다.
There was a problem hiding this comment.
확인했습니다! 그럼 단순 이름 변경에 대해서는 해당 API를 요청하지 않도록 방어로직을 짤 필요는 없는 걸까요? 정책으로 다룬다면 해당 부분에 대해서 명확히 FE 쪽과 협의가 필요해 보이긴 합니다!
There was a problem hiding this comment.
말씀하신 것처럼 FE/운영 정책은 명확히 맞춰야 할 것 같습니다. FE에서는 이름만 변경하는 경우 이미지 파일 없이 수정 API를 호출해도 되고 이미지까지 교체하려는 경우에만 새 파일을 함께 전달하는 방향으로 협의하겠습니다. PR 특이사항에도 이 정책을 명시하겠습니다.
Hexeong
left a comment
There was a problem hiding this comment.
고생하셨습니다! 추가 작업 내용은 까먹지 않게 이슈로 만들어주세요!
관련 이슈
작업 내용
관리자 대학 이미지 업로드를 대학 생성/수정 API에 통합
multipart/form-data로 대학 정보 JSON(request)과 로고/배경 이미지 파일을 함께 받도록 변경했습니다.multipart/form-data로 변경하고, 로고/배경 이미지 파일은 선택 입력으로 처리했습니다./file/admin/university/logo,/file/admin/university/background)는 제거했습니다.이유
이미지 URL 요청 필드 제거
AdminHostUniversityCreateRequest,AdminHostUniversityUpdateRequest에서logoImageUrl,backgroundImageUrl을 제거했습니다.HostUniversity이미지 필드에 저장하도록 변경했습니다.이유
대학별 S3 경로 식별자 개선
UploadDirectoryName.fromUniversityNames(englishName, koreanName)을 추가했습니다.이유
HostUniversity.englishName은 unique 제약이 없어 서로 다른 대학이 같은 영문명을 가질 수 있습니다.업로드 실패 보상 삭제 처리
warn로그만 남기고 원래 예외를 유지했습니다.이유
S3 삭제 API 캡슐화
S3Service.deleteFile(String)은 내부 구현으로 유지하고, 외부에서는deleteUploadedFile(UploadedFileUrlResponse)를 사용하도록 변경했습니다.UploadedFileUrlResponse에 내부 삭제용deletionKey를 추가하고, API 응답에는 노출되지 않도록@JsonIgnore를 적용했습니다.이유
S3Service내부로 캡슐화했습니다.테스트 보강
deleteUploadedFile이fileUrl이 아니라deletionKey로 삭제 요청을 보내는지 검증했습니다.이유
특이 사항
multipart/form-data로 변경됩니다.request,logoFile,backgroundFilepart가 필요합니다.requestpart가 필요하고,logoFile,backgroundFilepart는 선택입니다.리뷰 요구사항 (선택)
AdminHostUniversityService가 S3 업로드까지 담당하도록 변경한 결합도가 현재 유스케이스 기준에서 적절한지 확인 부탁드립니다.