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));
    }
}
  1. 해당 Post의 댓글들을 조회합니다.
  2. 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());
    }
}
  1. 건네받은 postId로 Post를 찾아옵니다.
  2. 찾아온 post와 페이징 정보를 담은 pageable을 매개변수로 댓글들을 찾아옵니다.
  3. 반아온 댓글 목록을 스트림과 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);
    }
}
  1. postId와 댓글 id를 받아옵니다.
  2. 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);
    }
  1. postId로 Post 정보, userName으로 User 정도, id로 Comment 정보를 받아옵니다.
  2. validateMathUser메서드를 사용하여 해당 api를 요청한 User와 Comment를 작성한 User가 일치하는지 확인합니다.
  3. 코멘스를 삭제하고, 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
코드플리