From be3248fe2ad9a3cec169780245f371ea336031e7 Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Fri, 4 Aug 2023 20:07:24 +0900 Subject: [PATCH 01/18] =?UTF-8?q?feat:=20merge=20conflict=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/prgms/boardservice/domain/post/Post.java | 6 ++++-- src/main/java/org/prgms/boardservice/domain/user/User.java | 7 ------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/prgms/boardservice/domain/post/Post.java b/src/main/java/org/prgms/boardservice/domain/post/Post.java index 7c28e883f..9ecb81a49 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/Post.java +++ b/src/main/java/org/prgms/boardservice/domain/post/Post.java @@ -30,13 +30,15 @@ public class Post extends BaseTime { private Long userId; - public Post(String title, String content) { + public Post(String title, String content, Long userId) { validateTitleLength(title); validateContentLength(content); this.title = title; this.content = content; - + this.userId = userId; + } + public void changeTitle(String title) { validateTitleLength(title); this.title = title; diff --git a/src/main/java/org/prgms/boardservice/domain/user/User.java b/src/main/java/org/prgms/boardservice/domain/user/User.java index bba337e7f..5d82e7585 100644 --- a/src/main/java/org/prgms/boardservice/domain/user/User.java +++ b/src/main/java/org/prgms/boardservice/domain/user/User.java @@ -6,13 +6,9 @@ import lombok.Getter; import lombok.NoArgsConstructor; import org.prgms.boardservice.domain.BaseTime; -import org.prgms.boardservice.domain.post.Post; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; import java.util.regex.Pattern; import static org.prgms.boardservice.util.ErrorMessage.*; @@ -39,9 +35,6 @@ public class User extends BaseTime { @NotBlank private String nickname; - @OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST) - private List posts = new ArrayList<>(); - public User(String email, String password, String nickname) { validateEmailPattern(email); validatePasswordPattern(password); From 5ebccb2943cd49dc268d243451e610ad7814b100 Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Fri, 4 Aug 2023 20:10:13 +0900 Subject: [PATCH 02/18] =?UTF-8?q?refactor:=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=95=94=ED=98=B8=ED=99=94=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 - .../java/org/prgms/boardservice/domain/user/User.java | 9 +-------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/build.gradle b/build.gradle index 4db71c4f8..405dbc367 100644 --- a/build.gradle +++ b/build.gradle @@ -35,7 +35,6 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' implementation 'org.springframework.boot:spring-boot-starter-validation:3.0.4' - implementation 'org.springframework.boot:spring-boot-starter-security' } tasks.named('test') { diff --git a/src/main/java/org/prgms/boardservice/domain/user/User.java b/src/main/java/org/prgms/boardservice/domain/user/User.java index 5d82e7585..59111867d 100644 --- a/src/main/java/org/prgms/boardservice/domain/user/User.java +++ b/src/main/java/org/prgms/boardservice/domain/user/User.java @@ -6,8 +6,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import org.prgms.boardservice.domain.BaseTime; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; import java.util.regex.Pattern; @@ -41,7 +39,7 @@ public User(String email, String password, String nickname) { validateNicknameLength(nickname); this.email = email; - this.password = encodePassword(password); + this.password = password; this.nickname = nickname; } @@ -66,9 +64,4 @@ private void validateNicknameLength(String nickname) { throw new IllegalArgumentException(INVALID_USER_NICKNAME_LENGTH.getMessage()); } } - - private String encodePassword(String password) { - PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); - return passwordEncoder.encode(password); - } } From 3967e19397d48541529ffc9210ed567793450599 Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Fri, 4 Aug 2023 20:12:40 +0900 Subject: [PATCH 03/18] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20DTO=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/prgms/boardservice/domain/post/Post.java | 7 +++---- .../domain/post/dto/PostCreateRequestDto.java | 14 ++++++++++++++ .../domain/post/dto/PostResponseDto.java | 10 ++++++++++ .../domain/post/dto/PostUpdateRequestDto.java | 4 ++++ 4 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/prgms/boardservice/domain/post/dto/PostCreateRequestDto.java create mode 100644 src/main/java/org/prgms/boardservice/domain/post/dto/PostResponseDto.java create mode 100644 src/main/java/org/prgms/boardservice/domain/post/dto/PostUpdateRequestDto.java diff --git a/src/main/java/org/prgms/boardservice/domain/post/Post.java b/src/main/java/org/prgms/boardservice/domain/post/Post.java index 9ecb81a49..d9ee7c914 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/Post.java +++ b/src/main/java/org/prgms/boardservice/domain/post/Post.java @@ -2,18 +2,17 @@ import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.NonNull; +import lombok.*; import org.prgms.boardservice.domain.BaseTime; import static org.prgms.boardservice.util.ErrorMessage.INVALID_POST_CONTENT; import static org.prgms.boardservice.util.ErrorMessage.INVALID_POST_TITLE; +@Builder @Getter @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor public class Post extends BaseTime { @Id diff --git a/src/main/java/org/prgms/boardservice/domain/post/dto/PostCreateRequestDto.java b/src/main/java/org/prgms/boardservice/domain/post/dto/PostCreateRequestDto.java new file mode 100644 index 000000000..07658253c --- /dev/null +++ b/src/main/java/org/prgms/boardservice/domain/post/dto/PostCreateRequestDto.java @@ -0,0 +1,14 @@ +package org.prgms.boardservice.domain.post.dto; + +import org.prgms.boardservice.domain.post.Post; + +public record PostCreateRequestDto(String title, String content, Long userId) { + + public Post toEntity() { + return Post.builder() + .title(title) + .content(content) + .userId(userId) + .build(); + } +} diff --git a/src/main/java/org/prgms/boardservice/domain/post/dto/PostResponseDto.java b/src/main/java/org/prgms/boardservice/domain/post/dto/PostResponseDto.java new file mode 100644 index 000000000..78d32e002 --- /dev/null +++ b/src/main/java/org/prgms/boardservice/domain/post/dto/PostResponseDto.java @@ -0,0 +1,10 @@ +package org.prgms.boardservice.domain.post.dto; + +import org.prgms.boardservice.domain.post.Post; + +public record PostResponseDto(Long id, String title, String content, Long userId) { + + public PostResponseDto(Post post) { + this(post.getId(), post.getTitle(), post.getContent(), post.getUserId()); + } +} diff --git a/src/main/java/org/prgms/boardservice/domain/post/dto/PostUpdateRequestDto.java b/src/main/java/org/prgms/boardservice/domain/post/dto/PostUpdateRequestDto.java new file mode 100644 index 000000000..9565ffbcd --- /dev/null +++ b/src/main/java/org/prgms/boardservice/domain/post/dto/PostUpdateRequestDto.java @@ -0,0 +1,4 @@ +package org.prgms.boardservice.domain.post.dto; + +public record PostUpdateRequestDto(String title, String content) { +} From 934a88c6f63d2479e5e812593028f999b928ce44 Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Fri, 4 Aug 2023 20:13:50 +0900 Subject: [PATCH 04/18] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EC=A0=84=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/prgms/boardservice/domain/post/PostService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/prgms/boardservice/domain/post/PostService.java b/src/main/java/org/prgms/boardservice/domain/post/PostService.java index 5fcff7fdb..9cd2356db 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/PostService.java +++ b/src/main/java/org/prgms/boardservice/domain/post/PostService.java @@ -52,6 +52,7 @@ public Page getByPage(Pageable pageable) { @Transactional public void deleteById(Long id) { + postRepository.findById(id).orElseThrow(() -> new NoSuchElementException(NOT_FOUND_POST.getMessage())); postRepository.deleteById(id); } From 3e71461c8126affcbef042ca46d6ad7cdc6d5dd1 Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Fri, 4 Aug 2023 20:14:02 +0900 Subject: [PATCH 05/18] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/PostController.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/main/java/org/prgms/boardservice/domain/post/PostController.java diff --git a/src/main/java/org/prgms/boardservice/domain/post/PostController.java b/src/main/java/org/prgms/boardservice/domain/post/PostController.java new file mode 100644 index 000000000..ce9b6a022 --- /dev/null +++ b/src/main/java/org/prgms/boardservice/domain/post/PostController.java @@ -0,0 +1,69 @@ +package org.prgms.boardservice.domain.post; + +import org.prgms.boardservice.domain.post.dto.PostCreateRequestDto; +import org.prgms.boardservice.domain.post.dto.PostResponseDto; +import org.prgms.boardservice.domain.post.dto.PostUpdateRequestDto; +import org.prgms.boardservice.domain.post.vo.PostUpdateVo; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.net.URI; +import java.util.NoSuchElementException; + +@RestController +@RequestMapping("/api/v1/posts") +public class PostController { + + private final PostService postService; + + public PostController(PostService postService) { + this.postService = postService; + } + + @ExceptionHandler(NoSuchElementException.class) + private ResponseEntity exceptionHandle(Exception exception) { + return ResponseEntity.badRequest() + .body(exception.getMessage()); + } + + @PostMapping + public ResponseEntity create(@RequestBody PostCreateRequestDto postCreateRequestDto) { + Long postId = postService.create(postCreateRequestDto.toEntity()); + + return ResponseEntity.created(URI.create("/api/v1/posts/" + postId)) + .build(); + } + + @GetMapping("/{id}") + public ResponseEntity getOne(@PathVariable Long id) throws NoSuchElementException { + PostResponseDto postResponseDto = new PostResponseDto(postService.getById(id)); + + return ResponseEntity.ok(postResponseDto); + } + + @GetMapping + public ResponseEntity> getPage(Pageable pageable) throws NoSuchElementException { + Page page = postService.getByPage(pageable); + + return ResponseEntity.ok(page.map(PostResponseDto::new)); + } + + @PatchMapping("/{id}") + public ResponseEntity update(@RequestBody PostUpdateRequestDto postUpdateRequestDto, @PathVariable Long id) { + Long postId = postService.update(new PostUpdateVo(id, postUpdateRequestDto.title(), postUpdateRequestDto.content())); + + return ResponseEntity.noContent() + .location(URI.create("/api/v1/posts/" + postId)) + .build(); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable Long id) { + postService.deleteById(id); + + return ResponseEntity.noContent() + .build(); + } +} From f08b0308db1da5c52b7fa3101168f9b314a82141 Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Tue, 8 Aug 2023 15:22:09 +0900 Subject: [PATCH 06/18] =?UTF-8?q?feat:=20=EC=97=94=ED=8B=B0=ED=8B=B0?= =?UTF-8?q?=EC=97=90=20@DynamicUpdate=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/org/prgms/boardservice/domain/post/Post.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/prgms/boardservice/domain/post/Post.java b/src/main/java/org/prgms/boardservice/domain/post/Post.java index d9ee7c914..bc1c26185 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/Post.java +++ b/src/main/java/org/prgms/boardservice/domain/post/Post.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; import lombok.*; +import org.hibernate.annotations.DynamicUpdate; import org.prgms.boardservice.domain.BaseTime; import static org.prgms.boardservice.util.ErrorMessage.INVALID_POST_CONTENT; @@ -13,6 +14,7 @@ @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor +@DynamicUpdate public class Post extends BaseTime { @Id From 00bf9eaa6eea590d7c3a281ba39600c8f7b6f83d Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Tue, 8 Aug 2023 15:23:10 +0900 Subject: [PATCH 07/18] =?UTF-8?q?feat:=20Page=EC=A0=95=EB=B3=B4=EB=A5=BC?= =?UTF-8?q?=20=EB=8B=B4=EC=9D=80=20DTO=20=EC=83=9D=EC=85=A9=20=EB=B0=8F=20?= =?UTF-8?q?=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/PostController.java | 6 ++-- .../boardservice/domain/post/dto/PageDto.java | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/prgms/boardservice/domain/post/dto/PageDto.java diff --git a/src/main/java/org/prgms/boardservice/domain/post/PostController.java b/src/main/java/org/prgms/boardservice/domain/post/PostController.java index ce9b6a022..28431e68d 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/PostController.java +++ b/src/main/java/org/prgms/boardservice/domain/post/PostController.java @@ -1,5 +1,6 @@ package org.prgms.boardservice.domain.post; +import org.prgms.boardservice.domain.post.dto.PageDto; import org.prgms.boardservice.domain.post.dto.PostCreateRequestDto; import org.prgms.boardservice.domain.post.dto.PostResponseDto; import org.prgms.boardservice.domain.post.dto.PostUpdateRequestDto; @@ -44,10 +45,11 @@ public ResponseEntity getOne(@PathVariable Long id) throws NoSu } @GetMapping - public ResponseEntity> getPage(Pageable pageable) throws NoSuchElementException { + public ResponseEntity> getPage(Pageable pageable) throws NoSuchElementException { Page page = postService.getByPage(pageable); + Page postResponseDtoPage = page.map(PostResponseDto::new); - return ResponseEntity.ok(page.map(PostResponseDto::new)); + return ResponseEntity.ok(new PageDto<>(postResponseDtoPage)); } @PatchMapping("/{id}") diff --git a/src/main/java/org/prgms/boardservice/domain/post/dto/PageDto.java b/src/main/java/org/prgms/boardservice/domain/post/dto/PageDto.java new file mode 100644 index 000000000..bad28f81c --- /dev/null +++ b/src/main/java/org/prgms/boardservice/domain/post/dto/PageDto.java @@ -0,0 +1,36 @@ +package org.prgms.boardservice.domain.post.dto; + +import org.springframework.data.domain.Page; + +import java.util.List; + +public record PageDto( + List data, + PageableResponse pageable +) { + public PageDto(Page page) { + this(page.getContent(), new PageableResponse<>(page)); + } + + private record PageableResponse( + boolean first, + boolean last, + int number, + int size, + String sort, + int totalPages, + long totalElements + ) { + private PageableResponse(Page page) { + this( + page.isFirst(), + page.isLast(), + page.getNumber(), + page.getSize(), + page.getSort().toString(), + page.getTotalPages(), + page.getTotalElements() + ); + } + } +} From 57ea02d4b7b11f3a55f076c67be81fb570af1044 Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Tue, 8 Aug 2023 15:23:25 +0900 Subject: [PATCH 08/18] =?UTF-8?q?test:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/PostControllerTest.java | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 src/test/java/org/prgms/boardservice/domain/post/PostControllerTest.java diff --git a/src/test/java/org/prgms/boardservice/domain/post/PostControllerTest.java b/src/test/java/org/prgms/boardservice/domain/post/PostControllerTest.java new file mode 100644 index 000000000..fdb07e45d --- /dev/null +++ b/src/test/java/org/prgms/boardservice/domain/post/PostControllerTest.java @@ -0,0 +1,153 @@ +package org.prgms.boardservice.domain.post; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.prgms.boardservice.domain.post.dto.PostCreateRequestDto; +import org.prgms.boardservice.domain.post.dto.PostUpdateRequestDto; +import org.prgms.boardservice.domain.post.vo.PostUpdateVo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@WebMvcTest +@AutoConfigureRestDocs +class PostControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private PostService postService; + + @Autowired + private ObjectMapper objectMapper; + + @Test + @DisplayName("게시글이 성공적으로 생성된다.") + void success_Save_Post() throws Exception { + PostCreateRequestDto requestDto = new PostCreateRequestDto("title", "content", 1L); + + // given + given(postService.create(any(Post.class))).willReturn(1L); + + // when + ResultActions resultActions = mockMvc.perform(post("/api/v1/posts") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto))); + + // then + resultActions + .andExpect(status().isCreated()) + .andExpect(header().stringValues("Location", "/api/v1/posts/1")) + .andDo(print()); + } + + @Test + @DisplayName("게시글을 id로 조회할 수 있다.") + void success_Get_One_Post() throws Exception { + // given + Long postId = 1L; + Post post = new Post(postId, "title", "content", 1L); + + given(postService.getById(postId)).willReturn(post); + + // when + ResultActions resultActions = mockMvc.perform(get("/api/v1/posts/{id}", postId)); + + // then + resultActions + .andExpect(status().isOk()) + .andExpect(jsonPath("id").value(post.getId())) + .andExpect(jsonPath("title").value(post.getTitle())) + .andExpect(jsonPath("content").value(post.getContent())) + .andExpect(jsonPath("userId").value(post.getUserId())) + .andDo(print()); + } + + @Test + @DisplayName("게시글을 페이지로 조회할 수 있다.") + void success_Get_Page_Post() throws Exception { + // given + PageRequest pageRequest = PageRequest.of(0, 2, Sort.by(Sort.Direction.DESC, "id")); + + Post post1 = new Post(1L, "title1", "content1", 1L); + Post post2 = new Post(2L, "title2", "content2", 1L); + + Page posts = new PageImpl<>(List.of(post2, post1), pageRequest, 3); + + given(postService.getByPage(pageRequest)).willReturn(posts); + + // when + ResultActions resultActions = mockMvc.perform(get("/api/v1/posts") + .param("page", String.valueOf(0)) + .param("size", String.valueOf(2)) + .param("sort", "id,desc")); + + // then + resultActions + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data[0].id").value(post2.getId())) + .andExpect(jsonPath("$.data[1].id").value(post1.getId())) + .andExpect(jsonPath("$.pageable.first").value(true)) + .andExpect(jsonPath("$.pageable.last").value(false)) + .andExpect(jsonPath("$.pageable.number").value(0)) + .andExpect(jsonPath("$.pageable.size").value(2)) + .andExpect(jsonPath("$.pageable.totalPages").value(2)) + .andExpect(jsonPath("$.pageable.totalElements").value(3)) + .andExpect(jsonPath("$.pageable.sort").value("id: DESC")) + .andDo(print()); + } + + @Test + @DisplayName("게시글이 성공적으로 수정된다.") + void success_Update_Post() throws Exception { + Long postId = 1L; + PostUpdateRequestDto requestDto = new PostUpdateRequestDto("new-title", "new-content"); + + // given + given(postService.update(any(PostUpdateVo.class))).willReturn(1L); + + // when + ResultActions resultActions = mockMvc.perform(patch("/api/v1/posts/{id}", postId) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto))); + + // then + resultActions + .andExpect(status().isNoContent()) + .andExpect(header().stringValues("Location", "/api/v1/posts/1")) + .andDo(print()); + } + + @Test + @DisplayName("게시글을 id로 삭제할 수 있다.") + void success_Delete_Post() throws Exception { + // given + Long postId = 1L; + + // when + ResultActions resultActions = mockMvc.perform(delete("/api/v1/posts/{id}", postId)); + + // then + resultActions + .andExpect(status().isNoContent()) + .andDo(print()); + } +} From 5431976fab5ee41a31d6690912b7bf503c8a84dd Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Tue, 8 Aug 2023 18:02:13 +0900 Subject: [PATCH 09/18] =?UTF-8?q?feat:=20asciidoc=EA=B4=80=EB=A0=A8=20grad?= =?UTF-8?q?le=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index 405dbc367..a9e9688e7 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,7 @@ java { } configurations { + asciidoctorExt compileOnly { extendsFrom annotationProcessor } @@ -33,6 +34,7 @@ dependencies { runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' + asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor' testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' implementation 'org.springframework.boot:spring-boot-starter-validation:3.0.4' } @@ -44,5 +46,6 @@ tasks.named('test') { tasks.named('asciidoctor') { inputs.dir snippetsDir + configurations 'asciidoctorExt' dependsOn test } From 106210f3dc825ad3423e1b6f5d199228be7d6af1 Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Tue, 8 Aug 2023 18:03:02 +0900 Subject: [PATCH 10/18] =?UTF-8?q?test:=20REST=20Docs=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/PostControllerTest.java | 73 +++++++++++++++++-- 1 file changed, 66 insertions(+), 7 deletions(-) diff --git a/src/test/java/org/prgms/boardservice/domain/post/PostControllerTest.java b/src/test/java/org/prgms/boardservice/domain/post/PostControllerTest.java index fdb07e45d..83ff28cfb 100644 --- a/src/test/java/org/prgms/boardservice/domain/post/PostControllerTest.java +++ b/src/test/java/org/prgms/boardservice/domain/post/PostControllerTest.java @@ -15,6 +15,8 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.payload.JsonFieldType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; @@ -22,6 +24,12 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @@ -56,7 +64,17 @@ void success_Save_Post() throws Exception { resultActions .andExpect(status().isCreated()) .andExpect(header().stringValues("Location", "/api/v1/posts/1")) - .andDo(print()); + .andDo(print()) + .andDo(document("post-save", + requestFields( + fieldWithPath("title").type(JsonFieldType.STRING).description("제목"), + fieldWithPath("content").type(JsonFieldType.STRING).description("내용"), + fieldWithPath("userId").type(JsonFieldType.NUMBER).description("유저 id") + ), + responseHeaders( + headerWithName("location").description("리소스 위치") + )) + ); } @Test @@ -69,7 +87,7 @@ void success_Get_One_Post() throws Exception { given(postService.getById(postId)).willReturn(post); // when - ResultActions resultActions = mockMvc.perform(get("/api/v1/posts/{id}", postId)); + ResultActions resultActions = mockMvc.perform(RestDocumentationRequestBuilders.get("/api/v1/posts/{id}", postId)); // then resultActions @@ -78,7 +96,18 @@ void success_Get_One_Post() throws Exception { .andExpect(jsonPath("title").value(post.getTitle())) .andExpect(jsonPath("content").value(post.getContent())) .andExpect(jsonPath("userId").value(post.getUserId())) - .andDo(print()); + .andDo(print()) + .andDo(document("post-get-one", + pathParameters( + parameterWithName("id").description("게시글 id") + ), + responseFields( + fieldWithPath("id").type(JsonFieldType.NUMBER).description("게시글 id"), + fieldWithPath("title").type(JsonFieldType.STRING).description("제목"), + fieldWithPath("content").type(JsonFieldType.STRING).description("내용"), + fieldWithPath("userId").type(JsonFieldType.NUMBER).description("유저 id") + )) + ); } @Test @@ -112,7 +141,23 @@ void success_Get_Page_Post() throws Exception { .andExpect(jsonPath("$.pageable.totalPages").value(2)) .andExpect(jsonPath("$.pageable.totalElements").value(3)) .andExpect(jsonPath("$.pageable.sort").value("id: DESC")) - .andDo(print()); + .andDo(print()) + .andDo(document("post-get-by-page", + responseFields( + fieldWithPath("data[]").type(JsonFieldType.ARRAY).description("데이터"), + fieldWithPath("data[].id").type(JsonFieldType.NUMBER).description("게시글 id"), + fieldWithPath("data[].title").type(JsonFieldType.STRING).description("제목"), + fieldWithPath("data[].content").type(JsonFieldType.STRING).description("내용"), + fieldWithPath("data[].userId").type(JsonFieldType.NUMBER).description("유저 id"), + fieldWithPath("pageable.first").type(JsonFieldType.BOOLEAN).description("처음 페이지 여부"), + fieldWithPath("pageable.last").type(JsonFieldType.BOOLEAN).description("마지막 페이지 여부"), + fieldWithPath("pageable.number").type(JsonFieldType.NUMBER).description("페이지 번호"), + fieldWithPath("pageable.size").type(JsonFieldType.NUMBER).description("페이지 당 게시글 개수"), + fieldWithPath("pageable.sort").type(JsonFieldType.STRING).description("정렬 기준"), + fieldWithPath("pageable.totalPages").type(JsonFieldType.NUMBER).description("전체 페이지 수"), + fieldWithPath("pageable.totalElements").type(JsonFieldType.NUMBER).description("전체 게시글 수") + )) + ); } @Test @@ -133,7 +178,16 @@ void success_Update_Post() throws Exception { resultActions .andExpect(status().isNoContent()) .andExpect(header().stringValues("Location", "/api/v1/posts/1")) - .andDo(print()); + .andDo(print()) + .andDo(document("post-update", + requestFields( + fieldWithPath("title").type(JsonFieldType.STRING).description("제목"), + fieldWithPath("content").type(JsonFieldType.STRING).description("내용") + ), + responseHeaders( + headerWithName("location").description("리소스 위치") + )) + ); } @Test @@ -143,11 +197,16 @@ void success_Delete_Post() throws Exception { Long postId = 1L; // when - ResultActions resultActions = mockMvc.perform(delete("/api/v1/posts/{id}", postId)); + ResultActions resultActions = mockMvc.perform(RestDocumentationRequestBuilders.delete("/api/v1/posts/{id}", postId)); // then resultActions .andExpect(status().isNoContent()) - .andDo(print()); + .andDo(print()) + .andDo(document("post-delete", + pathParameters( + parameterWithName("id").description("게시글 id") + ) + )); } } From 6db0a1f2e83736d96f5483710fbbfa6b2ff28769 Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Tue, 8 Aug 2023 18:03:49 +0900 Subject: [PATCH 11/18] =?UTF-8?q?docs:=20API=20=EB=AC=B8=EC=84=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/index.adoc | 76 ++++ src/docs/asciidoc/index.html | 764 +++++++++++++++++++++++++++++++++++ 2 files changed, 840 insertions(+) create mode 100644 src/docs/asciidoc/index.adoc create mode 100644 src/docs/asciidoc/index.html diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc new file mode 100644 index 000000000..a32f8bf64 --- /dev/null +++ b/src/docs/asciidoc/index.adoc @@ -0,0 +1,76 @@ +:hardbreaks: +ifndef::snippets[] +:snippets: ../../build/generated-snippets +endif::[] + +:icons: font +:source-highlighter: highlightjs +:toc: left +:toclevels: 2 +:sectlinks: +:sectnums: +:docinfo: shared-head + +== 게시글 + +=== 게시글 생성 + +==== [POST] /api/v1/posts + +.Request +include::{snippets}/post-save/http-request.adoc[] + +.Request Fields +include::{snippets}/post-save/request-fields.adoc[] + +.Response +include::{snippets}/post-save/http-response.adoc[] + +=== 게시글 조회 + +==== [GET] /api/v1/posts/{id} + +.Request +include::{snippets}/post-get-one/http-request.adoc[] + +.Response +include::{snippets}/post-get-one/http-response.adoc[] + +.Response Fields +include::{snippets}/post-get-one/response-fields.adoc[] + +=== 게시글 페이지 조회 + +==== [GET] /api/v1/posts?page={page}&size={size}&sort={sort} + +.Request +include::{snippets}/post-get-by-page/http-request.adoc[] + +.Response +include::{snippets}/post-get-by-page/http-response.adoc[] + +.Response Fields +include::{snippets}/post-get-by-page/response-fields.adoc[] + +=== 게시글 수정 + +==== [PATCH] /api/v1/posts/{id} + +.Request +include::{snippets}/post-update/http-request.adoc[] + +.Request Fields +include::{snippets}/post-update/request-fields.adoc[] + +.Response +include::{snippets}/post-update/http-response.adoc[] + +=== 게시글 삭제 + +==== [DELETE] /api/v1/posts/{id} + +.Request +include::{snippets}/post-delete/http-request.adoc[] + +.Response +include::{snippets}/post-delete/http-response.adoc[] diff --git a/src/docs/asciidoc/index.html b/src/docs/asciidoc/index.html new file mode 100644 index 000000000..20c7d46a1 --- /dev/null +++ b/src/docs/asciidoc/index.html @@ -0,0 +1,764 @@ + + + + + + + +게시글 + + + + + + + +
+
+

1. 게시글

+
+
+

1.1. 게시글 생성

+
+

1.1.1. [POST] /api/v1/posts

+
+
Request
+
+
POST /api/v1/posts HTTP/1.1
+Content-Type: application/json;charset=UTF-8
+Content-Length: 48
+Host: localhost:8080
+
+{"title":"title","content":"content","userId":1}
+
+
+ + +++++ + + + + + + + + + + + + + + + + + + + + + + + + +
Table 1. Request Fields
PathTypeDescription

title

String

제목

content

String

내용

userId

Number

유저 id

+
+
Response
+
+
HTTP/1.1 201 Created
+Location: /api/v1/posts/1
+
+
+
+
+
+

1.2. 게시글 조회

+
+

1.2.1. [GET] /api/v1/posts/{id}

+
+
Request
+
+
GET /api/v1/posts/1 HTTP/1.1
+Host: localhost:8080
+
+
+
+
Response
+
+
HTTP/1.1 200 OK
+Content-Type: application/json
+Content-Length: 55
+
+{"id":1,"title":"title","content":"content","userId":1}
+
+
+ + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 2. Response Fields
PathTypeDescription

id

Number

게시글 id

title

String

제목

content

String

내용

userId

Number

유저 id

+
+
+
+

1.3. 게시글 페이지 조회

+
+

1.3.1. [GET] /api/v1/posts?page={page}&size={size}&sort={sort}

+
+
Request
+
+
GET /api/v1/posts?page=0&size=2&sort=id%2Cdesc HTTP/1.1
+Host: localhost:8080
+
+
+
+
Response
+
+
HTTP/1.1 200 OK
+Content-Type: application/json
+Content-Length: 236
+
+{"data":[{"id":2,"title":"title2","content":"content2","userId":1},{"id":1,"title":"title1","content":"content1","userId":1}],"pageable":{"first":true,"last":false,"number":0,"size":2,"sort":"id: DESC","totalPages":2,"totalElements":3}}
+
+
+ + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 3. Response Fields
PathTypeDescription

data[]

Array

데이터

data[].id

Number

게시글 id

data[].title

String

제목

data[].content

String

내용

data[].userId

Number

유저 id

pageable.first

Boolean

처음 페이지 여부

pageable.last

Boolean

마지막 페이지 여부

pageable.number

Number

페이지 번호

pageable.size

Number

페이지 당 게시글 개수

pageable.sort

String

정렬 기준

pageable.totalPages

Number

전체 페이지 수

pageable.totalElements

Number

전체 게시글 수

+
+
+
+

1.4. 게시글 수정

+
+

1.4.1. [PATCH] /api/v1/posts/{id}

+
+
Request
+
+
PATCH /api/v1/posts/1 HTTP/1.1
+Content-Type: application/json;charset=UTF-8
+Content-Length: 45
+Host: localhost:8080
+
+{"title":"new-title","content":"new-content"}
+
+
+ + +++++ + + + + + + + + + + + + + + + + + + + +
Table 4. Request Fields
PathTypeDescription

title

String

제목

content

String

내용

+
+
Response
+
+
HTTP/1.1 204 No Content
+Location: /api/v1/posts/1
+
+
+
+
+
+

1.5. 게시글 삭제

+
+

1.5.1. [DELETE] /api/v1/posts/{id}

+
+
Request
+
+
DELETE /api/v1/posts/1 HTTP/1.1
+Host: localhost:8080
+
+
+
+
Response
+
+
HTTP/1.1 204 No Content
+
+
+
+
+
+
+
+ + + + + \ No newline at end of file From 7d80f7a7b41575f42eb64736911c381b32b95b29 Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Wed, 9 Aug 2023 17:26:37 +0900 Subject: [PATCH 12/18] =?UTF-8?q?refactor:=20Title,=20Content=20VO?= =?UTF-8?q?=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boardservice/domain/post/Content.java | 34 +++++++++++++ .../prgms/boardservice/domain/post/Post.java | 42 +++------------- .../boardservice/domain/post/PostService.java | 2 +- .../prgms/boardservice/domain/post/Title.java | 34 +++++++++++++ .../domain/post/dto/PostCreateRequestDto.java | 6 ++- .../domain/post/dto/PostResponseDto.java | 2 +- .../domain/post/PostControllerTest.java | 6 +-- .../domain/post/PostServiceTest.java | 8 ++-- .../boardservice/domain/post/PostTest.java | 48 +++++++++---------- 9 files changed, 112 insertions(+), 70 deletions(-) create mode 100644 src/main/java/org/prgms/boardservice/domain/post/Content.java create mode 100644 src/main/java/org/prgms/boardservice/domain/post/Title.java diff --git a/src/main/java/org/prgms/boardservice/domain/post/Content.java b/src/main/java/org/prgms/boardservice/domain/post/Content.java new file mode 100644 index 000000000..2b8daab2b --- /dev/null +++ b/src/main/java/org/prgms/boardservice/domain/post/Content.java @@ -0,0 +1,34 @@ +package org.prgms.boardservice.domain.post; + +import jakarta.persistence.Embeddable; +import jakarta.persistence.Lob; +import jakarta.validation.constraints.NotNull; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import static org.prgms.boardservice.util.ErrorMessage.INVALID_POST_CONTENT; +import static org.springframework.util.StringUtils.hasText; + +@Embeddable +@EqualsAndHashCode +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class Content { + + @Lob + @NotNull + private String value; + + public Content(String value) { + validateContentLength(value); + this.value = value; + } + + private void validateContentLength(String value) { + if (!hasText(value) || value.length() > 500) { + throw new IllegalArgumentException(INVALID_POST_CONTENT.getMessage()); + } + } +} diff --git a/src/main/java/org/prgms/boardservice/domain/post/Post.java b/src/main/java/org/prgms/boardservice/domain/post/Post.java index e9f0ea7c2..9583100d8 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/Post.java +++ b/src/main/java/org/prgms/boardservice/domain/post/Post.java @@ -1,14 +1,11 @@ package org.prgms.boardservice.domain.post; import jakarta.persistence.*; -import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.*; import org.hibernate.annotations.DynamicUpdate; import org.prgms.boardservice.domain.BaseTime; -import static org.prgms.boardservice.util.ErrorMessage.INVALID_POST_CONTENT; -import static org.prgms.boardservice.util.ErrorMessage.INVALID_POST_TITLE; - @Builder @Getter @Entity @@ -21,47 +18,22 @@ public class Post extends BaseTime { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(length = 20) - @NotBlank - private String title; + @Embedded + private Title title; - @Lob - @NotBlank - private String content; + @Embedded + private Content content; private Long userId; - public Post(String title, String content, Long userId) { - validateTitleLength(title); - validateContentLength(content); - + public Post(Title title, Content content, Long userId) { this.title = title; this.content = content; this.userId = userId; } - public void changeTitle(String title) { - validateTitleLength(title); - this.title = title; - } - - public void update(String title, String content) { - validateTitleLength(title); - validateContentLength(content); - + public void update(Title title, Content content) { this.title = title; this.content = content; } - - private void validateTitleLength(String title) { - if (!hasText(title) || title.length() > 20) { - throw new IllegalArgumentException(INVALID_POST_TITLE.getMessage()); - } - } - - private void validateContentLength(String content) { - if (!hasText(content) || content.length() > 500) { - throw new IllegalArgumentException(INVALID_POST_CONTENT.getMessage()); - } - } } diff --git a/src/main/java/org/prgms/boardservice/domain/post/PostService.java b/src/main/java/org/prgms/boardservice/domain/post/PostService.java index bc6e344e4..60f11b98e 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/PostService.java +++ b/src/main/java/org/prgms/boardservice/domain/post/PostService.java @@ -28,7 +28,7 @@ public Long update(PostUpdateVo postUpdateVo) { Post findPost = postRepository.findById(postUpdateVo.id()) .orElseThrow(() -> new NoSuchElementException(NOT_FOUND_POST.getMessage())); - findPost.update(postUpdateVo.title(), postUpdateVo.content()); + findPost.update(new Title(postUpdateVo.title()), new Content(postUpdateVo.content())); return postRepository.save(findPost).getId(); } diff --git a/src/main/java/org/prgms/boardservice/domain/post/Title.java b/src/main/java/org/prgms/boardservice/domain/post/Title.java new file mode 100644 index 000000000..24b20e7b1 --- /dev/null +++ b/src/main/java/org/prgms/boardservice/domain/post/Title.java @@ -0,0 +1,34 @@ +package org.prgms.boardservice.domain.post; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable;import jakarta.validation.constraints.NotNull; +import lombok.AccessLevel; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import static org.prgms.boardservice.util.ErrorMessage.INVALID_POST_TITLE; +import static org.springframework.util.StringUtils.hasText; + +@Embeddable +@EqualsAndHashCode +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class Title { + + @Column(length = 20) + @NotNull + private String value; + + public Title(String value) { + validateTitleLength(value); + this.value = value; + } + + private void validateTitleLength(String value) { + if (!hasText(value) || value.length() > 20) { + throw new IllegalArgumentException(INVALID_POST_TITLE.getMessage()); + } + } +} diff --git a/src/main/java/org/prgms/boardservice/domain/post/dto/PostCreateRequestDto.java b/src/main/java/org/prgms/boardservice/domain/post/dto/PostCreateRequestDto.java index 07658253c..8e7a3332f 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/dto/PostCreateRequestDto.java +++ b/src/main/java/org/prgms/boardservice/domain/post/dto/PostCreateRequestDto.java @@ -1,13 +1,15 @@ package org.prgms.boardservice.domain.post.dto; +import org.prgms.boardservice.domain.post.Content; import org.prgms.boardservice.domain.post.Post; +import org.prgms.boardservice.domain.post.Title; public record PostCreateRequestDto(String title, String content, Long userId) { public Post toEntity() { return Post.builder() - .title(title) - .content(content) + .title(new Title(title)) + .content(new Content(content)) .userId(userId) .build(); } diff --git a/src/main/java/org/prgms/boardservice/domain/post/dto/PostResponseDto.java b/src/main/java/org/prgms/boardservice/domain/post/dto/PostResponseDto.java index 78d32e002..06e892fbb 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/dto/PostResponseDto.java +++ b/src/main/java/org/prgms/boardservice/domain/post/dto/PostResponseDto.java @@ -5,6 +5,6 @@ public record PostResponseDto(Long id, String title, String content, Long userId) { public PostResponseDto(Post post) { - this(post.getId(), post.getTitle(), post.getContent(), post.getUserId()); + this(post.getId(), post.getTitle().getValue(), post.getContent().getValue(), post.getUserId()); } } diff --git a/src/test/java/org/prgms/boardservice/domain/post/PostControllerTest.java b/src/test/java/org/prgms/boardservice/domain/post/PostControllerTest.java index 83ff28cfb..8853a3d06 100644 --- a/src/test/java/org/prgms/boardservice/domain/post/PostControllerTest.java +++ b/src/test/java/org/prgms/boardservice/domain/post/PostControllerTest.java @@ -82,7 +82,7 @@ void success_Save_Post() throws Exception { void success_Get_One_Post() throws Exception { // given Long postId = 1L; - Post post = new Post(postId, "title", "content", 1L); + Post post = new Post(postId, new Title("title"), new Content("content"), 1L); given(postService.getById(postId)).willReturn(post); @@ -116,8 +116,8 @@ void success_Get_Page_Post() throws Exception { // given PageRequest pageRequest = PageRequest.of(0, 2, Sort.by(Sort.Direction.DESC, "id")); - Post post1 = new Post(1L, "title1", "content1", 1L); - Post post2 = new Post(2L, "title2", "content2", 1L); + Post post1 = new Post(1L, new Title("title1"), new Content("content1"), 1L); + Post post2 = new Post(2L, new Title("title2"), new Content("content2"), 1L); Page posts = new PageImpl<>(List.of(post2, post1), pageRequest, 3); diff --git a/src/test/java/org/prgms/boardservice/domain/post/PostServiceTest.java b/src/test/java/org/prgms/boardservice/domain/post/PostServiceTest.java index 2833cd45c..3027c69cb 100644 --- a/src/test/java/org/prgms/boardservice/domain/post/PostServiceTest.java +++ b/src/test/java/org/prgms/boardservice/domain/post/PostServiceTest.java @@ -26,7 +26,7 @@ class PostServiceTest { @Autowired PostService postService; - private final Post post = new Post("title", "content", 1L); + private final Post post = new Post(new Title("title"), new Content("content"), 1L); @BeforeEach void setUp() { @@ -36,7 +36,7 @@ void setUp() { @Test @DisplayName("게시글이 성공적으로 생성된다.") void success_Create_Post() { - Long id = postService.create(new Post("title", "content", 1L)); + Long id = postService.create(new Post(new Title("title"), new Content("content"), 1L)); Post get = postService.getById(id); @@ -90,8 +90,8 @@ void fail_Get_Post() { @Test @DisplayName("게시글을 페이징하여 조회할 수 있다.") void success_Get_By_Page() { - Post post1 = new Post("title1", "content1", 1L); - Post post2 = new Post("title2", "content2", 1L); + Post post1 = new Post(1L, new Title("title1"), new Content("content1"), 1L); + Post post2 = new Post(2L, new Title("title2"), new Content("content2"), 1L); postService.create(post1); postService.create(post2); diff --git a/src/test/java/org/prgms/boardservice/domain/post/PostTest.java b/src/test/java/org/prgms/boardservice/domain/post/PostTest.java index e85ef0d78..efa5bd374 100644 --- a/src/test/java/org/prgms/boardservice/domain/post/PostTest.java +++ b/src/test/java/org/prgms/boardservice/domain/post/PostTest.java @@ -3,9 +3,13 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.ValueSource; +import java.util.stream.Stream; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.prgms.boardservice.util.ErrorMessage.INVALID_POST_CONTENT; @@ -14,8 +18,8 @@ class PostTest { private final Long userId = 1L; - private final String titleMoreThenTwentyLength = "TitleMoreThenTwentyLength"; - private final String contentMoreThanFiveHundredLength = "sit amet cursus sit amet dictum sit amet justo donec enim diam vulputate ut pharetra sit amet aliquam id diam maecenas ultricies mi eget mauris pharetra et ultrices neque ornare aenean euismod elementum nisi quis eleifend quam adipiscing vitae proin sagittis nisl rhoncus mattis rhoncus urna neque viverra justo nec ultrices dui sapien eget mi proin sed libero enim sed faucibus turpis in eu mi bibendum neque egestas congue quisque egestas diam in arcu cursus euismod quis viverra nibh cras pulvinar mattis nunc sed blandit libero volutpat sed cras ornare arcu dui vivamus arcu felis bibendum ut tristique et egestas"; + private static final String titleMoreThenTwentyLength = "TitleMoreThenTwentyLength"; + private static final String contentMoreThanFiveHundredLength = "sit amet cursus sit amet dictum sit amet justo donec enim diam vulputate ut pharetra sit amet aliquam id diam maecenas ultricies mi eget mauris pharetra et ultrices neque ornare aenean euismod elementum nisi quis eleifend quam adipiscing vitae proin sagittis nisl rhoncus mattis rhoncus urna neque viverra justo nec ultrices dui sapien eget mi proin sed libero enim sed faucibus turpis in eu mi bibendum neque egestas congue quisque egestas diam in arcu cursus euismod quis viverra nibh cras pulvinar mattis nunc sed blandit libero volutpat sed cras ornare arcu dui vivamus arcu felis bibendum ut tristique et egestas"; @ParameterizedTest(name = "test with value [{0}]") @NullAndEmptySource @@ -24,7 +28,7 @@ class PostTest { void fail_Post_Constructor_With_Invalid_Title(String title) { String content = "content"; - assertThatThrownBy(() -> new Post(title, content, userId)) + assertThatThrownBy(() -> new Post(new Title(title), new Content(content), userId)) .isInstanceOf(IllegalArgumentException.class) .hasMessage(INVALID_POST_TITLE.getMessage()); } @@ -36,7 +40,7 @@ void fail_Post_Constructor_With_Invalid_Title(String title) { void fail_Post_Constructor_With_Invalid_Content(String content) { String title = "title"; - assertThatThrownBy(() -> new Post(title, content, userId)) + assertThatThrownBy(() ->new Post(new Title(title), new Content(content), userId)) .isInstanceOf(IllegalArgumentException.class) .hasMessage(INVALID_POST_CONTENT.getMessage()); } @@ -44,32 +48,28 @@ void fail_Post_Constructor_With_Invalid_Content(String content) { @Test @DisplayName("게시글이 성공적으로 생성된다.") void success_New_Post() { - Post post = new Post("title", "content", userId); + Post post = new Post(new Title("title"), new Content("content"), userId); assertThat(post).isNotNull(); } - @ParameterizedTest(name = "test with value [{0}]") - @NullAndEmptySource - @ValueSource(strings = {" ", titleMoreThenTwentyLength}) - @DisplayName("게시글을 유효하지 않은 제목으로 변경할 수 없다.") - void fail_Change_Post_With_Invalid_Title(String title) { - Post post = new Post("title", "content", userId); + @ParameterizedTest(name = "title: {0}, content: {1}") + @MethodSource("postInvalidInfo") + @DisplayName("게시글을 유효하지 않은 제목, 내용으로 변경할 수 없다.") + void fail_Change_Post_With_Invalid_Title(String title, String content) { + Post post = new Post(new Title("title"), new Content("content"), userId); - assertThatThrownBy(() -> post.changeTitle(title)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage(INVALID_POST_TITLE.getMessage()); + assertThatThrownBy(() -> post.update(new Title(title), new Content(content))) + .isInstanceOf(IllegalArgumentException.class); } - @ParameterizedTest(name = "test with value [{0}]") - @NullAndEmptySource - @ValueSource(strings = {" ", contentMoreThanFiveHundredLength}) - @DisplayName("게시글을 유효하지 않은 내용으로 변경할 수 없다.") - void fail_Change_Post_With_Invalid_Content(String content) { - Post post = new Post("title", "content", userId); - - assertThatThrownBy(() -> post.changeContent(content)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage(INVALID_POST_CONTENT.getMessage()); + static Stream postInvalidInfo() { + return Stream.of( + Arguments.arguments(null, null), + Arguments.arguments("", ""), + Arguments.arguments(" ", " "), + Arguments.arguments(titleMoreThenTwentyLength, "content"), + Arguments.arguments("title", contentMoreThanFiveHundredLength) + ); } } From 9995f6d52e4c3a556206ca13c36382bd694ca9b2 Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Wed, 9 Aug 2023 17:27:52 +0900 Subject: [PATCH 13/18] =?UTF-8?q?feat:=20=EC=97=94=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=EC=BB=AC=EB=9F=BC=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=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 --- src/main/java/org/prgms/boardservice/domain/post/Post.java | 2 ++ src/main/java/org/prgms/boardservice/domain/user/User.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/main/java/org/prgms/boardservice/domain/post/Post.java b/src/main/java/org/prgms/boardservice/domain/post/Post.java index 9583100d8..d1d1bc986 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/Post.java +++ b/src/main/java/org/prgms/boardservice/domain/post/Post.java @@ -12,6 +12,7 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @DynamicUpdate +@EqualsAndHashCode(of = "id", callSuper = false) public class Post extends BaseTime { @Id @@ -24,6 +25,7 @@ public class Post extends BaseTime { @Embedded private Content content; + @NotNull private Long userId; public Post(Title title, Content content, Long userId) { diff --git a/src/main/java/org/prgms/boardservice/domain/user/User.java b/src/main/java/org/prgms/boardservice/domain/user/User.java index 581ad654b..d8ee4d9b6 100644 --- a/src/main/java/org/prgms/boardservice/domain/user/User.java +++ b/src/main/java/org/prgms/boardservice/domain/user/User.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; import lombok.AccessLevel; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import org.prgms.boardservice.domain.BaseTime; @@ -14,6 +15,7 @@ @Getter @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) +@EqualsAndHashCode(of = "id", callSuper = false) public class User extends BaseTime { private static final String EMAIL_REGEX = "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$"; From c33d3a40b9ae42f4a0c98dda142b0f1d433cebd1 Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Sat, 12 Aug 2023 10:09:45 +0900 Subject: [PATCH 14/18] =?UTF-8?q?refactor:=20@Embeddable=20=EC=BB=AC?= =?UTF-8?q?=EB=9F=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../prgms/boardservice/domain/post/Content.java | 11 ++++++----- .../org/prgms/boardservice/domain/post/Title.java | 14 ++++++-------- .../domain/post/dto/PostResponseDto.java | 2 +- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/prgms/boardservice/domain/post/Content.java b/src/main/java/org/prgms/boardservice/domain/post/Content.java index 2b8daab2b..0189523bd 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/Content.java +++ b/src/main/java/org/prgms/boardservice/domain/post/Content.java @@ -1,5 +1,6 @@ package org.prgms.boardservice.domain.post; +import jakarta.persistence.Column; import jakarta.persistence.Embeddable; import jakarta.persistence.Lob; import jakarta.validation.constraints.NotNull; @@ -18,12 +19,12 @@ public class Content { @Lob - @NotNull - private String value; + @Column(nullable = false) + private String content; - public Content(String value) { - validateContentLength(value); - this.value = value; + public Content(String content) { + validateContentLength(content); + this.content = content; } private void validateContentLength(String value) { diff --git a/src/main/java/org/prgms/boardservice/domain/post/Title.java b/src/main/java/org/prgms/boardservice/domain/post/Title.java index 24b20e7b1..9596dc59d 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/Title.java +++ b/src/main/java/org/prgms/boardservice/domain/post/Title.java @@ -1,9 +1,8 @@ package org.prgms.boardservice.domain.post; import jakarta.persistence.Column; -import jakarta.persistence.Embeddable;import jakarta.validation.constraints.NotNull; +import jakarta.persistence.Embeddable; import lombok.AccessLevel; - import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; @@ -17,13 +16,12 @@ @Getter public class Title { - @Column(length = 20) - @NotNull - private String value; + @Column(length = 20, nullable = false) + private String title; - public Title(String value) { - validateTitleLength(value); - this.value = value; + public Title(String title) { + validateTitleLength(title); + this.title = title; } private void validateTitleLength(String value) { diff --git a/src/main/java/org/prgms/boardservice/domain/post/dto/PostResponseDto.java b/src/main/java/org/prgms/boardservice/domain/post/dto/PostResponseDto.java index 06e892fbb..070c825f7 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/dto/PostResponseDto.java +++ b/src/main/java/org/prgms/boardservice/domain/post/dto/PostResponseDto.java @@ -5,6 +5,6 @@ public record PostResponseDto(Long id, String title, String content, Long userId) { public PostResponseDto(Post post) { - this(post.getId(), post.getTitle().getValue(), post.getContent().getValue(), post.getUserId()); + this(post.getId(), post.getTitle().getTitle(), post.getContent().getContent(), post.getUserId()); } } From ee48192724ed4c6c49d749d6a4f508f4c664b9bc Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Sat, 12 Aug 2023 10:10:28 +0900 Subject: [PATCH 15/18] =?UTF-8?q?refactor:=20ExceptionHandler=20=EB=B2=94?= =?UTF-8?q?=EC=9C=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/prgms/boardservice/domain/post/PostController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/prgms/boardservice/domain/post/PostController.java b/src/main/java/org/prgms/boardservice/domain/post/PostController.java index 28431e68d..9c0b87848 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/PostController.java +++ b/src/main/java/org/prgms/boardservice/domain/post/PostController.java @@ -24,7 +24,7 @@ public PostController(PostService postService) { } @ExceptionHandler(NoSuchElementException.class) - private ResponseEntity exceptionHandle(Exception exception) { + private ResponseEntity noSuchElementExceptionHandle(NoSuchElementException exception) { return ResponseEntity.badRequest() .body(exception.getMessage()); } From 0caa2da0312737efc9f270b504cffc6c6c8dd770 Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Sat, 12 Aug 2023 10:13:56 +0900 Subject: [PATCH 16/18] =?UTF-8?q?refactor:=20Dto,=20Vo=20suffix=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/PostController.java | 30 +++++++++---------- .../boardservice/domain/post/PostService.java | 8 ++--- .../dto/{PageDto.java => PageResponse.java} | 4 +-- ...RequestDto.java => PostCreateRequest.java} | 2 +- ...PostResponseDto.java => PostResponse.java} | 4 +-- .../domain/post/dto/PostUpdateRequest.java | 4 +++ .../domain/post/dto/PostUpdateRequestDto.java | 4 --- .../domain/post/vo/PostUpdate.java | 4 +++ .../domain/post/vo/PostUpdateVo.java | 4 --- .../domain/post/PostControllerTest.java | 12 ++++---- .../domain/post/PostServiceTest.java | 13 ++++---- 11 files changed, 44 insertions(+), 45 deletions(-) rename src/main/java/org/prgms/boardservice/domain/post/dto/{PageDto.java => PageResponse.java} (92%) rename src/main/java/org/prgms/boardservice/domain/post/dto/{PostCreateRequestDto.java => PostCreateRequest.java} (83%) rename src/main/java/org/prgms/boardservice/domain/post/dto/{PostResponseDto.java => PostResponse.java} (63%) create mode 100644 src/main/java/org/prgms/boardservice/domain/post/dto/PostUpdateRequest.java delete mode 100644 src/main/java/org/prgms/boardservice/domain/post/dto/PostUpdateRequestDto.java create mode 100644 src/main/java/org/prgms/boardservice/domain/post/vo/PostUpdate.java delete mode 100644 src/main/java/org/prgms/boardservice/domain/post/vo/PostUpdateVo.java diff --git a/src/main/java/org/prgms/boardservice/domain/post/PostController.java b/src/main/java/org/prgms/boardservice/domain/post/PostController.java index 9c0b87848..2fc11127a 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/PostController.java +++ b/src/main/java/org/prgms/boardservice/domain/post/PostController.java @@ -1,10 +1,10 @@ package org.prgms.boardservice.domain.post; -import org.prgms.boardservice.domain.post.dto.PageDto; -import org.prgms.boardservice.domain.post.dto.PostCreateRequestDto; -import org.prgms.boardservice.domain.post.dto.PostResponseDto; -import org.prgms.boardservice.domain.post.dto.PostUpdateRequestDto; -import org.prgms.boardservice.domain.post.vo.PostUpdateVo; +import org.prgms.boardservice.domain.post.dto.PageResponse; +import org.prgms.boardservice.domain.post.dto.PostCreateRequest; +import org.prgms.boardservice.domain.post.dto.PostResponse; +import org.prgms.boardservice.domain.post.dto.PostUpdateRequest; +import org.prgms.boardservice.domain.post.vo.PostUpdate; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; @@ -30,31 +30,31 @@ private ResponseEntity noSuchElementExceptionHandle(NoSuchElementExcepti } @PostMapping - public ResponseEntity create(@RequestBody PostCreateRequestDto postCreateRequestDto) { - Long postId = postService.create(postCreateRequestDto.toEntity()); + public ResponseEntity create(@RequestBody PostCreateRequest postCreateRequest) { + Long postId = postService.create(postCreateRequest.toEntity()); return ResponseEntity.created(URI.create("/api/v1/posts/" + postId)) .build(); } @GetMapping("/{id}") - public ResponseEntity getOne(@PathVariable Long id) throws NoSuchElementException { - PostResponseDto postResponseDto = new PostResponseDto(postService.getById(id)); + public ResponseEntity getOne(@PathVariable Long id) throws NoSuchElementException { + PostResponse postResponse = new PostResponse(postService.getById(id)); - return ResponseEntity.ok(postResponseDto); + return ResponseEntity.ok(postResponse); } @GetMapping - public ResponseEntity> getPage(Pageable pageable) throws NoSuchElementException { + public ResponseEntity> getPage(Pageable pageable) throws NoSuchElementException { Page page = postService.getByPage(pageable); - Page postResponseDtoPage = page.map(PostResponseDto::new); + Page postResponseDtoPage = page.map(PostResponse::new); - return ResponseEntity.ok(new PageDto<>(postResponseDtoPage)); + return ResponseEntity.ok(new PageResponse<>(postResponseDtoPage)); } @PatchMapping("/{id}") - public ResponseEntity update(@RequestBody PostUpdateRequestDto postUpdateRequestDto, @PathVariable Long id) { - Long postId = postService.update(new PostUpdateVo(id, postUpdateRequestDto.title(), postUpdateRequestDto.content())); + public ResponseEntity update(@RequestBody PostUpdateRequest postUpdateRequest, @PathVariable Long id) { + Long postId = postService.update(new PostUpdate(id, postUpdateRequest.title(), postUpdateRequest.content())); return ResponseEntity.noContent() .location(URI.create("/api/v1/posts/" + postId)) diff --git a/src/main/java/org/prgms/boardservice/domain/post/PostService.java b/src/main/java/org/prgms/boardservice/domain/post/PostService.java index 60f11b98e..9c1ef343c 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/PostService.java +++ b/src/main/java/org/prgms/boardservice/domain/post/PostService.java @@ -1,6 +1,6 @@ package org.prgms.boardservice.domain.post; -import org.prgms.boardservice.domain.post.vo.PostUpdateVo; +import org.prgms.boardservice.domain.post.vo.PostUpdate; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -24,11 +24,11 @@ public Long create(Post post) { return postRepository.save(post).getId(); } - public Long update(PostUpdateVo postUpdateVo) { - Post findPost = postRepository.findById(postUpdateVo.id()) + public Long update(PostUpdate postUpdate) { + Post findPost = postRepository.findById(postUpdate.id()) .orElseThrow(() -> new NoSuchElementException(NOT_FOUND_POST.getMessage())); - findPost.update(new Title(postUpdateVo.title()), new Content(postUpdateVo.content())); + findPost.update(new Title(postUpdate.title()), new Content(postUpdate.content())); return postRepository.save(findPost).getId(); } diff --git a/src/main/java/org/prgms/boardservice/domain/post/dto/PageDto.java b/src/main/java/org/prgms/boardservice/domain/post/dto/PageResponse.java similarity index 92% rename from src/main/java/org/prgms/boardservice/domain/post/dto/PageDto.java rename to src/main/java/org/prgms/boardservice/domain/post/dto/PageResponse.java index bad28f81c..b6642ade2 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/dto/PageDto.java +++ b/src/main/java/org/prgms/boardservice/domain/post/dto/PageResponse.java @@ -4,11 +4,11 @@ import java.util.List; -public record PageDto( +public record PageResponse( List data, PageableResponse pageable ) { - public PageDto(Page page) { + public PageResponse(Page page) { this(page.getContent(), new PageableResponse<>(page)); } diff --git a/src/main/java/org/prgms/boardservice/domain/post/dto/PostCreateRequestDto.java b/src/main/java/org/prgms/boardservice/domain/post/dto/PostCreateRequest.java similarity index 83% rename from src/main/java/org/prgms/boardservice/domain/post/dto/PostCreateRequestDto.java rename to src/main/java/org/prgms/boardservice/domain/post/dto/PostCreateRequest.java index 8e7a3332f..134f6b291 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/dto/PostCreateRequestDto.java +++ b/src/main/java/org/prgms/boardservice/domain/post/dto/PostCreateRequest.java @@ -4,7 +4,7 @@ import org.prgms.boardservice.domain.post.Post; import org.prgms.boardservice.domain.post.Title; -public record PostCreateRequestDto(String title, String content, Long userId) { +public record PostCreateRequest(String title, String content, Long userId) { public Post toEntity() { return Post.builder() diff --git a/src/main/java/org/prgms/boardservice/domain/post/dto/PostResponseDto.java b/src/main/java/org/prgms/boardservice/domain/post/dto/PostResponse.java similarity index 63% rename from src/main/java/org/prgms/boardservice/domain/post/dto/PostResponseDto.java rename to src/main/java/org/prgms/boardservice/domain/post/dto/PostResponse.java index 070c825f7..ce6455c6b 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/dto/PostResponseDto.java +++ b/src/main/java/org/prgms/boardservice/domain/post/dto/PostResponse.java @@ -2,9 +2,9 @@ import org.prgms.boardservice.domain.post.Post; -public record PostResponseDto(Long id, String title, String content, Long userId) { +public record PostResponse(Long id, String title, String content, Long userId) { - public PostResponseDto(Post post) { + public PostResponse(Post post) { this(post.getId(), post.getTitle().getTitle(), post.getContent().getContent(), post.getUserId()); } } diff --git a/src/main/java/org/prgms/boardservice/domain/post/dto/PostUpdateRequest.java b/src/main/java/org/prgms/boardservice/domain/post/dto/PostUpdateRequest.java new file mode 100644 index 000000000..70480e2a4 --- /dev/null +++ b/src/main/java/org/prgms/boardservice/domain/post/dto/PostUpdateRequest.java @@ -0,0 +1,4 @@ +package org.prgms.boardservice.domain.post.dto; + +public record PostUpdateRequest(String title, String content) { +} diff --git a/src/main/java/org/prgms/boardservice/domain/post/dto/PostUpdateRequestDto.java b/src/main/java/org/prgms/boardservice/domain/post/dto/PostUpdateRequestDto.java deleted file mode 100644 index 9565ffbcd..000000000 --- a/src/main/java/org/prgms/boardservice/domain/post/dto/PostUpdateRequestDto.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.prgms.boardservice.domain.post.dto; - -public record PostUpdateRequestDto(String title, String content) { -} diff --git a/src/main/java/org/prgms/boardservice/domain/post/vo/PostUpdate.java b/src/main/java/org/prgms/boardservice/domain/post/vo/PostUpdate.java new file mode 100644 index 000000000..b11e15a61 --- /dev/null +++ b/src/main/java/org/prgms/boardservice/domain/post/vo/PostUpdate.java @@ -0,0 +1,4 @@ +package org.prgms.boardservice.domain.post.vo; + +public record PostUpdate(Long id, String title, String content) { +} diff --git a/src/main/java/org/prgms/boardservice/domain/post/vo/PostUpdateVo.java b/src/main/java/org/prgms/boardservice/domain/post/vo/PostUpdateVo.java deleted file mode 100644 index 1c0987a31..000000000 --- a/src/main/java/org/prgms/boardservice/domain/post/vo/PostUpdateVo.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.prgms.boardservice.domain.post.vo; - -public record PostUpdateVo(Long id, String title, String content) { -} diff --git a/src/test/java/org/prgms/boardservice/domain/post/PostControllerTest.java b/src/test/java/org/prgms/boardservice/domain/post/PostControllerTest.java index 8853a3d06..c78a83ec6 100644 --- a/src/test/java/org/prgms/boardservice/domain/post/PostControllerTest.java +++ b/src/test/java/org/prgms/boardservice/domain/post/PostControllerTest.java @@ -3,9 +3,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.prgms.boardservice.domain.post.dto.PostCreateRequestDto; -import org.prgms.boardservice.domain.post.dto.PostUpdateRequestDto; -import org.prgms.boardservice.domain.post.vo.PostUpdateVo; +import org.prgms.boardservice.domain.post.dto.PostCreateRequest; +import org.prgms.boardservice.domain.post.dto.PostUpdateRequest; +import org.prgms.boardservice.domain.post.vo.PostUpdate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; @@ -50,7 +50,7 @@ class PostControllerTest { @Test @DisplayName("게시글이 성공적으로 생성된다.") void success_Save_Post() throws Exception { - PostCreateRequestDto requestDto = new PostCreateRequestDto("title", "content", 1L); + PostCreateRequest requestDto = new PostCreateRequest("title", "content", 1L); // given given(postService.create(any(Post.class))).willReturn(1L); @@ -164,10 +164,10 @@ void success_Get_Page_Post() throws Exception { @DisplayName("게시글이 성공적으로 수정된다.") void success_Update_Post() throws Exception { Long postId = 1L; - PostUpdateRequestDto requestDto = new PostUpdateRequestDto("new-title", "new-content"); + PostUpdateRequest requestDto = new PostUpdateRequest("new-title", "new-content"); // given - given(postService.update(any(PostUpdateVo.class))).willReturn(1L); + given(postService.update(any(PostUpdate.class))).willReturn(1L); // when ResultActions resultActions = mockMvc.perform(patch("/api/v1/posts/{id}", postId) diff --git a/src/test/java/org/prgms/boardservice/domain/post/PostServiceTest.java b/src/test/java/org/prgms/boardservice/domain/post/PostServiceTest.java index 3027c69cb..398c26ce1 100644 --- a/src/test/java/org/prgms/boardservice/domain/post/PostServiceTest.java +++ b/src/test/java/org/prgms/boardservice/domain/post/PostServiceTest.java @@ -1,10 +1,9 @@ package org.prgms.boardservice.domain.post; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.prgms.boardservice.domain.post.vo.PostUpdateVo; +import org.prgms.boardservice.domain.post.vo.PostUpdate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Page; @@ -48,23 +47,23 @@ void success_Create_Post() { @Test @DisplayName("게시글이 성공적으로 수정된다.") void success_Update_Post() { - PostUpdateVo postUpdateVo = new PostUpdateVo(post.getId(), "new-title", "new-content"); + PostUpdate postUpdate = new PostUpdate(post.getId(), "new-title", "new-content"); - Long id = postService.update(postUpdateVo); + Long id = postService.update(postUpdate); Post updated = postService.getById(id); assertThat(updated).usingRecursiveComparison() .comparingOnlyFields("id", "title", "content") - .isEqualTo(postUpdateVo); + .isEqualTo(postUpdate); } @Test @DisplayName("존재하지 않는 게시글은 수정에 실패한다.") void fail_Update_Post() { - PostUpdateVo postUpdateVo = new PostUpdateVo(Long.MAX_VALUE, "title", "content"); + PostUpdate postUpdate = new PostUpdate(Long.MAX_VALUE, "title", "content"); - assertThatThrownBy(() -> postService.update(postUpdateVo)) + assertThatThrownBy(() -> postService.update(postUpdate)) .isInstanceOf(NoSuchElementException.class) .hasMessage(NOT_FOUND_POST.getMessage()); } From 3cf8b2ea7ab7a16f5196edae098270b5a72e8eb6 Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Sat, 12 Aug 2023 10:39:42 +0900 Subject: [PATCH 17/18] =?UTF-8?q?refactor:=20Email,=20Password=20VO?= =?UTF-8?q?=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../prgms/boardservice/domain/user/Email.java | 34 ++++++++++++++++++ .../boardservice/domain/user/Password.java | 36 +++++++++++++++++++ .../prgms/boardservice/domain/user/User.java | 32 ++++------------- 3 files changed, 76 insertions(+), 26 deletions(-) create mode 100644 src/main/java/org/prgms/boardservice/domain/user/Email.java create mode 100644 src/main/java/org/prgms/boardservice/domain/user/Password.java diff --git a/src/main/java/org/prgms/boardservice/domain/user/Email.java b/src/main/java/org/prgms/boardservice/domain/user/Email.java new file mode 100644 index 000000000..2e924632d --- /dev/null +++ b/src/main/java/org/prgms/boardservice/domain/user/Email.java @@ -0,0 +1,34 @@ +package org.prgms.boardservice.domain.user; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.regex.Pattern; + +import static org.prgms.boardservice.util.ErrorMessage.INVALID_USER_EMAIL_PATTERN; + +@Embeddable +@EqualsAndHashCode +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class Email { + private static final String EMAIL_REGEX = "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$"; + + @Column(length = 20, unique = true) + private String email; + + public Email(String email) { + validateEmailPattern(email); + this.email = email; + } + + private void validateEmailPattern(String email) { + if (!Pattern.matches(EMAIL_REGEX, email)) { + throw new IllegalArgumentException(INVALID_USER_EMAIL_PATTERN.getMessage()); + } + } +} diff --git a/src/main/java/org/prgms/boardservice/domain/user/Password.java b/src/main/java/org/prgms/boardservice/domain/user/Password.java new file mode 100644 index 000000000..68073c2f5 --- /dev/null +++ b/src/main/java/org/prgms/boardservice/domain/user/Password.java @@ -0,0 +1,36 @@ +package org.prgms.boardservice.domain.user; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.validation.constraints.NotBlank; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.regex.Pattern; + +import static org.prgms.boardservice.util.ErrorMessage.INVALID_USER_PASSWORD_PATTERN; + +@Embeddable +@EqualsAndHashCode +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class Password { + + private static final String PASSWORD_REGEX = "^.*(?=^.{8,15}$)(?=.*\\d)(?=.*[a-zA-Z])(?=.*[!@#$%^&+=]).*$"; + + @Column(length = 100, nullable = false) + private String password; + + public Password(String password) { + validatePasswordPattern(password); + this.password = password; + } + + private void validatePasswordPattern(String password) { + if (!Pattern.matches(PASSWORD_REGEX, password)) { + throw new IllegalArgumentException(INVALID_USER_PASSWORD_PATTERN.getMessage()); + } + } +} diff --git a/src/main/java/org/prgms/boardservice/domain/user/User.java b/src/main/java/org/prgms/boardservice/domain/user/User.java index d8ee4d9b6..b1badc000 100644 --- a/src/main/java/org/prgms/boardservice/domain/user/User.java +++ b/src/main/java/org/prgms/boardservice/domain/user/User.java @@ -8,9 +8,7 @@ import lombok.NoArgsConstructor; import org.prgms.boardservice.domain.BaseTime; -import java.util.regex.Pattern; - -import static org.prgms.boardservice.util.ErrorMessage.*; +import static org.prgms.boardservice.util.ErrorMessage.INVALID_USER_NICKNAME_LENGTH; @Getter @Entity @@ -18,29 +16,23 @@ @EqualsAndHashCode(of = "id", callSuper = false) public class User extends BaseTime { - private static final String EMAIL_REGEX = "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$"; - private static final String PASSWORD_REGEX = "^.*(?=^.{8,15}$)(?=.*\\d)(?=.*[a-zA-Z])(?=.*[!@#$%^&+=]).*$"; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "user_id") private Long id; - @Column(length = 20, unique = true) - @NotBlank - private String email; + @Embedded + private Email email; - @Column(length = 100) - @NotBlank - private String password; + @Embedded + private Password password; @Column(length = 10, unique = true) @NotBlank private String nickname; - public User(String email, String password, String nickname) { - validateEmailPattern(email); - validatePasswordPattern(password); + public User(Email email, Password password, String nickname) { validateNicknameLength(nickname); this.email = email; @@ -48,18 +40,6 @@ public User(String email, String password, String nickname) { this.nickname = nickname; } - private void validateEmailPattern(String email) { - if (!Pattern.matches(EMAIL_REGEX, email)) { - throw new IllegalArgumentException(INVALID_USER_EMAIL_PATTERN.getMessage()); - } - } - - private void validatePasswordPattern(String password) { - if (!Pattern.matches(PASSWORD_REGEX, password)) { - throw new IllegalArgumentException(INVALID_USER_PASSWORD_PATTERN.getMessage()); - } - } - private void validateNicknameLength(String nickname) { if (nickname.length() < 2 || nickname.length() > 10) { throw new IllegalArgumentException(INVALID_USER_NICKNAME_LENGTH.getMessage()); From 75fedbb0fb24f9ff1ff185dfcf88188c1f2a3322 Mon Sep 17 00:00:00 2001 From: hyeon-z Date: Sat, 12 Aug 2023 10:42:10 +0900 Subject: [PATCH 18/18] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20import=EB=AC=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/prgms/boardservice/domain/post/Content.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/prgms/boardservice/domain/post/Content.java b/src/main/java/org/prgms/boardservice/domain/post/Content.java index 0189523bd..f32e2e208 100644 --- a/src/main/java/org/prgms/boardservice/domain/post/Content.java +++ b/src/main/java/org/prgms/boardservice/domain/post/Content.java @@ -3,7 +3,6 @@ import jakarta.persistence.Column; import jakarta.persistence.Embeddable; import jakarta.persistence.Lob; -import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.EqualsAndHashCode; import lombok.Getter;