diff --git a/src/main/java/org/tuna/zoopzoop/backend/domain/datasource/service/DataSourceService.java b/src/main/java/org/tuna/zoopzoop/backend/domain/datasource/service/DataSourceService.java index 34dfb9db..9eceb0ff 100644 --- a/src/main/java/org/tuna/zoopzoop/backend/domain/datasource/service/DataSourceService.java +++ b/src/main/java/org/tuna/zoopzoop/backend/domain/datasource/service/DataSourceService.java @@ -181,7 +181,7 @@ public void moveMany(List ids, int targetFolderId) { public void hardDeleteOne(int dataSourceId) { DataSource ds = dataSourceRepository.findById(dataSourceId) .orElseThrow(() -> new NoResultException("존재하지 않는 자료입니다.")); - deleteOwnedImageIfAny(ds); +// deleteOwnedImageIfAny(ds); dataSourceRepository.delete(ds); } @@ -190,7 +190,7 @@ public void hardDeleteMany(List ids) { if (ids == null || ids.isEmpty()) return; List list = dataSourceRepository.findAllById(ids); if (list.size() != ids.size()) throw new NoResultException("존재하지 않는 자료 포함"); - for (DataSource ds : list) deleteOwnedImageIfAny(ds); +// for (DataSource ds : list) deleteOwnedImageIfAny(ds); dataSourceRepository.deleteAll(list); } @@ -321,19 +321,15 @@ public String uploadThumbnailAndReturnFinalUrl(MultipartFile image, String key) // ===== S3 삭제 관련 유틸 ===== // 소유한 이미지가 있으면 S3에서 삭제 - private void deleteOwnedImageIfAny(DataSource ds) { - String url = ds.getImageUrl(); - if (url == null || url.isBlank()) return; - if (!isOurS3Url(url)) return; - - String key = extractKeyFromUrl(url); - if (key == null || key.isBlank()) return; - - try { - s3Service.delete(key); - } catch (Exception ignore) { - // 파일 삭제 실패로 전체 삭제를 롤백하지 않음 - // 필요하면 warn 로그 추가 + public void deleteIfOwnedByExactKey(String imageUrl, String expectedKey) { + if (imageUrl == null || imageUrl.isBlank() || expectedKey == null || expectedKey.isBlank()) return; + String key = extractKeyFromUrl(imageUrl); + if (expectedKey.equals(key)) { + try { + s3Service.delete(key); + } catch (Exception ignore) { + // S3 삭제 실패가 전체 트랜잭션을 막지 않도록 무시 + } } } diff --git a/src/main/java/org/tuna/zoopzoop/backend/domain/datasource/service/PersonalDataSourceService.java b/src/main/java/org/tuna/zoopzoop/backend/domain/datasource/service/PersonalDataSourceService.java index e711257a..1e9c126a 100644 --- a/src/main/java/org/tuna/zoopzoop/backend/domain/datasource/service/PersonalDataSourceService.java +++ b/src/main/java/org/tuna/zoopzoop/backend/domain/datasource/service/PersonalDataSourceService.java @@ -16,6 +16,7 @@ import org.tuna.zoopzoop.backend.domain.datasource.dto.DataSourceSearchCondition; import org.tuna.zoopzoop.backend.domain.datasource.dto.DataSourceSearchItem; import org.tuna.zoopzoop.backend.domain.datasource.dto.UpdateOutcome; +import org.tuna.zoopzoop.backend.domain.datasource.entity.DataSource; import org.tuna.zoopzoop.backend.domain.datasource.entity.Tag; import org.tuna.zoopzoop.backend.domain.datasource.repository.DataSourceRepository; @@ -79,9 +80,13 @@ public int create(int memberId, String sourceUrl, Integer folderIdOrZero, DataSo // hard delete @Transactional public int deleteOne(int memberId, int dataSourceId) { - dataSourceRepository.findByIdAndMemberId(dataSourceId, memberId) + DataSource ds = dataSourceRepository.findByIdAndMemberId(dataSourceId, memberId) .orElseThrow(() -> new NoResultException("존재하지 않는 자료입니다.")); + // S3 삭제 + String expectedKey = domain.thumbnailKeyForPersonal(memberId, dataSourceId); + domain.deleteIfOwnedByExactKey(ds.getImageUrl(), expectedKey); + domain.hardDeleteOne(dataSourceId); return dataSourceId; } @@ -98,6 +103,13 @@ public void deleteMany(int memberId, List ids) { missing.removeAll(new HashSet<>(existing)); throw new NoResultException("존재하지 않거나 소유자가 다른 자료 ID 포함: " + missing); } + // S3 삭제 + List list = dataSourceRepository.findAllById(ids); + for (DataSource ds : list) { + String expectedKey = domain.thumbnailKeyForPersonal(memberId, ds.getId()); + domain.deleteIfOwnedByExactKey(ds.getImageUrl(), expectedKey); + } + domain.hardDeleteMany(ids); }