본문 바로가기
웹개발/SpringBoot

댓글 REST API 완성하기

by 철없는민물장어 2023. 3. 13.
728x90
반응형

Comment의 controller와 service를 만들어 REST API를 완성해보자.

 

@RestController
public class CommentApiController {

    @Autowired
    private CommentService commentService;

    //댓글 목록 조회
    @GetMapping("/api/articles/{articleId}/comments")
    public ResponseEntity<List<CommentDto>> comments(@PathVariable Long articleId){
        List<CommentDto> dtos = commentService.comments(articleId);
        return ResponseEntity.status(HttpStatus.OK).body(dtos);
    }

    //댓글 생성
    @PostMapping("/api/articles/{articleId}/comments")
    public ResponseEntity<CommentDto> create(@PathVariable Long articleId, @RequestBody CommentDto dto){

        CommentDto createdDto = commentService.create(articleId,dto);

        return ResponseEntity.status(HttpStatus.OK).body(createdDto);
    }

    //댓글 수정
    @PatchMapping("/api/comments/{id}")
    public ResponseEntity<CommentDto> update(@PathVariable Long id, @RequestBody CommentDto dto){

        CommentDto updatedDto = commentService.update(id,dto);

        return ResponseEntity.status(HttpStatus.OK).body(updatedDto);
    }
    //댓글 삭제
    @DeleteMapping("/api/comments/{id}")
    public ResponseEntity<CommentDto> delete(@PathVariable Long id){

        CommentDto deletedDto = commentService.delete(id);

        return ResponseEntity.status(HttpStatus.OK).body(deletedDto);
    }
}

CommentController이다.

 

컨트롤러의 메소드들은 모두 ResponseEntity타입으로 하고, 리턴은 정상응답만 한다.

(오류가 발생하는 경우에는 예외를 발생시키도록 했다)

 

컨트롤러는 dto만을 다룬다.(ResponseEntity의 타입도 dto로 했다)

 

@Service
public class CommentService {

    @Autowired
    private CommentRepository commentRepository;

    @Autowired
    private ArticleRepository articleRepository; //아티클도 사용해야하므로

    public List<CommentDto> comments(Long articleId) {
        //댓글목록 조회
//        List<Comment> comments = commentRepository.findByArticleId(articleId);

        //엔티티->DTO로 변환
//        List<CommentDto> dtos = new ArrayList<CommentDto>();
//        for(int i=0;i<comments.size();i++){
//            Comment c = comments.get(i);
//            CommentDto dto = CommentDto.createCommentDto(c);
//            dtos.add(dto);
//        }

        return commentRepository.findByArticleId(articleId)
                .stream()
                .map(comment -> CommentDto.createCommentDto(comment))
                .collect(Collectors.toList());
        //반환
    }

    @Transactional //DB를 건들기때문에, 중간에 문제가 생기면 롤백되도록 트랜잭션으로 설정
    public CommentDto create(Long articleId, CommentDto dto) {
        //게시글조회 및 예외발생
        Article article = articleRepository.findById(articleId)
                .orElseThrow(()-> new IllegalArgumentException("댓글 생성 실패: 대상 게시글이 없습니다"));
        //댓글 엔티티 생성
        Comment comment = Comment.createComment(dto,article);
        //댓글 엔티티를 DB로 저장

        Comment created = commentRepository.save(comment);

        //DTO로 변환하여 저장
        return CommentDto.createCommentDto(created);
    }

    @Transactional
    public CommentDto update(Long id, CommentDto dto) {
        //댓글 조회 및 예외발생
        Comment target = commentRepository.findById(dto.getId())
                .orElseThrow(()->new IllegalArgumentException("댓글 수정 실패: 대상 댓글이 없습니다"));
        //댓글 수정
        target.patch(dto);
        //댓글 엔티티를 DB로 저장
        Comment updated = commentRepository.save(target);
        //DTO로 변환하여 반환
        return CommentDto.createCommentDto(updated);
    }

    @Transactional
    public CommentDto delete(Long id) {

        //댓글 조회(및 예외발생)
        Comment target = commentRepository.findById(id)
                .orElseThrow(()-> new IllegalArgumentException("댓글 삭제 대상이 없습니다"));

        //댓글 DB에서 삭제
        commentRepository.delete(target);

        //삭제 댓글을 Dto로 반환
        return CommentDto.createCommentDto(target);
    }
}

Service 클래스이다.

 

컨트롤러에게 dto를 전달해야하기 때문에 모든 메소드의 리턴타입이 dto, 혹은 dto리스트로 되어있다.

또한, db를 건드는 메소드의 경우에는 중간에 에러 발생시 롤백되도록 @Transactional 어노테이션을 붙여 트랜잭션으로 설정해주었다.

 

데이터를 조회하는 경우, DB에서 데이터를 조회했을 때 데이터가 존재하지 않는경우 예외를 발생시켰다.

 

create메소드에서는 repository를 이용해 db에 값을 넣어야하는데,

파라미터로 받은 dto상태로는 db에 넣지 못해 entity로 변환하여야 하는데

이를위해 Comment(entity) 클래스의 createComment 메소드를 만들어 사용했다

    public static Comment createComment(CommentDto dto, Article article){
        //예외처리
        if(dto.getId() != null)
            throw new IllegalArgumentException("댓글의 id가 없어야합니다");
        if(dto.getArticleId() != article.getId())//JSON과 URL의 아티클이 다른경우
            throw new IllegalArgumentException("게시글의 id가 잘못되었습니다");

        return new Comment(
                dto.getId(),
                article,
                dto.getNickname(),
                dto.getBody()
        );
    }

이 때, dto에 id가 존재하면 db가 id를 자동생성하는것과 충돌이 생길 수 있으므로 예외를 발생시키고

dto와 url에서 나타내는 article_id가 서로 같지 않을 때도 예외를 발생시킨다.

 

 

 

update 메소드에서는 Comment엔티티의 patch메소드를 추가해서 사용했는데,

patch에서는 수정할 댓글과 원 댓글의 id가 일치하지 않으면 오류를 발생시키는 등의 동작을 한다.

    public void patch(CommentDto dto) {
        //예외발생
        if(this.id != dto.getId())
            throw new IllegalArgumentException("댓글 수정 실패. 잘못된 id가 입력됨");

        //객체를 갱신
        if(dto.getNickname()!=null)
            this.nickname=dto.getNickname();
        if(dto.getBody()!=null)
            this.body=dto.getBody();
    }

 

 

그리고, Repository가 반환하는 타입들은 entity이기 때문에

리턴시 dto로 만들어주기 위해서, Comment entity 클래스에 createDto 메소드를 추가해 사용했다

    public static CommentDto createCommentDto(Comment c) {
        return new CommentDto(
                c.getId(),
                c.getArticle().getId(),
                c.getNickname(),
                c.getBody()
        );
    }

 

728x90
반응형

댓글