본문 바로가기

⭐ SpringBoot/𝄜 게시판 with SpringBoot

22. 댓글 CRUD를 위한 Controller와 Service를 생성

728x90
반응형

# Commnet 컨트롤러와 서비스를 만들어, 댓글 REST API를 완성 한다.

 

해당 강의는 다소 복잡 하므로 23.댓글 서비스와 컨트롤러 강의를 참조하여 다시 재정리 해야함.


요약

댓글 CRUD 기능을 REST API로 구현을 하였다. 이를 위해서 RestController를 생성 하였고, 서비스와 협업 할수 있도록 CommentService를 생성하여 구현을 하였다. 이때 서비스는 2개의 레파지토리인 CommentRepository와 ArticleRepository와 협업을 하였다. 추가로 Controller에서 JSON을 받기위해 CommentDto를 생성 하였고, CommentDto안에는 JSON에서 던져진 키값을 @JsonProperty 어노테이션을 통해서 지정할 수 있었다.

추가로 Comment 엔티티에서 2개의 메소드를 생성 하였다. 첫번째는 dto와 article을 입력받아서 Commnet 엔티티를 반환하는 생성 메소드를 클래스 메소드로 생성해 보았고, 추가로 수정을 위해서 인스턴스 메소드인 patch 메소드를 생성 하였다. 그리고 끝으로 CommentService에서 스트림 문법을 연습할수 있는 문법도 보았고, 예외를 발생시키는 코드또한 작성을 하였다. 

 

코드작성 진행

api 패키지 폴더에 CommentApiController 라는 컨트롤러 파일을 생성한다.

RestController로 설정 후 CommentService를 Autowired로 연결해 준다.

 

CommentService가 없으므로 CommentService 파일을 service 패키지 폴더에 생성해 준다.

CommentService 파일에 @Service를 선언하고, @Autowired로 CommentRepository를 연결해 준다.

private ArticleRepository articleRepository; → CommentRepository 데이터도 사용해야 하지만 articleRepository데이터도 사용해야 하므로 Autowired를 통해 사용할 수 있도록 해야 한다.

 

나머지는 코드의 흐름으로 파악을 하고, 이해가 되지 않거나 막히면 강의를 참조하는것으로 진행...!!

 

CommentService의 전체 소스 코드

@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
    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(id)
                .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);
    }
}

CommentApiController의 관련내용 정리

// 서비스에게 위임 : commentService.comments(articleId); → 뭐를 리턴한다? List<CommentDto>: comment를 dto로 바꾼것 그것에 묶음을 반환한다. 그리고 이 값을 dtos 라고 변수에 담아 둔다.

CommentApiController의 전체 소스 코드

@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 updatedDto = commentService.delete(id);
        // 결과 응답
        return ResponseEntity.status(HttpStatus.OK).body(updatedDto);
    }
}

CommnetDto의 전체 소스코드

 public static CommentDto... → static은 class 메소드를 선언할때 사용한다.

@AllArgsConstructor
@NoArgsConstructor
@Getter
@ToString
public class CommentDto {
    private Long id;

    @JsonProperty("article_id")
    private Long articleId;
    private String nickname;
    private String body;

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

Comment 전체 소스 코드

@Entity
@Getter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Comment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne // 해당 댓글 엔티티 여러개가, 하나의 Article에 연관된다.!
    @JoinColumn(name = "article_id")
    private Article article;

    @Column
    private String nickname;

    @Column
    private String body;

    public static Comment createComment(CommentDto dto, Article article) {
        // 예외발생
        if (dto.getId() != null)
            throw new IllegalArgumentException("댓글 생성 실패! 댓글의 id가 없어야 합니다.");
        if (dto.getArticleId() != article.getId())
            throw new IllegalArgumentException("댓글 생성 실패! 게시글의 id가 잘못되었습니다.");

        // 예외가 발생하지 않았다면, 엔티티 생성 및 반환
        return new Comment( // 댓글의 내용을 만들어서 반환
                dto.getId(),
                article,
                dto.getNickname(),
                dto.getBody()
        );
    }

    public void patch(CommentDto dto) {
        // 예외 발생
        if (this.id != dto.getId()) // URL에서 던진 1번 id와 Json으로 던진 id가 다른경우 예외가 발생 되어야 한다.
            throw new IllegalArgumentException("댓글 수정 실패! 잘못된 id가 입력되었습니다.");

        // 예외가 발생하지 않았다면, 객체를 갱신
        if (dto.getNickname() != null)
            this.nickname = dto.getNickname();

        if (dto.getBody() != null)
            this.body = dto.getBody();
    }
}
728x90
반응형