From 7f58bc0ee6f89ec5be85db05c442d515b5648bf7 Mon Sep 17 00:00:00 2001 From: YoonJuHo Date: Wed, 5 Feb 2025 19:28:32 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20MomentImage=20update=20=EC=8B=9C=20?= =?UTF-8?q?=EC=88=98=EB=8F=99=20=EC=82=AD=EC=A0=9C=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?#591=20(#593)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: CascadeType 변경으로 이미지 수정 로직 수정 * refactor: 이미지 수동 삭제 로직 구현 * refactor: Batch 네이밍 Bulk로 변경 * refactor: URL 동등성 비교 메서드 추출 * refactor: 변수 오타 수정 * refactor: 메서드 재사용 * fix: test import 수정 --- .../comment/repository/CommentRepository.java | 2 +- .../repository/MemoryMemberRepository.java | 2 +- .../memory/service/MemoryService.java | 8 ++-- .../com/staccato/moment/domain/Moment.java | 17 +++------ .../staccato/moment/domain/MomentImage.java | 5 ++- .../staccato/moment/domain/MomentImages.java | 36 ++++++++++++++---- .../repository/MomentImageRepository.java | 8 ++-- .../moment/repository/MomentRepository.java | 2 +- .../moment/service/MomentService.java | 37 +++++++++++++------ .../repository/CommentRepositoryTest.java | 4 +- .../MemoryMemberRepositoryTest.java | 4 +- .../moment/domain/MomentImagesTest.java | 21 +++++++++++ .../repository/MomentImageRepositoryTest.java | 29 +++++++++++++-- .../repository/MomentRepositoryTest.java | 4 +- .../moment/service/MomentServiceTest.java | 16 ++++++-- 15 files changed, 140 insertions(+), 55 deletions(-) diff --git a/backend/src/main/java/com/staccato/comment/repository/CommentRepository.java b/backend/src/main/java/com/staccato/comment/repository/CommentRepository.java index a128142c..ebfaf024 100644 --- a/backend/src/main/java/com/staccato/comment/repository/CommentRepository.java +++ b/backend/src/main/java/com/staccato/comment/repository/CommentRepository.java @@ -12,5 +12,5 @@ public interface CommentRepository extends JpaRepository { @Modifying @Query("DELETE FROM Comment c WHERE c.moment.id IN :momentIds") - void deleteAllByMomentIdInBatch(@Param("momentIds") List momentIds); + void deleteAllByMomentIdInBulk(@Param("momentIds") List momentIds); } diff --git a/backend/src/main/java/com/staccato/memory/repository/MemoryMemberRepository.java b/backend/src/main/java/com/staccato/memory/repository/MemoryMemberRepository.java index 0baa73d2..9112289f 100644 --- a/backend/src/main/java/com/staccato/memory/repository/MemoryMemberRepository.java +++ b/backend/src/main/java/com/staccato/memory/repository/MemoryMemberRepository.java @@ -25,5 +25,5 @@ public interface MemoryMemberRepository extends JpaRepository images = new ArrayList<>(); public MomentImages(List addedImages) { @@ -38,16 +38,36 @@ protected void addAll(MomentImages newMomentImages, Moment moment) { }); } - protected void update(MomentImages momentImages, Moment moment) { - removeExistsImages(new ArrayList<>(images)); - addAll(momentImages, moment); + public boolean isNotEmpty() { + return !images.isEmpty(); } - private void removeExistsImages(List originalImages) { - originalImages.forEach(this.images::remove); + public List findImagesNotPresentIn(MomentImages targetMomentImages) { + return images.stream() + .filter(image -> !targetMomentImages.contains(image)) + .toList(); } - public boolean isNotEmpty() { - return !images.isEmpty(); + protected void update(MomentImages newMomentImages, Moment moment) { + removeExistImages(newMomentImages); + saveNewImages(newMomentImages, moment); + } + + private void removeExistImages(MomentImages newMomentImages) { + List momentImages = findImagesNotPresentIn(newMomentImages); + images.removeAll(momentImages); + } + + private void saveNewImages(MomentImages newMomentImages, Moment moment) { + List momentImages = newMomentImages.findImagesNotPresentIn(this); + momentImages.forEach(image -> { + this.images.add(image); + image.belongTo(moment); + }); + } + + private boolean contains(MomentImage momentImage) { + return this.images.stream() + .anyMatch(image -> image.isSame(momentImage)); } } diff --git a/backend/src/main/java/com/staccato/moment/repository/MomentImageRepository.java b/backend/src/main/java/com/staccato/moment/repository/MomentImageRepository.java index 85b62a77..fd163a99 100644 --- a/backend/src/main/java/com/staccato/moment/repository/MomentImageRepository.java +++ b/backend/src/main/java/com/staccato/moment/repository/MomentImageRepository.java @@ -1,7 +1,6 @@ package com.staccato.moment.repository; import java.util.List; -import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -9,9 +8,12 @@ import com.staccato.moment.domain.MomentImage; public interface MomentImageRepository extends JpaRepository { - Optional findFirstByMomentId(long momentId); @Modifying @Query("DELETE FROM MomentImage mi WHERE mi.moment.id In :momentIds") - void deleteAllByMomentIdInBatch(@Param("momentIds") List momentIds); + void deleteAllByMomentIdInBulk(@Param("momentIds") List momentIds); + + @Modifying + @Query("DELETE FROM MomentImage mi WHERE mi.id In :ids") + void deleteAllByIdInBulk(@Param("ids") List ids); } diff --git a/backend/src/main/java/com/staccato/moment/repository/MomentRepository.java b/backend/src/main/java/com/staccato/moment/repository/MomentRepository.java index 6b455122..0af740da 100644 --- a/backend/src/main/java/com/staccato/moment/repository/MomentRepository.java +++ b/backend/src/main/java/com/staccato/moment/repository/MomentRepository.java @@ -18,5 +18,5 @@ public interface MomentRepository extends JpaRepository { @Modifying @Query("DELETE FROM Moment m WHERE m.memory.id = :memoryId") - void deleteAllByMemoryIdInBatch(@Param("memoryId") Long memoryId); + void deleteAllByMemoryIdInBulk(@Param("memoryId") Long memoryId); } diff --git a/backend/src/main/java/com/staccato/moment/service/MomentService.java b/backend/src/main/java/com/staccato/moment/service/MomentService.java index 074c65fb..f1d08224 100644 --- a/backend/src/main/java/com/staccato/moment/service/MomentService.java +++ b/backend/src/main/java/com/staccato/moment/service/MomentService.java @@ -12,6 +12,8 @@ import com.staccato.memory.repository.MemoryRepository; import com.staccato.moment.domain.Feeling; import com.staccato.moment.domain.Moment; +import com.staccato.moment.domain.MomentImage; +import com.staccato.moment.domain.MomentImages; import com.staccato.moment.repository.MomentImageRepository; import com.staccato.moment.repository.MomentRepository; import com.staccato.moment.service.dto.request.FeelingRequest; @@ -43,11 +45,6 @@ public MomentIdResponse createMoment(MomentRequest momentRequest, Member member) return new MomentIdResponse(moment.getId()); } - private Memory getMemoryById(long memoryId) { - return memoryRepository.findById(memoryId) - .orElseThrow(() -> new StaccatoException("요청하신 추억을 찾을 수 없어요.")); - } - public MomentLocationResponses readAllMoment(Member member) { return new MomentLocationResponses(momentRepository.findAllByMemory_MemoryMembers_Member(member) .stream() @@ -72,21 +69,32 @@ public void updateMomentById( Memory targetMemory = getMemoryById(momentRequest.memoryId()); validateMemoryOwner(targetMemory, member); - Moment updatedMoment = momentRequest.toMoment(targetMemory); - moment.update(updatedMoment); + Moment newMoment = momentRequest.toMoment(targetMemory); + MomentImages originMomentImages = moment.getMomentImages(); + List images = originMomentImages.findImagesNotPresentIn(newMoment.getMomentImages()); + removeImages(images); + + moment.update(newMoment); } - private Moment getMomentById(long momentId) { - return momentRepository.findById(momentId) - .orElseThrow(() -> new StaccatoException("요청하신 스타카토를 찾을 수 없어요.")); + private void removeImages(List images) { + List ids = images.stream() + .map(MomentImage::getId) + .toList(); + momentImageRepository.deleteAllByIdInBulk(ids); + } + + private Memory getMemoryById(long memoryId) { + return memoryRepository.findById(memoryId) + .orElseThrow(() -> new StaccatoException("요청하신 추억을 찾을 수 없어요.")); } @Transactional public void deleteMomentById(long momentId, Member member) { momentRepository.findById(momentId).ifPresent(moment -> { validateMemoryOwner(moment.getMemory(), member); - commentRepository.deleteAllByMomentIdInBatch(List.of(momentId)); - momentImageRepository.deleteAllByMomentIdInBatch(List.of(momentId)); + commentRepository.deleteAllByMomentIdInBulk(List.of(momentId)); + momentImageRepository.deleteAllByMomentIdInBulk(List.of(momentId)); momentRepository.deleteById(momentId); }); } @@ -99,6 +107,11 @@ public void updateMomentFeelingById(long momentId, Member member, FeelingRequest moment.changeFeeling(feeling); } + private Moment getMomentById(long momentId) { + return momentRepository.findById(momentId) + .orElseThrow(() -> new StaccatoException("요청하신 스타카토를 찾을 수 없어요.")); + } + private void validateMemoryOwner(Memory memory, Member member) { if (memory.isNotOwnedBy(member)) { throw new ForbiddenException(); diff --git a/backend/src/test/java/com/staccato/comment/repository/CommentRepositoryTest.java b/backend/src/test/java/com/staccato/comment/repository/CommentRepositoryTest.java index 6c28a457..ac8c57f4 100644 --- a/backend/src/test/java/com/staccato/comment/repository/CommentRepositoryTest.java +++ b/backend/src/test/java/com/staccato/comment/repository/CommentRepositoryTest.java @@ -34,7 +34,7 @@ class CommentRepositoryTest extends RepositoryTest { @DisplayName("특정 스타카토의 id를 여러개를 가지고 있는 모든 댓글들을 삭제한다.") @Test - void deleteAllByMomentIdInBatch() { + void deleteAllByMomentIdInBulk() { // given Member member = memberRepository.save(MemberFixture.create()); Memory memory = memoryRepository.save(MemoryFixture.create(null, null)); @@ -46,7 +46,7 @@ void deleteAllByMomentIdInBatch() { momentRepository.save(moment2); // when - commentRepository.deleteAllByMomentIdInBatch(List.of(moment1.getId(), moment2.getId())); + commentRepository.deleteAllByMomentIdInBulk(List.of(moment1.getId(), moment2.getId())); em.flush(); em.clear(); diff --git a/backend/src/test/java/com/staccato/memory/repository/MemoryMemberRepositoryTest.java b/backend/src/test/java/com/staccato/memory/repository/MemoryMemberRepositoryTest.java index d7b615fc..2ca5c751 100644 --- a/backend/src/test/java/com/staccato/memory/repository/MemoryMemberRepositoryTest.java +++ b/backend/src/test/java/com/staccato/memory/repository/MemoryMemberRepositoryTest.java @@ -81,7 +81,7 @@ void findAllByMemberIdAndDateWhenNull() { @DisplayName("특정 추억의 id를 가지고 있는 모든 MemoryMember를 삭제한다.") @Test - void deleteAllByMemoryIdInBatch() { + void deleteAllByMemoryIdInBulk() { // given Member member = memberRepository.save(MemberFixture.create()); Member member2 = memberRepository.save(MemberFixture.create("hotea")); @@ -90,7 +90,7 @@ void deleteAllByMemoryIdInBatch() { MemoryMember memoryMember2 = memoryMemberRepository.save(new MemoryMember(member2, memory)); // when - memoryMemberRepository.deleteAllByMemoryIdInBatch(memory.getId()); + memoryMemberRepository.deleteAllByMemoryIdInBulk(memory.getId()); entityManager.flush(); entityManager.clear(); diff --git a/backend/src/test/java/com/staccato/moment/domain/MomentImagesTest.java b/backend/src/test/java/com/staccato/moment/domain/MomentImagesTest.java index d93a9ed3..d43669ae 100644 --- a/backend/src/test/java/com/staccato/moment/domain/MomentImagesTest.java +++ b/backend/src/test/java/com/staccato/moment/domain/MomentImagesTest.java @@ -69,4 +69,25 @@ void update() { () -> assertThat(images.size()).isEqualTo(2) ); } + + @DisplayName("포함되지 않는 사진들을 선별할 수 있다.") + @Test + void findImagesNotPresentIn() { + // given + MomentImages existingImages = new MomentImages(List.of("picture1", "picture3")); + MomentImages newImages = new MomentImages(List.of("picture1", "picture4")); + + // when + List remainingExistingImages = existingImages.findImagesNotPresentIn(newImages); + List remainingNewImages = newImages.findImagesNotPresentIn(existingImages); + + // then + assertAll( + + () -> assertThat(remainingExistingImages.size()).isEqualTo(1), + () -> assertThat(remainingNewImages.size()).isEqualTo(1), + () -> assertThat(remainingExistingImages.get(0).getImageUrl()).isEqualTo("picture3"), + () -> assertThat(remainingNewImages.get(0).getImageUrl()).isEqualTo("picture4") + ); + } } diff --git a/backend/src/test/java/com/staccato/moment/repository/MomentImageRepositoryTest.java b/backend/src/test/java/com/staccato/moment/repository/MomentImageRepositoryTest.java index a332b070..64edb448 100644 --- a/backend/src/test/java/com/staccato/moment/repository/MomentImageRepositoryTest.java +++ b/backend/src/test/java/com/staccato/moment/repository/MomentImageRepositoryTest.java @@ -14,6 +14,7 @@ import com.staccato.memory.domain.Memory; import com.staccato.memory.repository.MemoryRepository; import com.staccato.moment.domain.Moment; +import com.staccato.moment.domain.MomentImage; import com.staccato.moment.domain.MomentImages; import static org.assertj.core.api.Assertions.assertThat; @@ -29,9 +30,9 @@ class MomentImageRepositoryTest extends RepositoryTest { @PersistenceContext private EntityManager em; - @DisplayName("특정 스타카토의 id 여러개를 가지고 있는 모든 스타카토 이미지들을 삭제한다.") + @DisplayName("특정 스타카토의 id 여러개를 가지고 있는 모든 스타카토 이미지들을 벌크 삭제한다.") @Test - void deleteAllByMomentIdInBatch() { + void deleteAllByMomentIdInBulk() { // given Memory memory = memoryRepository.save(MemoryFixture.create(LocalDate.of(2023, 12, 31), LocalDate.of(2024, 1, 10))); Moment moment1 = momentRepository.save(MomentFixture @@ -40,8 +41,7 @@ void deleteAllByMomentIdInBatch() { .createWithImages(memory, LocalDateTime.of(2023, 12, 31, 22, 20), new MomentImages(List.of("url1", "url2")))); // when - momentImageRepository.deleteAllByMomentIdInBatch(List.of(moment1.getId(), moment2.getId())); - em.flush(); + momentImageRepository.deleteAllByMomentIdInBulk(List.of(moment1.getId(), moment2.getId())); em.clear(); // then @@ -53,4 +53,25 @@ void deleteAllByMomentIdInBatch() { .isNotEmpty()).isFalse() ); } + + @DisplayName("특정 스타카토 이미지들을 벌크 삭제한다.") + @Test + void deleteAllByIdInBulk() { + // given + Memory memory = memoryRepository.save(MemoryFixture.create(LocalDate.of(2023, 12, 31), LocalDate.of(2024, 1, 10))); + MomentImages momentImages = new MomentImages(List.of("url1", "url2", "url3")); + Moment moment = momentRepository.save(MomentFixture.createWithImages(memory, LocalDateTime.of(2023, 12, 31, 22, 20), momentImages)); + + List imageIds = moment.getMomentImages() + .getImages() + .stream() + .map(MomentImage::getId) + .toList(); + + // when + momentImageRepository.deleteAllByIdInBulk(imageIds); + + // then + assertThat(momentImageRepository.findAll()).isEmpty(); + } } diff --git a/backend/src/test/java/com/staccato/moment/repository/MomentRepositoryTest.java b/backend/src/test/java/com/staccato/moment/repository/MomentRepositoryTest.java index 8374a89f..f6db91ba 100644 --- a/backend/src/test/java/com/staccato/moment/repository/MomentRepositoryTest.java +++ b/backend/src/test/java/com/staccato/moment/repository/MomentRepositoryTest.java @@ -69,7 +69,7 @@ void findAllByMemory_MemoryMembers_Member() { @DisplayName("특정 추억의 id를 가진 모든 스타카토를 삭제한다.") @Test - void deleteAllByMemoryIdInBatch() { + void deleteAllByMemoryIdInBulk() { // given Member member = memberRepository.save(MemberFixture.create()); Memory memory = memoryRepository.save(MemoryFixture.create(LocalDate.of(2023, 12, 31), LocalDate.of(2024, 1, 10))); @@ -80,7 +80,7 @@ void deleteAllByMemoryIdInBatch() { Moment moment2 = momentRepository.save(MomentFixture.create(memory, LocalDateTime.of(2024, 1, 1, 22, 21))); // when - momentRepository.deleteAllByMemoryIdInBatch(memory.getId()); + momentRepository.deleteAllByMemoryIdInBulk(memory.getId()); entityManager.flush(); entityManager.clear(); diff --git a/backend/src/test/java/com/staccato/moment/service/MomentServiceTest.java b/backend/src/test/java/com/staccato/moment/service/MomentServiceTest.java index 32d3cd38..eee954d2 100644 --- a/backend/src/test/java/com/staccato/moment/service/MomentServiceTest.java +++ b/backend/src/test/java/com/staccato/moment/service/MomentServiceTest.java @@ -1,5 +1,6 @@ package com.staccato.moment.service; +import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; @@ -22,6 +23,7 @@ import com.staccato.memory.repository.MemoryRepository; import com.staccato.moment.domain.Feeling; import com.staccato.moment.domain.Moment; +import com.staccato.moment.domain.MomentImage; import com.staccato.moment.domain.MomentImages; import com.staccato.moment.repository.MomentImageRepository; import com.staccato.moment.repository.MomentRepository; @@ -77,7 +79,8 @@ void createMomentWithMomentImages() { // then assertAll( () -> assertThat(momentRepository.findById(momentId)).isNotEmpty(), - () -> assertThat(momentImageRepository.findFirstByMomentId(momentId)).isNotEmpty() + () -> assertThat(momentImageRepository.findAll().size()).isEqualTo(1), + () -> assertThat(momentImageRepository.findAll().get(0).getMoment().getId()).isEqualTo(momentId) ); } @@ -177,12 +180,19 @@ void updateMomentById() { Moment moment = saveMomentWithImages(memory); // when - MomentRequest momentRequest = MomentRequestFixture.create(memory2.getId()); + MomentRequest momentRequest = new MomentRequest("newStaccatoTitle", "placeName", "newAddress", BigDecimal.ONE, BigDecimal.ONE, LocalDateTime.now(), memory2.getId(), List.of("https://existExample.com.jpg", "https://newExample.com.jpg")); momentService.updateMomentById(moment.getId(), momentRequest, member); // then Moment foundedMoment = momentRepository.findById(moment.getId()).get(); - assertThat(foundedMoment.getMemory().getId()).isEqualTo(memory2.getId()); + List images = momentImageRepository.findAll(); + assertAll( + () -> assertThat(foundedMoment.getTitle()).isEqualTo("newStaccatoTitle"), + () -> assertThat(foundedMoment.getMemory().getId()).isEqualTo(memory2.getId()), + () -> assertThat(images.size()).isEqualTo(2), + () -> assertThat(images.get(0).getImageUrl()).isEqualTo("https://existExample.com.jpg"), + () -> assertThat(images.get(1).getImageUrl()).isEqualTo("https://newExample.com.jpg") + ); } @DisplayName("본인 것이 아닌 스타카토를 수정하려고 하면 예외가 발생한다.")