728x90
이전글
https://chordplaylist.tistory.com/231
댓글 조회
- 앤드포인트
GET/posts/{postId}/comments[?page=0]
- 리턴
{
"resultCode": "SUCCESS",
"result":{
"content":[
{
"id": 3,
"comment": "comment test3",
"userName": "test",
"postId": 2,
"createdAt": "2022-12-20T16:07:25.699346"
},
{
"id": 2,
"comment": "comment test2",
"userName": "test",
"postId": 2,
"createdAt": "2022-12-20T16:03:30.670768"
}
],
"pageable":{"sort":{"empty": false, "sorted": true, "unsorted": false },
"offset": 0,…},
"last": true,
"totalPages": 1,
"totalElements": 2,
"size": 10,
"number": 0,
"sort":{
"empty": false,
"sorted": true,
"unsorted": false
},
"numberOfElements": 2,
"first": true,
"empty": false
}
}
Comment Read Response
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
public class CommentReadResponse {
private Integer id;
private String comment;
private String userName;
private Integer postId;
private LocalDateTime createdAt;
}
테스트 코드 작성
@Test
@DisplayName("댓글 조회 성공")
@WithMockUser
void success_get_comment() throws Exception {
String url = String.format("/api/v1/posts/%d/comments", 1);
mockMvc.perform(get(url))
.andExpect(status().isOk())
.andDo(print());
}
API 구현
Controller
...
public class CommentRestController {
private final CommentService commentService;
@ApiOperation(value = "댓글 조회")
@GetMapping("/{postId}/comments")
public Response<Page<CommentReadResponse>> getComments(@PathVariable Integer postId) {
PageRequest pageable = PageRequest.of(0, 10, Sort.by("createdAt").descending());
List<CommentReadResponse> comments = commentService.getAllComments(pageable, postId);
return Response.success(new PageImpl<>(comments));
}
}
- 해당 Post의 댓글들을 조회합니다.
- 10건씩 끊어서 페이징을 하며 createdAt을 기준으로 정렬합니다.
Service
...
public class CommentService {
...
@Transactional
public List<CommentReadResponse> getAllComments(PageRequest pageable, Integer postId) {
Post post = service.validateGetPostById(postId);
Page<Comment> comments = commentRepository.findCommentsByPost(post, pageable);
return comments.stream()
.map(Comment::toResponse)
.collect(Collectors.toList());
}
}
- 건네받은 postId로 Post를 찾아옵니다.
- 찾아온 post와 페이징 정보를 담은 pageable을 매개변수로 댓글들을 찾아옵니다.
- 반아온 댓글 목록을 스트림과 toResponse() 메서드를 사용하여 List<CommentReadResponse>로 만들어 다시 반환합니다.
댓글 삭제
- 앤드포인트
DELETE/posts/{postsId}/comments/{id}
- 리턴
{
"resultCode": "SUCCESS",
"result":{
"message": "댓글 삭제 완료",
"id": 4
}
}
Comment Delete Response
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class CommentDeleteResponse {
private String message;
private Integer id;
}
테스트 코드 작성
@Test
@DisplayName("댓글 삭제 성공")
void success_delete_comment() throws Exception {
User user = UserFixture.get("chordpli", "1234");
Post post = PostFixture.get(user);
Comment comment = CommentFixture.get(user, post);
given(commentService.deleteComment(any(), any(), any())).willReturn(new CommentDeleteResponse("댓글 삭제 완료", 1));
String url = String.format("/api/v1/posts/%d/comments/%d", post.getId(), comment.getId());
mockMvc.perform(delete(url).with(csrf())
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(1)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.resultCode").exists())
.andExpect(jsonPath("$.resultCode").value("SUCCESS"))
.andExpect(jsonPath("$.result.id").exists())
.andExpect(jsonPath("$.result.id").value(1))
.andExpect(jsonPath("$.result.message").exists())
.andExpect(jsonPath("$.result.message").value("댓글 삭제 완료"))
.andDo(print());
}
@Test
@DisplayName("댓글 삭제 실패 - 인증 실패")
void fail_delete_comment_certification() throws Exception {
User user = UserFixture.get("chordpli", "1234");
Post post = PostFixture.get(user);
Comment comment = CommentFixture.get(user, post);
given(commentService.deleteComment(any(), any(), any())).willThrow(new SNSAppException(INVALID_TOKEN, INVALID_TOKEN.getMessage()));
String url = String.format("/api/v1/posts/%d/comments/%d", post.getId(), comment.getId());
mockMvc.perform(delete(url).with(csrf())
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(1)))
.andExpect(status().isUnauthorized())
.andDo(print());
}
@Test
@DisplayName("댓글 삭제 실패 - 댓글 불일치")
void fail_delete_comment_mismatch_comment() throws Exception {
User user = UserFixture.get("chordpli", "1234");
Post post = PostFixture.get(user);
Comment comment = CommentFixture.get(user, post);
given(commentService.deleteComment(any(), any(), any())).willThrow(new SNSAppException(MISMATCH_COMMENT, MISMATCH_COMMENT.getMessage()));
String url = String.format("/api/v1/posts/%d/comments/%d", post.getId(), comment.getId());
mockMvc.perform(delete(url).with(csrf())
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(1)))
.andExpect(status().isUnauthorized())
.andDo(print());
}
@Test
@DisplayName("댓글 삭제 실패 - 작성자 불일치")
void fail_delete_comment_mismatch_writer() throws Exception {
User user = UserFixture.get("chordpli", "1234");
Post post = PostFixture.get(user);
Comment comment = CommentFixture.get(user, post);
given(commentService.deleteComment(any(), any(), any())).willThrow(new SNSAppException(MISMATCH_USER, MISMATCH_USER.getMessage()));
String url = String.format("/api/v1/posts/%d/comments/%d", post.getId(), comment.getId());
mockMvc.perform(delete(url).with(csrf())
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(1)))
.andExpect(status().isUnauthorized())
.andDo(print());
}
@Test
@DisplayName("댓글 삭제 실패 - 데이터베이스 에러")
void fail_delete_comment_db_error() throws Exception {
User user = UserFixture.get("chordpli", "1234");
Post post = PostFixture.get(user);
Comment comment = CommentFixture.get(user, post);
given(commentService.deleteComment(any(), any(), any())).willThrow(new SNSAppException(DATABASE_ERROR, DATABASE_ERROR.getMessage()));
String url = String.format("/api/v1/posts/%d/comments/%d", post.getId(), comment.getId());
mockMvc.perform(delete(url).with(csrf())
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(1)))
.andExpect(status().isInternalServerError())
.andDo(print());
}
API 구현
Controller
@Slf4j
public class CommentRestController {
@ApiOperation(value = "댓글 삭제")
@DeleteMapping("/{postId}/comments/{id}")
public Response<CommentDeleteResponse> deleteComment(@PathVariable Integer postId,
@PathVariable Integer id,
Authentication authentication) {
String userName = authentication.getName();
CommentDeleteResponse response = commentService.deleteComment(postId, id, userName);
return Response.success(response);
}
}
- postId와 댓글 id를 받아옵니다.
- authentication으로 userName을 얻어 postId, 댓글 id와 함께 service로 건네줍니다.
Service
...
public class CommentService {
@Transactional
public CommentDeleteResponse deleteComment(Integer postId, Integer id, String userName) {
Post post = service.validateGetPostById(postId);
User user = service.validateGetUserByUserName(userName);
Comment comment = service.validateGetCommentById(id);
service.validateMatchUsers(user, comment);
commentRepository.delete(comment);
return new CommentDeleteResponse("댓글 삭제 완료", id);
}
- postId로 Post 정보, userName으로 User 정도, id로 Comment 정보를 받아옵니다.
- validateMathUser메서드를 사용하여 해당 api를 요청한 User와 Comment를 작성한 User가 일치하는지 확인합니다.
- 코멘스를 삭제하고, id와 지정한 문자열을 반환합니다.
반응형
'프로젝트 > Archive' 카테고리의 다른 글
[08] Alarm Api 개발 (0) | 2023.01.06 |
---|---|
[07] Like Api 개발 (0) | 2023.01.05 |
[06] Comment Api 개발 - 1 (1) | 2023.01.04 |
[05] 리팩토링 - 2 (0) | 2022.12.29 |
[05] 리팩토링 - 1 (0) | 2022.12.29 |