From 279aa99de1e37d7184a320d861cfffe3c1c47acf Mon Sep 17 00:00:00 2001 From: yeonjae02 <0yeonjae2@naver.com> Date: Thu, 1 Aug 2024 13:06:29 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=8B=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이미지 엔티티에 이미지 순서 필드 추가 - 추가, 삭제의 경우에 대한 경우의 수를 나눠서 처리 --- .../community_board/api/PostController.java | 6 +- .../community_board/dto/PostResponseDto.java | 20 ++- .../community_board/dto/PostUpdateDto.java | 4 +- .../domain/community_board/entity/Image.java | 3 + .../repository/ImageRepository.java | 5 +- .../community_board/service/PostService.java | 140 ++++++++++-------- 6 files changed, 106 insertions(+), 72 deletions(-) diff --git a/src/main/java/com/solucitation/midpoint_backend/domain/community_board/api/PostController.java b/src/main/java/com/solucitation/midpoint_backend/domain/community_board/api/PostController.java index 59e5585..b91560e 100644 --- a/src/main/java/com/solucitation/midpoint_backend/domain/community_board/api/PostController.java +++ b/src/main/java/com/solucitation/midpoint_backend/domain/community_board/api/PostController.java @@ -312,19 +312,19 @@ public ResponseEntity updatePost(@PathVariable Long postId, } int nowImageCnt = postService.getPostById(postId, member).getImages().size(); - postUpdateDto.validate(nowImageCnt); // 제목, 본문, 해시태그, 삭제할 이미지 검증 + int validImageCnt = 0; if (postImages != null && !postImages.isEmpty()) { // 이미지 변경이 있는 경우 int nextImageCnt = nowImageCnt - postUpdateDto.getDeleteImageUrl().size(); // 삭제 작업만 진행했을 때의 이미지 개수 - int validImageCnt = 0; + for (MultipartFile postImage : postImages) { // 추가할 이미지 개수 if (postImage != null && !postImage.isEmpty()) validImageCnt++; } if (nextImageCnt + validImageCnt > 3) // 최종 이미지 개수 return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("이미지는 최대 3장까지 업로드 가능합니다."); } - + postUpdateDto.validate(nowImageCnt, validImageCnt); // 제목, 본문, 해시태그, 삭제할 이미지 검증 postService.updatePost(postId, postUpdateDto, member, postImages); return ResponseEntity.status(HttpStatus.OK).body("게시글을 성공적으로 수정했습니다."); diff --git a/src/main/java/com/solucitation/midpoint_backend/domain/community_board/dto/PostResponseDto.java b/src/main/java/com/solucitation/midpoint_backend/domain/community_board/dto/PostResponseDto.java index 284ee66..e922669 100644 --- a/src/main/java/com/solucitation/midpoint_backend/domain/community_board/dto/PostResponseDto.java +++ b/src/main/java/com/solucitation/midpoint_backend/domain/community_board/dto/PostResponseDto.java @@ -1,11 +1,13 @@ package com.solucitation.midpoint_backend.domain.community_board.dto; +import com.solucitation.midpoint_backend.domain.community_board.entity.Image; import com.solucitation.midpoint_backend.domain.community_board.entity.Post; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; @@ -25,7 +27,23 @@ public PostResponseDto(Post post) { this.likes = false; if (!post.getImages().isEmpty()) { - this.firstImageUrl = post.getImages().get(0).getImageUrl(); + // 이미지 리스트를 order 값에 따라 오름차순으로 정렬 + List sortedImages = post.getImages().stream() + .sorted(Comparator.comparingInt(image -> { + Integer order = image.getOrder(); + return (order != null) ? order : Integer.MAX_VALUE; // order가 null인 이미지는 마지막으로 이동 + })) + .collect(Collectors.toList()); + + // order 값이 1인 이미지를 찾기 + Image targetImage = sortedImages.stream() + .filter(image -> image.getOrder() != null && image.getOrder() == 1) + .findFirst() + .orElse(null); + + + // 첫 번째 이미지의 URL을 설정 + this.firstImageUrl = sortedImages.get(0).getImageUrl(); } this.hashtags = post.getPostHashtags().stream() diff --git a/src/main/java/com/solucitation/midpoint_backend/domain/community_board/dto/PostUpdateDto.java b/src/main/java/com/solucitation/midpoint_backend/domain/community_board/dto/PostUpdateDto.java index a1f9c26..cf1fa06 100644 --- a/src/main/java/com/solucitation/midpoint_backend/domain/community_board/dto/PostUpdateDto.java +++ b/src/main/java/com/solucitation/midpoint_backend/domain/community_board/dto/PostUpdateDto.java @@ -21,7 +21,7 @@ public class PostUpdateDto { private List deleteImageUrl = new ArrayList<>(); - public void validate(int imageCnt) { + public void validate(int exist, int add) { if (postHashtag != null) { if (postHashtag.size() != 2 || postHashtag.get(0).equals(postHashtag.get(1))) { throw new IllegalArgumentException("서로 다른 두 개의 해시태그를 선택해야 합니다."); @@ -46,7 +46,7 @@ public void validate(int imageCnt) { } if (deleteImageUrl != null) { - if (deleteImageUrl.size() >= imageCnt) { // 이미지를 전부 삭제하는 경우 방지 + if (exist - deleteImageUrl.size() + add <= 0) { // 이미지를 올리지 않는 경우 처리 throw new IllegalArgumentException("이미지는 최소 한 장 업로드해야 합니다."); } diff --git a/src/main/java/com/solucitation/midpoint_backend/domain/community_board/entity/Image.java b/src/main/java/com/solucitation/midpoint_backend/domain/community_board/entity/Image.java index 0d62239..9642977 100644 --- a/src/main/java/com/solucitation/midpoint_backend/domain/community_board/entity/Image.java +++ b/src/main/java/com/solucitation/midpoint_backend/domain/community_board/entity/Image.java @@ -38,6 +38,9 @@ public class Image { @JoinColumn(name = "post_id", nullable = true) private Post post; + @Column(name="post_order") + private Integer order; + @ManyToOne(fetch = LAZY) @JoinColumn(name="member_id", nullable = false) private Member member; diff --git a/src/main/java/com/solucitation/midpoint_backend/domain/community_board/repository/ImageRepository.java b/src/main/java/com/solucitation/midpoint_backend/domain/community_board/repository/ImageRepository.java index a18db2b..ba7de4f 100644 --- a/src/main/java/com/solucitation/midpoint_backend/domain/community_board/repository/ImageRepository.java +++ b/src/main/java/com/solucitation/midpoint_backend/domain/community_board/repository/ImageRepository.java @@ -7,7 +7,6 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -import java.util.List; import java.util.Optional; @Repository @@ -17,8 +16,10 @@ public interface ImageRepository extends JpaRepository { @Query("SELECT COUNT(i) FROM Image i WHERE i.imageUrl = :imageUrl AND i.post.id = :postId") Long countByImageUrlAndPostId(@Param("imageUrl") String imageUrl, @Param("postId") Long postId); + @Query("SELECT i.order FROM Image i WHERE i.imageUrl = :imageUrl AND i.post.id = :postId") + Integer findOrderByImageUrlAndPostId(@Param("imageUrl") String imageUrl, @Param("postId") Long postId); + @Modifying @Query("DELETE FROM Image i WHERE i.imageUrl = :imageUrl AND i.post.id = :postId") void deleteImageByImageUrlAndPostId(@Param("imageUrl") String imageUrl, @Param("postId") Long postId); } - diff --git a/src/main/java/com/solucitation/midpoint_backend/domain/community_board/service/PostService.java b/src/main/java/com/solucitation/midpoint_backend/domain/community_board/service/PostService.java index 04ab83f..247f182 100644 --- a/src/main/java/com/solucitation/midpoint_backend/domain/community_board/service/PostService.java +++ b/src/main/java/com/solucitation/midpoint_backend/domain/community_board/service/PostService.java @@ -42,8 +42,9 @@ public PostDetailDto getPostById(Long postId, Member member) { MemberProfileResponseDto memberProfileResponseDto = memberService.getMemberProfile(memberEmail); List images = post.getImages().stream() - .map(Image::getImageUrl) - .toList(); + .sorted(Comparator.comparingInt(Image::getOrder)) // order 값에 따라 정렬 + .map(Image::getImageUrl) // 이미지 URL 추출 + .toList(); // 리스트로 변환 List hashtags = post.getPostHashtags().stream() .map(postHashtag -> postHashtag.getHashtag().getId()) @@ -139,6 +140,7 @@ public List addImages(Post post, Member member, List post List images = new ArrayList<>(); if (!postImages.isEmpty()) { try { + int cnt = 1; for (MultipartFile postImage : postImages) { if (postImage.isEmpty()) { log.error("게시글 이미지 업로드 실패"); @@ -147,9 +149,10 @@ public List addImages(Post post, Member member, List post String postImageUrl = s3Service.upload("post-images", postImage.getOriginalFilename(), postImage); Image image = Image.builder() - .imageUrl(postImageUrl).member(member).post(post).build(); + .imageUrl(postImageUrl).member(member).post(post).order(cnt).build(); imageRepository.save(image); images.add(image); + cnt++; } } catch (IOException e){ log.error("게시글 이미지 업로드 실패: {}", e.getMessage()); @@ -257,50 +260,6 @@ private Set wordsToPosts(String[] words) { return resultSet; } -// @Transactional -// public void updatePost(Long postId, PostUpdateDto postUpdateDto, Member member, List postImages) -// throws AccessDeniedException { -// Post post = postRepository.findById(postId) -// .orElseThrow(() -> new EntityNotFoundException("해당 게시글이 존재하지 않습니다.")); -// -// if (!post.getMember().getId().equals(member.getId())) { -// throw new AccessDeniedException("해당 게시글을 수정할 권한이 없습니다. 본인이 작성한 글만 수정할 수 있습니다."); -// } -// -// List newPostHashtag = null; -// if (postUpdateDto.getPostHashtag() != null) // 해시태그 변경 시 수정 -// newPostHashtag = addHashtags(post, postUpdateDto.getPostHashtag()); -// -// List existImages = new ArrayList<>(post.getImages()); -// List newImages = null; -// -// try { -// if (postImages != null && !postImages.isEmpty()) // 이미지 변경 시 수정 -// newImages = addImages(post, member, postImages); -// -// // 기존 이미지 삭제 -// if (newImages != null && !newImages.isEmpty()) { -// deleteImages(existImages); -// } -// -// post = Post.builder() -// .id(post.getId()) -// .member(post.getMember()) -// .createDate(post.getCreateDate()) -// .title(postUpdateDto.getTitle() != null ? postUpdateDto.getTitle() : post.getTitle()) -// .content(postUpdateDto.getContent() != null ? postUpdateDto.getContent() : post.getContent()) -// .postHashtags(newPostHashtag != null && !newPostHashtag.isEmpty() ? newPostHashtag : post.getPostHashtags()) -// .images(newImages != null && !newImages.isEmpty() ? newImages : existImages) -// .build(); -// -// postRepository.save(post); -// } catch (Exception e) { -// // 트랜잭션 롤백 -// TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); -// throw e; // 오류를 다시 던집니다. -// } -// } - @Transactional public void updatePost(Long postId, PostUpdateDto postUpdateDto, Member member, List postImages) throws AccessDeniedException { @@ -316,25 +275,32 @@ public void updatePost(Long postId, PostUpdateDto postUpdateDto, Member member, newPostHashtag = addHashtags(post, postUpdateDto.getPostHashtag()); List existImages = new ArrayList<>(post.getImages()); + updateImageOrders(existImages); List newImages = null; try { - if (postImages != null && !postImages.isEmpty()) { - newImages = addForUpdateImages(post, member, postImages); // 새 이미지 추가 - if (newImages != null && !newImages.isEmpty()) { - existImages.addAll(newImages); - } - } - + List deleteIdx = new ArrayList<>(); // 기존 이미지 삭제 if (postUpdateDto.getDeleteImageUrl() != null && !postUpdateDto.getDeleteImageUrl().isEmpty()) { for (String imageUrl : postUpdateDto.getDeleteImageUrl()) { Long cnt = imageRepository.countByImageUrlAndPostId(imageUrl, postId); if (cnt == 0) { - throw new RuntimeException("해당 게시글에 존재하지 않는 이미지는 삭제할 수 없습니다."); + throw new RuntimeException("해당 게시글에 존재하지 않는 이미지는 삭제할 수 없습니다."); } } - deleteImagesWithUrl(postUpdateDto.getDeleteImageUrl(), postId); + deleteIdx = deleteImagesWithUrl(postUpdateDto.getDeleteImageUrl(), postId); + } + + if (postImages != null && !postImages.isEmpty()) { + newImages = addForUpdateImages(post, member, postImages, deleteIdx); + if (newImages != null && !newImages.isEmpty()) { + existImages.addAll(newImages); + } + } + + // Debug: 정렬된 이미지 리스트 출력 + for (Image image : existImages) { + System.out.println(image.getOrder() + " : " + image.getImageUrl()); } post = Post.builder() @@ -354,21 +320,43 @@ public void updatePost(Long postId, PostUpdateDto postUpdateDto, Member member, } } - private List addForUpdateImages(Post post, Member member, List postImages) { + @Transactional + protected List addForUpdateImages(Post post, Member member, List postImages, List deleteId) { List images = new ArrayList<>(); + + int idx = 0; + + // Debug: 초기 idx 값 출력 + System.out.println("Initial idx = " + idx); + if (!postImages.isEmpty()) { try { for (MultipartFile postImage : postImages) { if (postImage.isEmpty()) // 사용자가 이미지 변경을 요청하지 않음 - return null; + continue; + String postImageUrl = s3Service.upload("post-images", postImage.getOriginalFilename(), postImage); + int order; + if (!deleteId.isEmpty() && idx < deleteId.size()) { + order = deleteId.get(idx); + } else { + order = post.getImages().size() + idx + 1; // 기존 이미지 개수 + 현재 추가된 이미지 순서 + } + + // Debug: 현재 이미지의 order 값 출력 + System.out.println("Order for new image = " + order); + Image image = Image.builder() - .imageUrl(postImageUrl).member(member).post(post).build(); + .imageUrl(postImageUrl).member(member).post(post) + .order(order) + .build(); + imageRepository.save(image); images.add(image); + idx++; } - } catch (IOException e){ + } catch (IOException e) { log.error("게시글 이미지 업로드 실패: {}", e.getMessage()); throw new RuntimeException("게시글 이미지 업로드에 실패하였습니다."); } @@ -376,6 +364,23 @@ private List addForUpdateImages(Post post, Member member, List images) { + // 이미지 리스트를 order 값에 따라 오름차순으로 정렬 + images.sort(Comparator.comparingInt(image -> image.getOrder() != null ? image.getOrder() : Integer.MAX_VALUE)); + + int order = 1; + for (Image image : images) { + System.out.println("image.getImageUrl() = " + image.getImageUrl()); + System.out.println("order = " + order); + image.setOrder(order++); + } + + // 이미지의 order 값을 데이터베이스에 반영 + imageRepository.saveAll(images); + } + @Transactional protected void deleteImages(List images) { for (Image image : images) { @@ -385,10 +390,17 @@ protected void deleteImages(List images) { } @Transactional - protected void deleteImagesWithUrl(List images, Long postId) { + protected List deleteImagesWithUrl(List images, Long postId) { + List deleteImageIdx = new ArrayList<>(); for (String imageUrl : images) { - s3Service.delete(imageUrl); - imageRepository.deleteImageByImageUrlAndPostId(imageUrl, postId); + Integer idx = imageRepository.findOrderByImageUrlAndPostId(imageUrl, postId); + System.out.println("remove idx = " + idx); + if (idx != null) { + s3Service.delete(imageUrl); + imageRepository.deleteImageByImageUrlAndPostId(imageUrl, postId); + deleteImageIdx.add(idx); + } } + return deleteImageIdx; } -} \ No newline at end of file +}