Skip to content

Commit dfdaa5d

Browse files
authored
[refactor] 이미지 업로드 방식 변경에 대한 post 도메인 수정#180
[refactor] 이미지 업로드 방식 변경에 대한 post 도메인 수정#180
2 parents 162475e + ff2a630 commit dfdaa5d

File tree

20 files changed

+483
-86
lines changed

20 files changed

+483
-86
lines changed

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ dependencies {
5050

5151
// AWS S3
5252
implementation("io.awspring.cloud:spring-cloud-aws-starter-s3:3.4.0")
53+
implementation("com.amazonaws:aws-java-sdk-s3:1.12.777")
5354

5455
runtimeOnly("com.h2database:h2")
5556
runtimeOnly("com.mysql:mysql-connector-j")

src/main/java/com/back/domain/myhistory/dto/MyHistoryLikedPostItemDto.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.back.domain.myhistory.dto;
22

33
import com.back.domain.post.post.entity.Post;
4+
import com.back.domain.post.post.entity.PostImage;
45
import com.back.domain.post.post.entity.PostLike;
56
import java.time.LocalDateTime;
7+
import java.util.List;
68
import lombok.Builder;
79
import lombok.Getter;
810

@@ -11,7 +13,7 @@
1113
public class MyHistoryLikedPostItemDto {
1214
private Long id;
1315
private String title;
14-
private String imageUrl;
16+
private List<String> imageUrls;
1517
private LocalDateTime likedAt;
1618
private Integer likeCount;
1719
private Integer commentCount;
@@ -21,7 +23,9 @@ public static MyHistoryLikedPostItemDto from(PostLike pl) {
2123
return MyHistoryLikedPostItemDto.builder()
2224
.id(p.getId())
2325
.title(p.getTitle())
24-
.imageUrl(p.getImageUrl())
26+
.imageUrls(p.getImages().stream()
27+
.map(PostImage::getUrl)
28+
.toList())
2529
.likedAt(pl.getCreatedAt())
2630
.likeCount(p.getLikeCount())
2731
.commentCount(p.getCommentCount())

src/main/java/com/back/domain/myhistory/dto/MyHistoryPostItemDto.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.back.domain.myhistory.dto;
22

33
import com.back.domain.post.post.entity.Post;
4+
import com.back.domain.post.post.entity.PostImage;
5+
import java.util.List;
46
import lombok.Builder;
57
import lombok.Getter;
68

@@ -11,7 +13,7 @@
1113
public class MyHistoryPostItemDto {
1214
private Long id;
1315
private String title;
14-
private String imageUrl;
16+
private List<String> imageUrls;
1517
private LocalDateTime createdAt;
1618
private Integer likeCount;
1719
private Integer commentCount;
@@ -20,7 +22,9 @@ public static MyHistoryPostItemDto from(Post p) {
2022
return MyHistoryPostItemDto.builder()
2123
.id(p.getId())
2224
.title(p.getTitle())
23-
.imageUrl(p.getImageUrl())
25+
.imageUrls(p.getImages().stream()
26+
.map(PostImage::getUrl)
27+
.toList())
2428
.createdAt(p.getCreatedAt())
2529
.likeCount(p.getLikeCount())
2630
.commentCount(p.getCommentCount())

src/main/java/com/back/domain/post/post/controller/PostController.java

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,19 @@
88
import io.swagger.v3.oas.annotations.Operation;
99
import io.swagger.v3.oas.annotations.tags.Tag;
1010
import jakarta.validation.Valid;
11-
import lombok.RequiredArgsConstructor;
12-
import org.springframework.web.bind.annotation.*;
13-
1411
import java.util.List;
12+
import lombok.RequiredArgsConstructor;
13+
import org.springframework.http.MediaType;
14+
import org.springframework.web.bind.annotation.DeleteMapping;
15+
import org.springframework.web.bind.annotation.GetMapping;
16+
import org.springframework.web.bind.annotation.PatchMapping;
17+
import org.springframework.web.bind.annotation.PathVariable;
18+
import org.springframework.web.bind.annotation.PostMapping;
19+
import org.springframework.web.bind.annotation.RequestMapping;
20+
import org.springframework.web.bind.annotation.RequestParam;
21+
import org.springframework.web.bind.annotation.RequestPart;
22+
import org.springframework.web.bind.annotation.RestController;
23+
import org.springframework.web.multipart.MultipartFile;
1524

1625
@RestController
1726
@RequestMapping("/posts")
@@ -23,15 +32,18 @@ public class PostController {
2332

2433
/**
2534
* 게시글 작성 API
35+
*
2636
* @param reqBody 게시글 작성 요청 DTO
37+
* @param images 첨부 이미지 파일들 (optional)
2738
* @return 작성된 게시글 정보
2839
*/
29-
@PostMapping
40+
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
3041
@Operation(summary = "게시글 작성")
3142
public RsData<PostResponseDto> createPost(
32-
@Valid @RequestBody PostCreateRequestDto reqBody
43+
@RequestPart("post") @Valid PostCreateRequestDto reqBody,
44+
@RequestPart(value = "images", required = false) List<MultipartFile> images
3345
) {
34-
return RsData.successOf(postService.createPost(reqBody)); // code=200, message="success"
46+
return RsData.successOf(postService.createPost(reqBody, images)); // code=200, message="success"
3547
}
3648

3749
/**
@@ -66,13 +78,14 @@ public RsData<PostResponseDto> getPost(
6678
* @param reqBody 게시글 수정 요청 DTO
6779
* @return 수정된 게시글 정보
6880
*/
69-
@PatchMapping("/{postId}")
81+
@PatchMapping(value = "/{postId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
7082
@Operation(summary = "게시글 수정")
7183
public RsData<PostResponseDto> updatePost(
7284
@PathVariable Long postId,
73-
@Valid @RequestBody PostUpdateRequestDto reqBody
85+
@RequestPart("post") @Valid PostUpdateRequestDto reqBody,
86+
@RequestPart(value = "images", required = false) List<MultipartFile> images
7487
) {
75-
return RsData.successOf(postService.updatePost(postId, reqBody)); // code=200, message="success"
88+
return RsData.successOf(postService.updatePost(postId, reqBody, images)); // code=200, message="success"
7689
}
7790

7891
/**

src/main/java/com/back/domain/post/post/dto/request/PostCreateRequestDto.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ public record PostCreateRequestDto(
1111
String title,
1212
@NotBlank (message = "내용은 필수입니다.")
1313
String content,
14-
String imageUrl,
1514
String videoUrl,
1615
List<String> tags
1716
) {

src/main/java/com/back/domain/post/post/dto/request/PostUpdateRequestDto.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ public record PostUpdateRequestDto(
88
PostStatus status,
99
String title,
1010
String content,
11-
String imageUrl,
11+
// 기존 이미지 중 유지할 이미지 ID 목록
12+
List<Long> keepImageIds,
1213
String videoUrl,
1314
List<String> tags
1415
) {

src/main/java/com/back/domain/post/post/dto/response/PostResponseDto.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.back.domain.post.post.dto.response;
22

33
import com.back.domain.post.post.entity.Post;
4+
import com.back.domain.post.post.entity.PostImage;
45
import com.back.domain.post.post.enums.PostStatus;
56
import java.time.LocalDateTime;
67
import java.util.List;
@@ -14,7 +15,7 @@ public record PostResponseDto(
1415
PostStatus status,
1516
String title,
1617
String content,
17-
String imageUrl,
18+
List<String> imageUrls,
1819
String videoUrl,
1920
List<String> tags,
2021
Integer likeCount,
@@ -32,7 +33,9 @@ public PostResponseDto(Post post) {
3233
post.getStatus(),
3334
post.getTitle(),
3435
post.getContent(),
35-
post.getImageUrl(),
36+
post.getImages().stream()
37+
.map(PostImage::getUrl)
38+
.toList(),
3639
post.getVideoUrl(),
3740
post.getPostTags().stream()
3841
.map(postTag -> postTag.getTag().getName())

src/main/java/com/back/domain/post/post/entity/Post.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.back.domain.post.post.entity;
22

33
import com.back.domain.post.category.entity.Category;
4+
import com.back.domain.post.comment.entity.Comment;
45
import com.back.domain.post.post.enums.PostStatus;
56
import com.back.domain.user.entity.User;
67
import jakarta.persistence.*;
@@ -62,9 +63,14 @@ public class Post {
6263
@Column(name = "content", nullable = false, columnDefinition = "TEXT")
6364
private String content;
6465

65-
// 게시글 이미지 URL
66-
@Column(name = "image_url")
67-
private String imageUrl;
66+
// Post → Comment = 1:N
67+
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
68+
private List<Comment> comments = new ArrayList<>();
69+
70+
// Post → PostImage = 1:N
71+
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
72+
@OrderBy("sortOrder ASC") // 조회 시 순서대로 정렬
73+
private List<PostImage> images = new ArrayList<>();
6874

6975
// 게시글 동영상 URL
7076
@Column(name = "video_url")
@@ -105,8 +111,12 @@ public void updateContent(String content) {
105111
this.content = content;
106112
}
107113

108-
public void updateImage(String imageUrl) {
109-
this.imageUrl = imageUrl;
114+
public void updateImages(List<PostImage> images) {
115+
this.images.clear();
116+
for (PostImage i : images) {
117+
i.updatePost(this);
118+
this.images.add(i);
119+
}
110120
}
111121

112122
public void updateVideo(String videoUrl) {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.back.domain.post.post.entity;
2+
3+
import com.back.domain.post.post.enums.PostImageStatus;
4+
import jakarta.persistence.Column;
5+
import jakarta.persistence.Entity;
6+
import jakarta.persistence.EntityListeners;
7+
import jakarta.persistence.EnumType;
8+
import jakarta.persistence.Enumerated;
9+
import jakarta.persistence.FetchType;
10+
import jakarta.persistence.GeneratedValue;
11+
import jakarta.persistence.GenerationType;
12+
import jakarta.persistence.Id;
13+
import jakarta.persistence.JoinColumn;
14+
import jakarta.persistence.ManyToOne;
15+
import lombok.AllArgsConstructor;
16+
import lombok.Builder;
17+
import lombok.Getter;
18+
import lombok.NoArgsConstructor;
19+
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
20+
21+
@Entity
22+
@Getter
23+
@EntityListeners(AuditingEntityListener.class)
24+
@NoArgsConstructor(access = lombok.AccessLevel.PROTECTED)
25+
@AllArgsConstructor
26+
@Builder
27+
public class PostImage {
28+
29+
// 각 이미지를 구분하는 유일한 번호
30+
@Id
31+
@GeneratedValue(strategy = GenerationType.IDENTITY)
32+
@Column(name = "id")
33+
private Long id;
34+
35+
// 해당 이미지가 첨부된 게시글의 고유 식별자
36+
@ManyToOne(fetch = FetchType.LAZY)
37+
@JoinColumn(name = "post_id")
38+
private Post post;
39+
40+
// 이미지 파일 명
41+
@Column(name = "file_name")
42+
private String fileName;
43+
44+
// 이미지 URL
45+
@Column(name = "url")
46+
private String url;
47+
48+
// 이미지 삭제 상태 (기본값: 게시)
49+
@Builder.Default
50+
@Enumerated(EnumType.STRING)
51+
@Column(name = "status", nullable = false)
52+
private PostImageStatus status = PostImageStatus.POSTED;
53+
54+
// 이미지 순서
55+
@Column(name = "sort_order")
56+
private Integer sortOrder;
57+
58+
public void updatePost(Post post) {
59+
this.post = post;
60+
}
61+
62+
public void updateSortOrder(Integer sortOrder) {
63+
this.sortOrder = sortOrder;
64+
}
65+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.back.domain.post.post.enums;
2+
3+
import lombok.Getter;
4+
import lombok.RequiredArgsConstructor;
5+
6+
@Getter
7+
@RequiredArgsConstructor
8+
public enum PostImageStatus {
9+
POSTED("게시", "이미지가 해당 게시물에 게시된 상태"),
10+
DELETED("삭제", "이미지가 삭제 처리된 상태");
11+
12+
private final String title;
13+
private final String description;
14+
}

0 commit comments

Comments
 (0)