Skip to content

Commit

Permalink
refactor: 게시글 수정 시 이미지 처리 로직 개선
Browse files Browse the repository at this point in the history
- 이미지 엔티티에 이미지 순서 필드 추가
- 추가, 삭제의 경우에 대한 경우의 수를 나눠서 처리
  • Loading branch information
yeonjae02 committed Aug 1, 2024
1 parent 40edcbd commit 279aa99
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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("게시글을 성공적으로 수정했습니다.");
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -25,7 +27,23 @@ public PostResponseDto(Post post) {
this.likes = false;

if (!post.getImages().isEmpty()) {
this.firstImageUrl = post.getImages().get(0).getImageUrl();
// 이미지 리스트를 order 값에 따라 오름차순으로 정렬
List<Image> 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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class PostUpdateDto {

private List<String> 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("서로 다른 두 개의 해시태그를 선택해야 합니다.");
Expand All @@ -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("이미지는 최소 한 장 업로드해야 합니다.");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -17,8 +16,10 @@ public interface ImageRepository extends JpaRepository<Image, Long> {
@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);
}

Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ public PostDetailDto getPostById(Long postId, Member member) {
MemberProfileResponseDto memberProfileResponseDto = memberService.getMemberProfile(memberEmail);

List<String> images = post.getImages().stream()
.map(Image::getImageUrl)
.toList();
.sorted(Comparator.comparingInt(Image::getOrder)) // order 값에 따라 정렬
.map(Image::getImageUrl) // 이미지 URL 추출
.toList(); // 리스트로 변환

List<Long> hashtags = post.getPostHashtags().stream()
.map(postHashtag -> postHashtag.getHashtag().getId())
Expand Down Expand Up @@ -139,6 +140,7 @@ public List<Image> addImages(Post post, Member member, List<MultipartFile> post
List<Image> images = new ArrayList<>();
if (!postImages.isEmpty()) {
try {
int cnt = 1;
for (MultipartFile postImage : postImages) {
if (postImage.isEmpty()) {
log.error("게시글 이미지 업로드 실패");
Expand All @@ -147,9 +149,10 @@ public List<Image> addImages(Post post, Member member, List<MultipartFile> 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());
Expand Down Expand Up @@ -257,50 +260,6 @@ private Set<Post> wordsToPosts(String[] words) {
return resultSet;
}

// @Transactional
// public void updatePost(Long postId, PostUpdateDto postUpdateDto, Member member, List<MultipartFile> postImages)
// throws AccessDeniedException {
// Post post = postRepository.findById(postId)
// .orElseThrow(() -> new EntityNotFoundException("해당 게시글이 존재하지 않습니다."));
//
// if (!post.getMember().getId().equals(member.getId())) {
// throw new AccessDeniedException("해당 게시글을 수정할 권한이 없습니다. 본인이 작성한 글만 수정할 수 있습니다.");
// }
//
// List<PostHashtag> newPostHashtag = null;
// if (postUpdateDto.getPostHashtag() != null) // 해시태그 변경 시 수정
// newPostHashtag = addHashtags(post, postUpdateDto.getPostHashtag());
//
// List<Image> existImages = new ArrayList<>(post.getImages());
// List<Image> 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<MultipartFile> postImages)
throws AccessDeniedException {
Expand All @@ -316,25 +275,32 @@ public void updatePost(Long postId, PostUpdateDto postUpdateDto, Member member,
newPostHashtag = addHashtags(post, postUpdateDto.getPostHashtag());

List<Image> existImages = new ArrayList<>(post.getImages());
updateImageOrders(existImages);
List<Image> newImages = null;

try {
if (postImages != null && !postImages.isEmpty()) {
newImages = addForUpdateImages(post, member, postImages); // 새 이미지 추가
if (newImages != null && !newImages.isEmpty()) {
existImages.addAll(newImages);
}
}

List<Integer> 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()
Expand All @@ -354,28 +320,67 @@ public void updatePost(Long postId, PostUpdateDto postUpdateDto, Member member,
}
}

private List<Image> addForUpdateImages(Post post, Member member, List<MultipartFile> postImages) {
@Transactional
protected List<Image> addForUpdateImages(Post post, Member member, List<MultipartFile> postImages, List<Integer> deleteId) {
List<Image> 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("게시글 이미지 업로드에 실패하였습니다.");
}
}
return images;
}

// 이미지의 order 값을 연속적으로 설정하는 메서드
@Transactional
protected void updateImageOrders(List<Image> 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<Image> images) {
for (Image image : images) {
Expand All @@ -385,10 +390,17 @@ protected void deleteImages(List<Image> images) {
}

@Transactional
protected void deleteImagesWithUrl(List<String> images, Long postId) {
protected List<Integer> deleteImagesWithUrl(List<String> images, Long postId) {
List<Integer> 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;
}
}
}

0 comments on commit 279aa99

Please sign in to comment.