From 06964891c055192467f3a5020dadc5d23f984197 Mon Sep 17 00:00:00 2001 From: Youngjoo Date: Mon, 26 Aug 2024 05:56:01 +0900 Subject: [PATCH 01/11] =?UTF-8?q?feat=20:=20=ED=95=B4=EC=89=AC=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=EB=A1=9C=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EB=B6=88?= =?UTF-8?q?=EB=9F=AC=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../content/controller/ContentController.java | 9 ---- .../content/controller/PostController.java | 32 ++++++++++++++ .../domain/{Content.java => Post.java} | 12 +++-- .../wanted/media/content/dto/PostDto.java | 44 +++++++++++++++++++ .../content/repository/ContentRepository.java | 7 --- .../content/repository/PostRepository.java | 14 ++++++ .../media/content/service/ContentService.java | 7 --- .../media/content/service/PostService.java | 29 ++++++++++++ src/main/resources/data.sql | 19 ++++++++ 9 files changed, 146 insertions(+), 27 deletions(-) delete mode 100644 src/main/java/wanted/media/content/controller/ContentController.java create mode 100644 src/main/java/wanted/media/content/controller/PostController.java rename src/main/java/wanted/media/content/domain/{Content.java => Post.java} (82%) create mode 100644 src/main/java/wanted/media/content/dto/PostDto.java delete mode 100644 src/main/java/wanted/media/content/repository/ContentRepository.java create mode 100644 src/main/java/wanted/media/content/repository/PostRepository.java delete mode 100644 src/main/java/wanted/media/content/service/ContentService.java create mode 100644 src/main/java/wanted/media/content/service/PostService.java create mode 100644 src/main/resources/data.sql diff --git a/src/main/java/wanted/media/content/controller/ContentController.java b/src/main/java/wanted/media/content/controller/ContentController.java deleted file mode 100644 index 00583de..0000000 --- a/src/main/java/wanted/media/content/controller/ContentController.java +++ /dev/null @@ -1,9 +0,0 @@ -package wanted.media.content.controller; - -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/contents") -public class ContentController { -} diff --git a/src/main/java/wanted/media/content/controller/PostController.java b/src/main/java/wanted/media/content/controller/PostController.java new file mode 100644 index 0000000..45fd5c6 --- /dev/null +++ b/src/main/java/wanted/media/content/controller/PostController.java @@ -0,0 +1,32 @@ +package wanted.media.content.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import wanted.media.content.domain.Post; +import wanted.media.content.dto.PostDto; +import wanted.media.content.service.PostService; + +import java.util.List; +import java.util.stream.Collectors; + +import static org.hibernate.query.sqm.tree.SqmNode.log; + + +@RestController +@RequestMapping("/posts") +public class PostController { + @Autowired + private PostService postService; + + @GetMapping + public List list(@RequestParam(value = "hashtag", required = true) String account) { + List posts = postService.findPosts(account); + log.info("Content List : " + posts); + return posts.stream() + .map(PostDto::allPosts) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/src/main/java/wanted/media/content/domain/Content.java b/src/main/java/wanted/media/content/domain/Post.java similarity index 82% rename from src/main/java/wanted/media/content/domain/Content.java rename to src/main/java/wanted/media/content/domain/Post.java index fed9efd..9136afe 100644 --- a/src/main/java/wanted/media/content/domain/Content.java +++ b/src/main/java/wanted/media/content/domain/Post.java @@ -4,6 +4,7 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.*; +import org.hibernate.annotations.ColumnDefault; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; @@ -13,18 +14,19 @@ @Entity @Getter -@Table(name = "contents") +@Table(name = "posts") @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @Builder @EntityListeners(AuditingEntityListener.class) -public class Content { +public class Post { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "content_id", nullable = false) + @Column(name = "post_id", nullable = false) private Long id; @Column(name = "like_count") + @ColumnDefault("0") private Long likeCount; @Enumerated(EnumType.STRING) @@ -39,8 +41,10 @@ public class Content { private String hashtags; + @ColumnDefault("0") private Long viewCount; + @ColumnDefault("0") private Long shareCount; @LastModifiedDate @@ -50,7 +54,7 @@ public class Content { private LocalDateTime createdAt; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id") + @JoinColumn(name = "user_id", nullable = false) @NotNull private User user; diff --git a/src/main/java/wanted/media/content/dto/PostDto.java b/src/main/java/wanted/media/content/dto/PostDto.java new file mode 100644 index 0000000..5894162 --- /dev/null +++ b/src/main/java/wanted/media/content/dto/PostDto.java @@ -0,0 +1,44 @@ +package wanted.media.content.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import wanted.media.content.domain.Post; +import wanted.media.content.domain.Type; + +import java.time.LocalDateTime; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class PostDto { + private Long id; + private Long likeCount; + private Type type; + private String title; + private String content; + private String hashtags; + private Long viewCount; + private Long shareCount; + private LocalDateTime updatedAt; + private LocalDateTime createdAt; + private String account; + + public static PostDto allPosts(Post post) { + return new PostDto( + post.getId(), + post.getLikeCount(), + post.getType(), + post.getTitle(), + post.getContent(), + post.getHashtags(), + post.getViewCount(), + post.getShareCount(), + post.getUpdatedAt(), + post.getCreatedAt(), + post.getUser().getAccount() + ); + } +} diff --git a/src/main/java/wanted/media/content/repository/ContentRepository.java b/src/main/java/wanted/media/content/repository/ContentRepository.java deleted file mode 100644 index 510a593..0000000 --- a/src/main/java/wanted/media/content/repository/ContentRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package wanted.media.content.repository; - -import org.springframework.data.jpa.repository.JpaRepository; -import wanted.media.content.domain.Content; - -public interface ContentRepository extends JpaRepository { -} diff --git a/src/main/java/wanted/media/content/repository/PostRepository.java b/src/main/java/wanted/media/content/repository/PostRepository.java new file mode 100644 index 0000000..2a7d0dd --- /dev/null +++ b/src/main/java/wanted/media/content/repository/PostRepository.java @@ -0,0 +1,14 @@ +package wanted.media.content.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import wanted.media.content.domain.Post; + +import java.util.List; + +public interface PostRepository extends JpaRepository { + @Query("SELECT p FROM Post p " + + "WHERE p.user.account LIKE :account") + List findBySearchContaining(@Param("account") String account); +} diff --git a/src/main/java/wanted/media/content/service/ContentService.java b/src/main/java/wanted/media/content/service/ContentService.java deleted file mode 100644 index b505adc..0000000 --- a/src/main/java/wanted/media/content/service/ContentService.java +++ /dev/null @@ -1,7 +0,0 @@ -package wanted.media.content.service; - -import org.springframework.stereotype.Service; - -@Service -public class ContentService { -} diff --git a/src/main/java/wanted/media/content/service/PostService.java b/src/main/java/wanted/media/content/service/PostService.java new file mode 100644 index 0000000..bfa04bb --- /dev/null +++ b/src/main/java/wanted/media/content/service/PostService.java @@ -0,0 +1,29 @@ +package wanted.media.content.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import wanted.media.content.domain.Post; +import wanted.media.content.repository.PostRepository; +import wanted.media.user.repository.UserRepository; + +import java.util.List; + +@Service +public class PostService { + @Autowired + private UserRepository userRepository; + + @Autowired + private PostRepository postRepository; + + @Transactional(readOnly = true) + public List findPosts(String account) { + if (account == null || account.isEmpty()) { + throw new IllegalStateException("해당하는 태그를 찾을 수 없습니다."); + } + + return postRepository.findBySearchContaining(account); + } + +} diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 0000000..4c6ede2 --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,19 @@ +INSERT INTO users(account, email, password, grade) +VALUES ('wanted', 'wanted@gmail.com', 'wanted', 'orange'); + +INSERT INTO contents(like_count, type, title, content, hashtags, view_count, share_count, updated_at, created_at, + user_id) +VALUES (5, 'FACEBOOK', '판교 나들이', '오늘은 판교에 와보았어요. 미래의 직장이 될 곳들이 많이...', 'wanted', 54, 30, '2024-08-23', '2024-08-20', + 'wanted'); +INSERT INTO contents(like_count, type, title, content, hashtags, view_count, share_count, updated_at, created_at, + user_id) +VALUES (15, 'TWITTER', '판교 나들이', '오늘은 판교에 와보았어요. 미래의 직장이 될 곳들이 많이...', 'wanted', 154, 10, '2024-08-23', '2024-08-20', + 'wanted'); +INSERT INTO contents(like_count, type, title, content, hashtags, view_count, share_count, updated_at, created_at, + user_id) +VALUES (1, 'THREADS', '판교 나들이', '오늘은 판교에 와보았어요. 미래의 직장이 될 곳들이 많이...', 'wanted', 43, 2, '2024-08-23', '2024-08-20', + 'wanted'); +INSERT INTO contents(like_count, type, title, content, hashtags, view_count, share_count, updated_at, created_at, + user_id) +VALUES (30, 'INSTAGRAM', '판교 나들이', '오늘은 판교에 와보았어요. 미래의 직장이 될 곳들이 많이...', 'wanted', 24, 7, '2024-08-23', '2024-08-20', + 'wanted'); \ No newline at end of file From bd87f8791d7d40c78c2138969db5a7dc2c644ab2 Mon Sep 17 00:00:00 2001 From: Youngjoo Date: Mon, 26 Aug 2024 06:02:03 +0900 Subject: [PATCH 02/11] =?UTF-8?q?feat=20:=20=ED=95=B4=EC=89=AC=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=20=EB=B0=8F=20type=20=EC=9E=85=EB=A0=A5=EB=B0=9B?= =?UTF-8?q?=EC=95=84=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EB=B6=88=EB=9F=AC?= =?UTF-8?q?=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../media/content/controller/PostController.java | 6 ++++-- .../media/content/repository/PostRepository.java | 5 ++++- .../wanted/media/content/service/PostService.java | 11 +++++++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/java/wanted/media/content/controller/PostController.java b/src/main/java/wanted/media/content/controller/PostController.java index 45fd5c6..f5fe61b 100644 --- a/src/main/java/wanted/media/content/controller/PostController.java +++ b/src/main/java/wanted/media/content/controller/PostController.java @@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import wanted.media.content.domain.Post; +import wanted.media.content.domain.Type; import wanted.media.content.dto.PostDto; import wanted.media.content.service.PostService; @@ -22,8 +23,9 @@ public class PostController { private PostService postService; @GetMapping - public List list(@RequestParam(value = "hashtag", required = true) String account) { - List posts = postService.findPosts(account); + public List list(@RequestParam(value = "hashtag", required = true) String account, + @RequestParam(value = "type", required = false) Type type) { + List posts = postService.findPosts(account, type); log.info("Content List : " + posts); return posts.stream() .map(PostDto::allPosts) diff --git a/src/main/java/wanted/media/content/repository/PostRepository.java b/src/main/java/wanted/media/content/repository/PostRepository.java index 2a7d0dd..dc315cd 100644 --- a/src/main/java/wanted/media/content/repository/PostRepository.java +++ b/src/main/java/wanted/media/content/repository/PostRepository.java @@ -4,11 +4,14 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import wanted.media.content.domain.Post; +import wanted.media.content.domain.Type; import java.util.List; public interface PostRepository extends JpaRepository { @Query("SELECT p FROM Post p " + "WHERE p.user.account LIKE :account") - List findBySearchContaining(@Param("account") String account); + List findBySearchContaining(@Param("account") String account, @Param("type") Type type); + + List findByType(@Param("type") Type type); } diff --git a/src/main/java/wanted/media/content/service/PostService.java b/src/main/java/wanted/media/content/service/PostService.java index bfa04bb..e782ad0 100644 --- a/src/main/java/wanted/media/content/service/PostService.java +++ b/src/main/java/wanted/media/content/service/PostService.java @@ -4,6 +4,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import wanted.media.content.domain.Post; +import wanted.media.content.domain.Type; import wanted.media.content.repository.PostRepository; import wanted.media.user.repository.UserRepository; @@ -18,9 +19,15 @@ public class PostService { private PostRepository postRepository; @Transactional(readOnly = true) - public List findPosts(String account) { + public List findPosts(String account, Type type) { if (account == null || account.isEmpty()) { - throw new IllegalStateException("해당하는 태그를 찾을 수 없습니다."); + return type == null ? + postRepository.findAll() : postRepository.findByType(type); + //throw new IllegalStateException("해당하는 태그를 찾을 수 없습니다."); + } else { + return type == null ? + postRepository.findBySearchContaining(account) : + postRepository.findBySearchContaining(account, type); } return postRepository.findBySearchContaining(account); From e4d6cc19ed08df6361e4ae500c88e8944ec6a319 Mon Sep 17 00:00:00 2001 From: Youngjoo Date: Mon, 26 Aug 2024 06:09:24 +0900 Subject: [PATCH 03/11] =?UTF-8?q?refactor=20:=20=ED=95=B4=EC=89=AC?= =?UTF-8?q?=ED=83=9C=EA=B7=B8=20=EB=B0=8F=20type=20=EC=9E=85=EB=A0=A5?= =?UTF-8?q?=EB=B0=9B=EC=95=84=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EB=B6=88?= =?UTF-8?q?=EB=9F=AC=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wanted/media/content/repository/PostRepository.java | 3 ++- .../java/wanted/media/content/service/PostService.java | 7 +------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/wanted/media/content/repository/PostRepository.java b/src/main/java/wanted/media/content/repository/PostRepository.java index dc315cd..89931b3 100644 --- a/src/main/java/wanted/media/content/repository/PostRepository.java +++ b/src/main/java/wanted/media/content/repository/PostRepository.java @@ -10,7 +10,8 @@ public interface PostRepository extends JpaRepository { @Query("SELECT p FROM Post p " + - "WHERE p.user.account LIKE :account") + "WHERE p.user.account LIKE :account " + + "AND p.type LIKE :type") List findBySearchContaining(@Param("account") String account, @Param("type") Type type); List findByType(@Param("type") Type type); diff --git a/src/main/java/wanted/media/content/service/PostService.java b/src/main/java/wanted/media/content/service/PostService.java index e782ad0..9202529 100644 --- a/src/main/java/wanted/media/content/service/PostService.java +++ b/src/main/java/wanted/media/content/service/PostService.java @@ -24,13 +24,8 @@ public List findPosts(String account, Type type) { return type == null ? postRepository.findAll() : postRepository.findByType(type); //throw new IllegalStateException("해당하는 태그를 찾을 수 없습니다."); - } else { - return type == null ? - postRepository.findBySearchContaining(account) : - postRepository.findBySearchContaining(account, type); } - - return postRepository.findBySearchContaining(account); + return postRepository.findBySearchContaining(account, type); } } From 963459e4fa0e2d58e1a5a2f62a6b7b64470201f3 Mon Sep 17 00:00:00 2001 From: Youngjoo Date: Mon, 26 Aug 2024 06:17:11 +0900 Subject: [PATCH 04/11] =?UTF-8?q?feat=20:=EC=98=A4=EB=A6=84=EC=B0=A8?= =?UTF-8?q?=EC=88=9C,=20=EB=82=B4=EB=A6=BC=EC=B0=A8=EC=88=9C=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=95=EB=A0=AC=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wanted/media/content/controller/PostController.java | 6 ++++-- .../wanted/media/content/repository/PostRepository.java | 5 +++-- src/main/java/wanted/media/content/service/PostService.java | 4 +++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/wanted/media/content/controller/PostController.java b/src/main/java/wanted/media/content/controller/PostController.java index f5fe61b..08cb6fc 100644 --- a/src/main/java/wanted/media/content/controller/PostController.java +++ b/src/main/java/wanted/media/content/controller/PostController.java @@ -24,8 +24,10 @@ public class PostController { @GetMapping public List list(@RequestParam(value = "hashtag", required = true) String account, - @RequestParam(value = "type", required = false) Type type) { - List posts = postService.findPosts(account, type); + @RequestParam(value = "type", required = false) Type type, + @RequestParam(value = "orderBy", defaultValue = "createdAt") String orderBy, + @RequestParam(value = "sortDirection", defaultValue = "ASC") String sortDirection) { + List posts = postService.findPosts(account, type, orderBy, sortDirection); log.info("Content List : " + posts); return posts.stream() .map(PostDto::allPosts) diff --git a/src/main/java/wanted/media/content/repository/PostRepository.java b/src/main/java/wanted/media/content/repository/PostRepository.java index 89931b3..3ee463a 100644 --- a/src/main/java/wanted/media/content/repository/PostRepository.java +++ b/src/main/java/wanted/media/content/repository/PostRepository.java @@ -1,5 +1,6 @@ package wanted.media.content.repository; +import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -10,9 +11,9 @@ public interface PostRepository extends JpaRepository { @Query("SELECT p FROM Post p " + - "WHERE p.user.account LIKE :account " + + "WHERE p.user.account LIKE %:account% " + "AND p.type LIKE :type") - List findBySearchContaining(@Param("account") String account, @Param("type") Type type); + List findBySearchContaining(@Param("account") String account, @Param("type") Type type, Sort sort); List findByType(@Param("type") Type type); } diff --git a/src/main/java/wanted/media/content/service/PostService.java b/src/main/java/wanted/media/content/service/PostService.java index 9202529..c69e7dd 100644 --- a/src/main/java/wanted/media/content/service/PostService.java +++ b/src/main/java/wanted/media/content/service/PostService.java @@ -1,6 +1,7 @@ package wanted.media.content.service; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import wanted.media.content.domain.Post; @@ -19,7 +20,8 @@ public class PostService { private PostRepository postRepository; @Transactional(readOnly = true) - public List findPosts(String account, Type type) { + public List findPosts(String account, Type type, String orderBy, String sortDirection) { + Sort sort = Sort.by(Sort.Direction.fromString(sortDirection), orderBy) if (account == null || account.isEmpty()) { return type == null ? postRepository.findAll() : postRepository.findByType(type); From 2c777e365c80fe68395f680bda3bb8466b103675 Mon Sep 17 00:00:00 2001 From: Youngjoo Date: Mon, 26 Aug 2024 06:28:06 +0900 Subject: [PATCH 05/11] =?UTF-8?q?feat=20:=EA=B2=8C=EC=8B=9C=EB=AC=BC=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20search=5Fby=20=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../content/controller/PostController.java | 6 ++++-- .../content/repository/PostRepository.java | 12 ++++++++---- .../media/content/service/PostService.java | 17 +++++++++-------- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/main/java/wanted/media/content/controller/PostController.java b/src/main/java/wanted/media/content/controller/PostController.java index 08cb6fc..cfcc2bf 100644 --- a/src/main/java/wanted/media/content/controller/PostController.java +++ b/src/main/java/wanted/media/content/controller/PostController.java @@ -26,8 +26,10 @@ public class PostController { public List list(@RequestParam(value = "hashtag", required = true) String account, @RequestParam(value = "type", required = false) Type type, @RequestParam(value = "orderBy", defaultValue = "createdAt") String orderBy, - @RequestParam(value = "sortDirection", defaultValue = "ASC") String sortDirection) { - List posts = postService.findPosts(account, type, orderBy, sortDirection); + @RequestParam(value = "sortDirection", defaultValue = "ASC") String sortDirection, + @RequestParam(value = "search_by", defaultValue = "title, content") String searchBy, + @RequestParam(value = "search", required = false) String search) { + List posts = postService.findPosts(account, type, orderBy, sortDirection, searchBy, search); log.info("Content List : " + posts); return posts.stream() .map(PostDto::allPosts) diff --git a/src/main/java/wanted/media/content/repository/PostRepository.java b/src/main/java/wanted/media/content/repository/PostRepository.java index 3ee463a..f2878dc 100644 --- a/src/main/java/wanted/media/content/repository/PostRepository.java +++ b/src/main/java/wanted/media/content/repository/PostRepository.java @@ -12,8 +12,12 @@ public interface PostRepository extends JpaRepository { @Query("SELECT p FROM Post p " + "WHERE p.user.account LIKE %:account% " + - "AND p.type LIKE :type") - List findBySearchContaining(@Param("account") String account, @Param("type") Type type, Sort sort); - - List findByType(@Param("type") Type type); + "AND (:type IS NULL OR p.type = :type) " + + "AND ((:searchBy = 'title' AND LOWER(p.title) LIKE LOWER(CONCAT('%', :search, '%'))) " + + "OR (:searchBy = 'content' AND LOWER(p.content) LIKE LOWER(CONCAT('%', :search, '%'))) " + + "OR (:searchBy = 'title,content' AND (LOWER(p.title) LIKE LOWER(CONCAT('%', :search, '%')) " + + "OR LOWER(p.content) LIKE LOWER(CONCAT('%', :search, '%')))))") + List findBySearchContaining(@Param("account") String account, @Param("type") Type type, + @Param("searchBy") String searchBy, @Param("search") String search, + Sort sort); } diff --git a/src/main/java/wanted/media/content/service/PostService.java b/src/main/java/wanted/media/content/service/PostService.java index c69e7dd..f30941c 100644 --- a/src/main/java/wanted/media/content/service/PostService.java +++ b/src/main/java/wanted/media/content/service/PostService.java @@ -20,14 +20,15 @@ public class PostService { private PostRepository postRepository; @Transactional(readOnly = true) - public List findPosts(String account, Type type, String orderBy, String sortDirection) { - Sort sort = Sort.by(Sort.Direction.fromString(sortDirection), orderBy) - if (account == null || account.isEmpty()) { - return type == null ? - postRepository.findAll() : postRepository.findByType(type); - //throw new IllegalStateException("해당하는 태그를 찾을 수 없습니다."); - } - return postRepository.findBySearchContaining(account, type); + public List findPosts(String account, Type type, String orderBy, String sortDirection, String searchBy, String search) { + Sort sort = Sort.by(Sort.Direction.fromString(sortDirection), orderBy); + +// if (search == null || search.isEmpty()) { +// return postRepository.findAll(); +// //throw new IllegalStateException("해당하는 태그를 찾을 수 없습니다."); +// } + + return postRepository.findBySearchContaining(account, type, searchBy, search, sort); } } From 62222d9e705127c987b5aed5c952f0cec8445cd9 Mon Sep 17 00:00:00 2001 From: Youngjoo Date: Mon, 26 Aug 2024 06:38:03 +0900 Subject: [PATCH 06/11] =?UTF-8?q?feat=20:=EA=B2=8C=EC=8B=9C=EB=AC=BC=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../media/content/controller/PostController.java | 14 ++++++++------ .../media/content/repository/PostRepository.java | 9 ++++----- .../wanted/media/content/service/PostService.java | 10 ++++++---- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/main/java/wanted/media/content/controller/PostController.java b/src/main/java/wanted/media/content/controller/PostController.java index cfcc2bf..bee18f7 100644 --- a/src/main/java/wanted/media/content/controller/PostController.java +++ b/src/main/java/wanted/media/content/controller/PostController.java @@ -1,6 +1,7 @@ package wanted.media.content.controller; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -13,8 +14,6 @@ import java.util.List; import java.util.stream.Collectors; -import static org.hibernate.query.sqm.tree.SqmNode.log; - @RestController @RequestMapping("/posts") @@ -28,11 +27,14 @@ public List list(@RequestParam(value = "hashtag", required = true) Stri @RequestParam(value = "orderBy", defaultValue = "createdAt") String orderBy, @RequestParam(value = "sortDirection", defaultValue = "ASC") String sortDirection, @RequestParam(value = "search_by", defaultValue = "title, content") String searchBy, - @RequestParam(value = "search", required = false) String search) { - List posts = postService.findPosts(account, type, orderBy, sortDirection, searchBy, search); - log.info("Content List : " + posts); - return posts.stream() + @RequestParam(value = "search", required = false) String search, + @RequestParam(value = "page", defaultValue = "0") int page, + @RequestParam(value = "page_count", defaultValue = "10") int pageCount) { + Page postPage = postService.findPosts(account, type, orderBy, sortDirection, searchBy, search, page, pageCount); + List postDtos = postPage.getContent().stream() .map(PostDto::allPosts) .collect(Collectors.toList()); + + return postDtos; } } \ No newline at end of file diff --git a/src/main/java/wanted/media/content/repository/PostRepository.java b/src/main/java/wanted/media/content/repository/PostRepository.java index f2878dc..2e16fc9 100644 --- a/src/main/java/wanted/media/content/repository/PostRepository.java +++ b/src/main/java/wanted/media/content/repository/PostRepository.java @@ -1,14 +1,13 @@ package wanted.media.content.repository; -import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import wanted.media.content.domain.Post; import wanted.media.content.domain.Type; -import java.util.List; - public interface PostRepository extends JpaRepository { @Query("SELECT p FROM Post p " + "WHERE p.user.account LIKE %:account% " + @@ -17,7 +16,7 @@ public interface PostRepository extends JpaRepository { "OR (:searchBy = 'content' AND LOWER(p.content) LIKE LOWER(CONCAT('%', :search, '%'))) " + "OR (:searchBy = 'title,content' AND (LOWER(p.title) LIKE LOWER(CONCAT('%', :search, '%')) " + "OR LOWER(p.content) LIKE LOWER(CONCAT('%', :search, '%')))))") - List findBySearchContaining(@Param("account") String account, @Param("type") Type type, + Page findBySearchContaining(@Param("account") String account, @Param("type") Type type, @Param("searchBy") String searchBy, @Param("search") String search, - Sort sort); + Pageable pageable); } diff --git a/src/main/java/wanted/media/content/service/PostService.java b/src/main/java/wanted/media/content/service/PostService.java index f30941c..03affb6 100644 --- a/src/main/java/wanted/media/content/service/PostService.java +++ b/src/main/java/wanted/media/content/service/PostService.java @@ -1,6 +1,9 @@ package wanted.media.content.service; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -9,8 +12,6 @@ import wanted.media.content.repository.PostRepository; import wanted.media.user.repository.UserRepository; -import java.util.List; - @Service public class PostService { @Autowired @@ -20,15 +21,16 @@ public class PostService { private PostRepository postRepository; @Transactional(readOnly = true) - public List findPosts(String account, Type type, String orderBy, String sortDirection, String searchBy, String search) { + public Page findPosts(String account, Type type, String orderBy, String sortDirection, String searchBy, String search, int page, int pageCount) { Sort sort = Sort.by(Sort.Direction.fromString(sortDirection), orderBy); + Pageable pageable = PageRequest.of(page, pageCount, sort); // if (search == null || search.isEmpty()) { // return postRepository.findAll(); // //throw new IllegalStateException("해당하는 태그를 찾을 수 없습니다."); // } - return postRepository.findBySearchContaining(account, type, searchBy, search, sort); + return postRepository.findBySearchContaining(account, type, searchBy, search, pageable); } } From d2f3c471975ea6d5a15e7b7559c2a1d04db02ee7 Mon Sep 17 00:00:00 2001 From: Youngjoo Date: Mon, 26 Aug 2024 16:44:11 +0900 Subject: [PATCH 07/11] =?UTF-8?q?feat=20:=EA=B2=8C=EC=8B=9C=EB=AC=BC=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../media/post/controller/PostController.java | 35 ++++++++++++++++++- .../media/{content => post}/dto/PostDto.java | 8 ++--- .../repository/PostRepository.java | 8 ++--- .../media/post/service/PostService.java | 28 +++++++++++++++ 4 files changed, 70 insertions(+), 9 deletions(-) rename src/main/java/wanted/media/{content => post}/dto/PostDto.java (88%) rename src/main/java/wanted/media/{content => post}/repository/PostRepository.java (85%) diff --git a/src/main/java/wanted/media/post/controller/PostController.java b/src/main/java/wanted/media/post/controller/PostController.java index 97212e6..832f921 100644 --- a/src/main/java/wanted/media/post/controller/PostController.java +++ b/src/main/java/wanted/media/post/controller/PostController.java @@ -1,9 +1,42 @@ package wanted.media.post.controller; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import wanted.media.post.domain.Post; +import wanted.media.post.domain.Type; +import wanted.media.post.dto.PostDto; +import wanted.media.post.service.PostService; + +import java.util.List; +import java.util.stream.Collectors; @RestController -@RequestMapping("/posts") +@RequestMapping("/api/posts") public class PostController { + @Autowired + private PostService postService; + + @GetMapping + public List list(@RequestParam(value = "hashtag", required = true) String account, + @RequestParam(value = "type", required = false) Type type, + @RequestParam(value = "orderBy", defaultValue = "createdAt") String orderBy, + @RequestParam(value = "sortDirection", defaultValue = "ASC") String sortDirection, + @RequestParam(value = "search_by", defaultValue = "title, content") String searchBy, + @RequestParam(value = "search", required = false) String search, + @RequestParam(value = "page", defaultValue = "0") int page, + @RequestParam(value = "page_count", defaultValue = "10") int pageCount) { + Page postPage = postService.findPosts(account, type, orderBy, sortDirection, searchBy, search, page, pageCount); + List postDtos = postPage.getContent().stream() + .map(PostDto::allPosts) + .collect(Collectors.toList()); + + //System.out.println("PostDtos: " + postDtos); + + return postDtos; + } + } diff --git a/src/main/java/wanted/media/content/dto/PostDto.java b/src/main/java/wanted/media/post/dto/PostDto.java similarity index 88% rename from src/main/java/wanted/media/content/dto/PostDto.java rename to src/main/java/wanted/media/post/dto/PostDto.java index 5894162..cbe960d 100644 --- a/src/main/java/wanted/media/content/dto/PostDto.java +++ b/src/main/java/wanted/media/post/dto/PostDto.java @@ -1,11 +1,11 @@ -package wanted.media.content.dto; +package wanted.media.post.dto; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import wanted.media.content.domain.Post; -import wanted.media.content.domain.Type; +import wanted.media.post.domain.Post; +import wanted.media.post.domain.Type; import java.time.LocalDateTime; @@ -14,7 +14,7 @@ @Getter @Setter public class PostDto { - private Long id; + private String id; private Long likeCount; private Type type; private String title; diff --git a/src/main/java/wanted/media/content/repository/PostRepository.java b/src/main/java/wanted/media/post/repository/PostRepository.java similarity index 85% rename from src/main/java/wanted/media/content/repository/PostRepository.java rename to src/main/java/wanted/media/post/repository/PostRepository.java index 2e16fc9..4eca176 100644 --- a/src/main/java/wanted/media/content/repository/PostRepository.java +++ b/src/main/java/wanted/media/post/repository/PostRepository.java @@ -1,16 +1,16 @@ -package wanted.media.content.repository; +package wanted.media.post.repository; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import wanted.media.content.domain.Post; -import wanted.media.content.domain.Type; +import wanted.media.post.domain.Post; +import wanted.media.post.domain.Type; public interface PostRepository extends JpaRepository { @Query("SELECT p FROM Post p " + - "WHERE p.user.account LIKE %:account% " + + "WHERE p.user.account = :account " + "AND (:type IS NULL OR p.type = :type) " + "AND ((:searchBy = 'title' AND LOWER(p.title) LIKE LOWER(CONCAT('%', :search, '%'))) " + "OR (:searchBy = 'content' AND LOWER(p.content) LIKE LOWER(CONCAT('%', :search, '%'))) " + diff --git a/src/main/java/wanted/media/post/service/PostService.java b/src/main/java/wanted/media/post/service/PostService.java index cb52ee0..702c44e 100644 --- a/src/main/java/wanted/media/post/service/PostService.java +++ b/src/main/java/wanted/media/post/service/PostService.java @@ -1,7 +1,35 @@ package wanted.media.post.service; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import wanted.media.post.domain.Post; +import wanted.media.post.domain.Type; +import wanted.media.post.repository.PostRepository; +import wanted.media.user.repository.UserRepository; @Service public class PostService { + @Autowired + private UserRepository userRepository; + + @Autowired + private PostRepository postRepository; + + @Transactional(readOnly = true) + public Page findPosts(String account, Type type, String orderBy, String sortDirection, String searchBy, String search, int page, int pageCount) { + Sort sort = Sort.by(Sort.Direction.fromString(sortDirection), orderBy); + Pageable pageable = PageRequest.of(page, pageCount, sort); + +// if (search == null || search.isEmpty()) { +// return postRepository.findAll(); +// //throw new IllegalStateException("해당하는 태그를 찾을 수 없습니다."); +// } + + return postRepository.findBySearchContaining(account, type, searchBy, search, pageable); + } } From 1888f864c3f3faf73b3a3f7c681b353b818e69e4 Mon Sep 17 00:00:00 2001 From: Youngjoo Date: Mon, 26 Aug 2024 17:02:26 +0900 Subject: [PATCH 08/11] =?UTF-8?q?feat=20:=EA=B2=8C=EC=8B=9C=EB=AC=BC=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EA=B8=B0=EB=8A=A5=20=EC=97=90=EB=9F=AC?= =?UTF-8?q?=ED=95=B8=EB=93=A4=EB=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../media/exception/CustomException.java | 13 ++++++++++ .../wanted/media/exception/ErrorCode.java | 13 ++++++---- .../wanted/media/exception/ErrorResponse.java | 10 +++++-- .../handler/GlobalExceptionHandler.java | 26 ++++++++++++++----- .../media/post/service/PostService.java | 13 ++++++---- 5 files changed, 57 insertions(+), 18 deletions(-) create mode 100644 src/main/java/wanted/media/exception/CustomException.java diff --git a/src/main/java/wanted/media/exception/CustomException.java b/src/main/java/wanted/media/exception/CustomException.java new file mode 100644 index 0000000..a8f4885 --- /dev/null +++ b/src/main/java/wanted/media/exception/CustomException.java @@ -0,0 +1,13 @@ +package wanted.media.exception; + +import lombok.Getter; + +@Getter +public class CustomException extends RuntimeException { + private final ErrorCode errorCode; + + public CustomException(ErrorCode errorCode) { + super(errorCode.getMessage()); + this.errorCode = errorCode; + } +} diff --git a/src/main/java/wanted/media/exception/ErrorCode.java b/src/main/java/wanted/media/exception/ErrorCode.java index dade125..2229c62 100644 --- a/src/main/java/wanted/media/exception/ErrorCode.java +++ b/src/main/java/wanted/media/exception/ErrorCode.java @@ -1,15 +1,18 @@ package wanted.media.exception; -import org.springframework.http.HttpStatus; - import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; @Getter @RequiredArgsConstructor public enum ErrorCode { - ENTITY_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 엔티티입니다."); + ENTITY_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 엔티티입니다."), + INVALID_INPUT_VALUE(HttpStatus.BAD_REQUEST, "클라이언트의 입력 값을 확인해주세요."), + INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류가 발생했습니다."), + UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "인증이 필요합니다."), + FORBIDDEN(HttpStatus.FORBIDDEN, "권한이 없습니다."); - private final HttpStatus status; - private final String message; + private final HttpStatus status; + private final String message; } diff --git a/src/main/java/wanted/media/exception/ErrorResponse.java b/src/main/java/wanted/media/exception/ErrorResponse.java index 5ce1086..b673189 100644 --- a/src/main/java/wanted/media/exception/ErrorResponse.java +++ b/src/main/java/wanted/media/exception/ErrorResponse.java @@ -2,10 +2,16 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; + @Getter @RequiredArgsConstructor public class ErrorResponse { - private final int statusCode; - private final String message; + private final int statusCode; + private final String message; + + public ErrorResponse(ErrorCode errorCode) { + this.statusCode = errorCode.getStatus().value(); + this.message = errorCode.getMessage(); + } } diff --git a/src/main/java/wanted/media/exception/handler/GlobalExceptionHandler.java b/src/main/java/wanted/media/exception/handler/GlobalExceptionHandler.java index c96a472..50333f0 100644 --- a/src/main/java/wanted/media/exception/handler/GlobalExceptionHandler.java +++ b/src/main/java/wanted/media/exception/handler/GlobalExceptionHandler.java @@ -4,15 +4,29 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; - +import wanted.media.exception.CustomException; +import wanted.media.exception.ErrorCode; import wanted.media.exception.ErrorResponse; @RestControllerAdvice public class GlobalExceptionHandler { - @ExceptionHandler(BadRequestException.class) - public ResponseEntity handleBadRequestException(BadRequestException e) { - return ResponseEntity.badRequest() - .body(new ErrorResponse(400, e.getMessage())); - } + @ExceptionHandler(BadRequestException.class) + public ResponseEntity handleBadRequestException(BadRequestException e) { + return ResponseEntity.badRequest() + .body(new ErrorResponse(400, e.getMessage())); + } + + @ExceptionHandler(CustomException.class) + public ResponseEntity handleCustomException(CustomException ex) { + ErrorCode errorCode = ex.getErrorCode(); + ErrorResponse response = new ErrorResponse(errorCode); + return new ResponseEntity<>(response, errorCode.getStatus()); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleGenericException(Exception ex) { + ErrorResponse response = new ErrorResponse(ErrorCode.INTERNAL_SERVER_ERROR); + return new ResponseEntity<>(response, ErrorCode.INTERNAL_SERVER_ERROR.getStatus()); + } } diff --git a/src/main/java/wanted/media/post/service/PostService.java b/src/main/java/wanted/media/post/service/PostService.java index 702c44e..62baa7d 100644 --- a/src/main/java/wanted/media/post/service/PostService.java +++ b/src/main/java/wanted/media/post/service/PostService.java @@ -7,6 +7,8 @@ import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import wanted.media.exception.CustomException; +import wanted.media.exception.ErrorCode; import wanted.media.post.domain.Post; import wanted.media.post.domain.Type; import wanted.media.post.repository.PostRepository; @@ -25,11 +27,12 @@ public Page findPosts(String account, Type type, String orderBy, String so Sort sort = Sort.by(Sort.Direction.fromString(sortDirection), orderBy); Pageable pageable = PageRequest.of(page, pageCount, sort); -// if (search == null || search.isEmpty()) { -// return postRepository.findAll(); -// //throw new IllegalStateException("해당하는 태그를 찾을 수 없습니다."); -// } + // 예시: 검색 조건에 맞는 게시물이 없는 경우 예외 처리 + Page posts = postRepository.findBySearchContaining(account, type, searchBy, search, pageable); + if (posts.isEmpty()) { + throw new CustomException(ErrorCode.ENTITY_NOT_FOUND); + } - return postRepository.findBySearchContaining(account, type, searchBy, search, pageable); + return posts; } } From a6711894dc1d64d2818b73c6d0f3bd792f06af41 Mon Sep 17 00:00:00 2001 From: Youngjoo Date: Mon, 26 Aug 2024 17:13:57 +0900 Subject: [PATCH 09/11] =?UTF-8?q?feat=20:=EA=B2=8C=EC=8B=9C=EB=AC=BC=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EA=B8=B0=EB=8A=A5=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/controller/PostControllerTest.java | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 src/test/java/wanted/media/post/controller/PostControllerTest.java diff --git a/src/test/java/wanted/media/post/controller/PostControllerTest.java b/src/test/java/wanted/media/post/controller/PostControllerTest.java new file mode 100644 index 0000000..4b9d23d --- /dev/null +++ b/src/test/java/wanted/media/post/controller/PostControllerTest.java @@ -0,0 +1,67 @@ +package wanted.media.post.controller; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.TestPropertySource; +import wanted.media.post.dto.PostDto; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +@TestPropertySource(locations = "classpath:application-test.properties") +class PostControllerTest { + @Autowired + private TestRestTemplate restTemplate; + + @Test + public void posts_list_성공() { + // When + String url = "/api/posts?hashtag=wanted&type=FACEBOOK&orderBy=createdAt&sortDirection=ASC&search_by=title&search=판교&page=0&page_count=10"; + ResponseEntity responseEntity = restTemplate.getForEntity(url, List.class); + + // Then + assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK); + List posts = responseEntity.getBody(); + assertThat(posts).isNotEmpty(); + } + + @Test + public void search_by_성공() { + String url = "/api/posts?hashtag=wanted&type=FACEBOOK&orderBy=createdAt&sortDirection=ASC&search_by=invalidSearchBy&page=0&page_count=10"; + ResponseEntity responseEntity = restTemplate.getForEntity(url, List.class); + + assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + } + + @Test + public void 유효하지_않은_page값() { + String url = "/api/posts?hashtag=wanted&type=FACEBOOK&orderBy=createdAt&sortDirection=ASC&search_by=title&search=판교&page=-1&page_count=10"; + ResponseEntity responseEntity = restTemplate.getForEntity(url, List.class); + + assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + } + + @Test + public void 유효하지_않은_page_count_값() { + String url = "/api/posts?hashtag=wanted&type=FACEBOOK&orderBy=createdAt&sortDirection=ASC&search_by=title&search=판교&page=0&page_count=-10"; + ResponseEntity responseEntity = restTemplate.getForEntity(url, List.class); + + assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + } + + @Test + public void search_키워드가_없을_때() { + String url = "/api/posts?hashtag=wanted&type=FACEBOOK&orderBy=createdAt&sortDirection=ASC&search_by=title&page=0&page_count=10"; + ResponseEntity responseEntity = restTemplate.getForEntity(url, List.class); + + assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK); + List posts = responseEntity.getBody(); + assertThat(posts).isNotEmpty(); + } +} \ No newline at end of file From 6f06467e3e60f61cfe7fc843f1af4a716db2a798 Mon Sep 17 00:00:00 2001 From: Youngjoo Date: Mon, 26 Aug 2024 20:29:17 +0900 Subject: [PATCH 10/11] =?UTF-8?q?refactor=20:=EA=B2=8C=EC=8B=9C=EB=AC=BC?= =?UTF-8?q?=20=EB=AA=A9=EB=A1=9D=20=EC=A4=91=EB=B3=B5=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20=EC=96=B4=EB=85=B8=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=88=98=EC=A0=95,=20EOL=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../media/content/service/PostService.java | 36 ------ ...tion.java => PostListCustomException.java} | 4 +- .../handler/GlobalExceptionHandler.java | 8 +- .../media/post/controller/PostController.java | 26 ++-- .../java/wanted/media/post/dto/PostDto.java | 34 ++---- .../media/post/service/PostService.java | 14 +-- src/main/resources/data.sql | 19 --- .../post/controller/PostControllerTest.java | 115 +++++++++++------- 8 files changed, 107 insertions(+), 149 deletions(-) delete mode 100644 src/main/java/wanted/media/content/service/PostService.java rename src/main/java/wanted/media/exception/{CustomException.java => PostListCustomException.java} (60%) delete mode 100644 src/main/resources/data.sql diff --git a/src/main/java/wanted/media/content/service/PostService.java b/src/main/java/wanted/media/content/service/PostService.java deleted file mode 100644 index 03affb6..0000000 --- a/src/main/java/wanted/media/content/service/PostService.java +++ /dev/null @@ -1,36 +0,0 @@ -package wanted.media.content.service; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import wanted.media.content.domain.Post; -import wanted.media.content.domain.Type; -import wanted.media.content.repository.PostRepository; -import wanted.media.user.repository.UserRepository; - -@Service -public class PostService { - @Autowired - private UserRepository userRepository; - - @Autowired - private PostRepository postRepository; - - @Transactional(readOnly = true) - public Page findPosts(String account, Type type, String orderBy, String sortDirection, String searchBy, String search, int page, int pageCount) { - Sort sort = Sort.by(Sort.Direction.fromString(sortDirection), orderBy); - Pageable pageable = PageRequest.of(page, pageCount, sort); - -// if (search == null || search.isEmpty()) { -// return postRepository.findAll(); -// //throw new IllegalStateException("해당하는 태그를 찾을 수 없습니다."); -// } - - return postRepository.findBySearchContaining(account, type, searchBy, search, pageable); - } - -} diff --git a/src/main/java/wanted/media/exception/CustomException.java b/src/main/java/wanted/media/exception/PostListCustomException.java similarity index 60% rename from src/main/java/wanted/media/exception/CustomException.java rename to src/main/java/wanted/media/exception/PostListCustomException.java index a8f4885..5919e77 100644 --- a/src/main/java/wanted/media/exception/CustomException.java +++ b/src/main/java/wanted/media/exception/PostListCustomException.java @@ -3,10 +3,10 @@ import lombok.Getter; @Getter -public class CustomException extends RuntimeException { +public class PostListCustomException extends RuntimeException { private final ErrorCode errorCode; - public CustomException(ErrorCode errorCode) { + public PostListCustomException(ErrorCode errorCode) { super(errorCode.getMessage()); this.errorCode = errorCode; } diff --git a/src/main/java/wanted/media/exception/handler/GlobalExceptionHandler.java b/src/main/java/wanted/media/exception/handler/GlobalExceptionHandler.java index 50333f0..adc1bc4 100644 --- a/src/main/java/wanted/media/exception/handler/GlobalExceptionHandler.java +++ b/src/main/java/wanted/media/exception/handler/GlobalExceptionHandler.java @@ -4,9 +4,9 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; -import wanted.media.exception.CustomException; import wanted.media.exception.ErrorCode; import wanted.media.exception.ErrorResponse; +import wanted.media.exception.PostListCustomException; @RestControllerAdvice public class GlobalExceptionHandler { @@ -16,9 +16,9 @@ public ResponseEntity handleBadRequestException(BadRequestExcepti return ResponseEntity.badRequest() .body(new ErrorResponse(400, e.getMessage())); } - - @ExceptionHandler(CustomException.class) - public ResponseEntity handleCustomException(CustomException ex) { + + @ExceptionHandler(PostListCustomException.class) + public ResponseEntity handleCustomException(PostListCustomException ex) { ErrorCode errorCode = ex.getErrorCode(); ErrorResponse response = new ErrorResponse(errorCode); return new ResponseEntity<>(response, errorCode.getStatus()); diff --git a/src/main/java/wanted/media/post/controller/PostController.java b/src/main/java/wanted/media/post/controller/PostController.java index 832f921..df8a1b1 100644 --- a/src/main/java/wanted/media/post/controller/PostController.java +++ b/src/main/java/wanted/media/post/controller/PostController.java @@ -2,6 +2,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -21,22 +23,24 @@ public class PostController { private PostService postService; @GetMapping - public List list(@RequestParam(value = "hashtag", required = true) String account, - @RequestParam(value = "type", required = false) Type type, - @RequestParam(value = "orderBy", defaultValue = "createdAt") String orderBy, - @RequestParam(value = "sortDirection", defaultValue = "ASC") String sortDirection, - @RequestParam(value = "search_by", defaultValue = "title, content") String searchBy, - @RequestParam(value = "search", required = false) String search, - @RequestParam(value = "page", defaultValue = "0") int page, - @RequestParam(value = "page_count", defaultValue = "10") int pageCount) { + public ResponseEntity> list(@RequestParam(value = "hashtag", required = true) String account, + @RequestParam(value = "type", required = false) Type type, + @RequestParam(value = "orderBy", defaultValue = "createdAt") String orderBy, + @RequestParam(value = "sortDirection", defaultValue = "ASC") String sortDirection, + @RequestParam(value = "search_by", defaultValue = "title, content") String searchBy, + @RequestParam(value = "search", required = false) String search, + @RequestParam(value = "page", defaultValue = "0") int page, + @RequestParam(value = "page_count", defaultValue = "10") int pageCount) { Page postPage = postService.findPosts(account, type, orderBy, sortDirection, searchBy, search, page, pageCount); List postDtos = postPage.getContent().stream() .map(PostDto::allPosts) .collect(Collectors.toList()); - //System.out.println("PostDtos: " + postDtos); - - return postDtos; + if (postDtos.isEmpty()) { + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + return new ResponseEntity<>(postDtos, HttpStatus.OK); } } diff --git a/src/main/java/wanted/media/post/dto/PostDto.java b/src/main/java/wanted/media/post/dto/PostDto.java index cbe960d..2740c8d 100644 --- a/src/main/java/wanted/media/post/dto/PostDto.java +++ b/src/main/java/wanted/media/post/dto/PostDto.java @@ -1,31 +1,23 @@ package wanted.media.post.dto; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; import wanted.media.post.domain.Post; import wanted.media.post.domain.Type; import java.time.LocalDateTime; -@AllArgsConstructor -@NoArgsConstructor -@Getter -@Setter -public class PostDto { - private String id; - private Long likeCount; - private Type type; - private String title; - private String content; - private String hashtags; - private Long viewCount; - private Long shareCount; - private LocalDateTime updatedAt; - private LocalDateTime createdAt; - private String account; - +public record PostDto( + String id, + Long likeCount, + Type type, + String title, + String content, + String hashtags, + Long viewCount, + Long shareCount, + LocalDateTime updatedAt, + LocalDateTime createdAt, + String account +) { public static PostDto allPosts(Post post) { return new PostDto( post.getId(), diff --git a/src/main/java/wanted/media/post/service/PostService.java b/src/main/java/wanted/media/post/service/PostService.java index 62baa7d..a42aecf 100644 --- a/src/main/java/wanted/media/post/service/PostService.java +++ b/src/main/java/wanted/media/post/service/PostService.java @@ -1,25 +1,19 @@ package wanted.media.post.service; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import wanted.media.exception.CustomException; -import wanted.media.exception.ErrorCode; import wanted.media.post.domain.Post; import wanted.media.post.domain.Type; import wanted.media.post.repository.PostRepository; -import wanted.media.user.repository.UserRepository; @Service +@RequiredArgsConstructor public class PostService { - @Autowired - private UserRepository userRepository; - - @Autowired private PostRepository postRepository; @Transactional(readOnly = true) @@ -27,11 +21,7 @@ public Page findPosts(String account, Type type, String orderBy, String so Sort sort = Sort.by(Sort.Direction.fromString(sortDirection), orderBy); Pageable pageable = PageRequest.of(page, pageCount, sort); - // 예시: 검색 조건에 맞는 게시물이 없는 경우 예외 처리 Page posts = postRepository.findBySearchContaining(account, type, searchBy, search, pageable); - if (posts.isEmpty()) { - throw new CustomException(ErrorCode.ENTITY_NOT_FOUND); - } return posts; } diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql deleted file mode 100644 index 4c6ede2..0000000 --- a/src/main/resources/data.sql +++ /dev/null @@ -1,19 +0,0 @@ -INSERT INTO users(account, email, password, grade) -VALUES ('wanted', 'wanted@gmail.com', 'wanted', 'orange'); - -INSERT INTO contents(like_count, type, title, content, hashtags, view_count, share_count, updated_at, created_at, - user_id) -VALUES (5, 'FACEBOOK', '판교 나들이', '오늘은 판교에 와보았어요. 미래의 직장이 될 곳들이 많이...', 'wanted', 54, 30, '2024-08-23', '2024-08-20', - 'wanted'); -INSERT INTO contents(like_count, type, title, content, hashtags, view_count, share_count, updated_at, created_at, - user_id) -VALUES (15, 'TWITTER', '판교 나들이', '오늘은 판교에 와보았어요. 미래의 직장이 될 곳들이 많이...', 'wanted', 154, 10, '2024-08-23', '2024-08-20', - 'wanted'); -INSERT INTO contents(like_count, type, title, content, hashtags, view_count, share_count, updated_at, created_at, - user_id) -VALUES (1, 'THREADS', '판교 나들이', '오늘은 판교에 와보았어요. 미래의 직장이 될 곳들이 많이...', 'wanted', 43, 2, '2024-08-23', '2024-08-20', - 'wanted'); -INSERT INTO contents(like_count, type, title, content, hashtags, view_count, share_count, updated_at, created_at, - user_id) -VALUES (30, 'INSTAGRAM', '판교 나들이', '오늘은 판교에 와보았어요. 미래의 직장이 될 곳들이 많이...', 'wanted', 24, 7, '2024-08-23', '2024-08-20', - 'wanted'); \ No newline at end of file diff --git a/src/test/java/wanted/media/post/controller/PostControllerTest.java b/src/test/java/wanted/media/post/controller/PostControllerTest.java index 4b9d23d..ce1de43 100644 --- a/src/test/java/wanted/media/post/controller/PostControllerTest.java +++ b/src/test/java/wanted/media/post/controller/PostControllerTest.java @@ -1,67 +1,94 @@ package wanted.media.post.controller; +import lombok.RequiredArgsConstructor; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; +import org.springframework.http.MediaType; import org.springframework.test.context.TestPropertySource; -import wanted.media.post.dto.PostDto; +import org.springframework.test.web.servlet.MockMvc; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest -@TestPropertySource(locations = "classpath:application-test.properties") +@RequiredArgsConstructor +@TestPropertySource(locations = "classpath:application-test.yml") class PostControllerTest { - @Autowired - private TestRestTemplate restTemplate; + private MockMvc mockMvc; @Test - public void posts_list_성공() { - // When - String url = "/api/posts?hashtag=wanted&type=FACEBOOK&orderBy=createdAt&sortDirection=ASC&search_by=title&search=판교&page=0&page_count=10"; - ResponseEntity responseEntity = restTemplate.getForEntity(url, List.class); - - // Then - assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK); - List posts = responseEntity.getBody(); - assertThat(posts).isNotEmpty(); + void posts_list_성공() throws Exception { + mockMvc.perform(get("/api/posts") + .param("hashtag", "wanted") + .param("type", "FACEBOOK") + .param("orderBy", "createdAt") + .param("sortDirection", "ASC") + .param("search_by", "title") + .param("search", "판교") + .param("page", "0") + .param("page_count", "10") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isNotEmpty()); // 응답이 비어있지 않음을 확인 } @Test - public void search_by_성공() { - String url = "/api/posts?hashtag=wanted&type=FACEBOOK&orderBy=createdAt&sortDirection=ASC&search_by=invalidSearchBy&page=0&page_count=10"; - ResponseEntity responseEntity = restTemplate.getForEntity(url, List.class); - - assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + void search_by_성공() throws Exception { + mockMvc.perform(get("/api/posts") + .param("hashtag", "wanted") + .param("type", "FACEBOOK") + .param("orderBy", "createdAt") + .param("sortDirection", "ASC") + .param("search_by", "invalidSearchBy") + .param("page", "0") + .param("page_count", "10") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()); // 잘못된 검색 조건에 대한 에러 확인 } @Test - public void 유효하지_않은_page값() { - String url = "/api/posts?hashtag=wanted&type=FACEBOOK&orderBy=createdAt&sortDirection=ASC&search_by=title&search=판교&page=-1&page_count=10"; - ResponseEntity responseEntity = restTemplate.getForEntity(url, List.class); - - assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + void 유효하지_않은_page값() throws Exception { + mockMvc.perform(get("/api/posts") + .param("hashtag", "wanted") + .param("type", "FACEBOOK") + .param("orderBy", "createdAt") + .param("sortDirection", "ASC") + .param("search_by", "title") + .param("search", "판교") + .param("page", "-1") // 유효하지 않은 페이지 번호 + .param("page_count", "10") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()); // 페이지 값이 유효하지 않음을 확인 } @Test - public void 유효하지_않은_page_count_값() { - String url = "/api/posts?hashtag=wanted&type=FACEBOOK&orderBy=createdAt&sortDirection=ASC&search_by=title&search=판교&page=0&page_count=-10"; - ResponseEntity responseEntity = restTemplate.getForEntity(url, List.class); - - assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + void 유효하지_않은_page_count_값() throws Exception { + mockMvc.perform(get("/api/posts") + .param("hashtag", "wanted") + .param("type", "FACEBOOK") + .param("orderBy", "createdAt") + .param("sortDirection", "ASC") + .param("search_by", "title") + .param("search", "판교") + .param("page", "0") + .param("page_count", "-10") // 유효하지 않은 페이지 수 + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()); // 페이지 수가 유효하지 않음을 확인 } - + @Test - public void search_키워드가_없을_때() { - String url = "/api/posts?hashtag=wanted&type=FACEBOOK&orderBy=createdAt&sortDirection=ASC&search_by=title&page=0&page_count=10"; - ResponseEntity responseEntity = restTemplate.getForEntity(url, List.class); - - assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK); - List posts = responseEntity.getBody(); - assertThat(posts).isNotEmpty(); + void search_키워드가_없을_때() throws Exception { + mockMvc.perform(get("/api/posts") + .param("hashtag", "wanted") + .param("type", "FACEBOOK") + .param("orderBy", "createdAt") + .param("sortDirection", "ASC") + .param("search_by", "title") + .param("page", "0") + .param("page_count", "10") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isNotEmpty()); // 응답이 비어있지 않음을 확인 } -} \ No newline at end of file +} From 9454ccf142584378d875a55255476a53068314a5 Mon Sep 17 00:00:00 2001 From: Youngjoo Date: Mon, 26 Aug 2024 21:12:59 +0900 Subject: [PATCH 11/11] =?UTF-8?q?refactor=20:=EA=B2=8C=EC=8B=9C=EB=AC=BC?= =?UTF-8?q?=20=EB=AA=A9=EB=A1=9D=20final=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/wanted/media/post/service/PostService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/wanted/media/post/service/PostService.java b/src/main/java/wanted/media/post/service/PostService.java index a42aecf..733d5da 100644 --- a/src/main/java/wanted/media/post/service/PostService.java +++ b/src/main/java/wanted/media/post/service/PostService.java @@ -14,7 +14,7 @@ @Service @RequiredArgsConstructor public class PostService { - private PostRepository postRepository; + private final PostRepository postRepository; @Transactional(readOnly = true) public Page findPosts(String account, Type type, String orderBy, String sortDirection, String searchBy, String search, int page, int pageCount) {