[Spring Boot] Day15 - 댓글 Rest API 구현

Sarah·2025년 12월 17일

Spring Boot

목록 보기
14/17

오늘의 목표

이전 파트에서 댓글 엔티티와 댓글 리파지터리를 만들어두었으니 오늘은 댓글 api컨트롤러와 서비스를 만들어서 rest api를 구현해보자!


컨트롤러 / 서비스 생성

ApiController

  • api 디렉터리에 'CommentApiController'이름으로 클래스 생성
  • @RestController 어노테이션 선언, @Autowired 로 CommentService 객체 주입
@RestController
public class CommentApiController {
    @Autowired
   private CommentService commentService;

Service

  • service 디렉터리에 'CommentService'이름으로 클래스 생성
  • @Service 어노테이션 선언, @Autowired로 CommentRepository, ArticleRepository 객체 주입
@Service
@Slf4j
public class CommentService {
    @Autowired
    private CommentRepository commentRepository;
    @Autowired
    private ArticleRepository articleRepository;

댓글 조회 (Read)

ApiController

    @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);
    }
  • 반환형을 ResponseEntity<List<CommentDto>>로 하기.

    ResponseEntity

    • 댓글 생성 결과를 응답코드와 함께 보내기 위해서.

    엔티티인 Comment가 아니라 Dto인 CommentDto를 반환형으로 하는 이유?

    • 엔티티 그대로 반환하면 comment-article 연관관계, 무한루프 JSON깨짐, 보안문제 때문에 '필요한 정보만' 가져오는 출력 DTO를 사용해야 한다.
    • 즉, DB에서 조회한 댓글 엔티티 목록을 DTO로 변환해서 반환해야 함.

Service

    public List<CommentDto> comments(Long articleId) {
        List<Comment> comments = commentRepository.findByArticleId(articleId);
        List<CommentDto> dtos = new ArrayList<>();
        for(int i=0 ; i<comments.size() ; i++) {
            Comment c = comments.get(i);
            CommentDto dto = CommentDto.createCommentDto(c);
            dtos.add(dto);
        }
        return dtos;
  • commentRepositoryfindByArticleId 메서드를 사용해서 url로 받아온 articleId와 comment엔티티의 article_id와 같은 데이터들을 list로 가져옴.
  • CommentDto (dto) 타입의 새로운 ArrayList 생성
  • for문을 사용하여 comment 목록에서 하나씩 꺼내기
  • CommentDto의 createCommentDto 메서드를 사용하여 dto로 변환하고 dtos 목록에 넣기.

CommentDto

    public static CommentDto createCommentDto(Comment comment) {
        return new CommentDto(
                comment.getId(),
                comment.getArticle().getId(),
                comment.getNickname(),
                comment.getBody()
        );
    }
  • 엔티티를 dto로 변환하는 CommentDto의 메서드는 위와 같다.
  • 엔티티를 매개변수로 받아서 새로운 new CommentDto (새로운 dto 객체) 만들어서 반환

댓글 생성 (Create)

ApiController

    @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);
    }
  • 매개변수로 url의 articleId와 http요청의 body를 담은 dto로 받기.

Service

    @Transactional
    public CommentDto create(Long articleId, CommentDto dto) {
        Article article = articleRepository.findById(articleId)
                .orElseThrow(()-> new IllegalArgumentException("댓글 생성 실패! 대상 게시글이 없습니다."));
        Comment comment = Comment.create(dto, article);
        Comment created = commentRepository.save(comment);
        return CommentDto.createCommentDto(created);
    }
  • url로 받아온 articleId -> articleRepository 사용해서 해당 id의 게시물이 존재하는지 확인하기.
  • 게시물이 존재하지 않다면 예외 던지기 .orElseThrow(()-> new IllegalArgumentException("댓글 생성 실패! 대상 게시글이 없습니다.")
  • 게시물이 존재한다면 -> Comment엔티티의 create 메서드 호출하여 엔티티 생성!!
  • 위에서 생성한 엔티티를 리파지터리 사용하여 저장하고 created에 저장
  • 저장된 created를 dto로 변환하여 반환

Comment Entity

    public static Comment create(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()
        );
    }
  • 게시물이 존재한다는 전제하에 받아온 commentdto, article
  • http 요청의 body에 (dto) id가 적혀있다면 예외 던지기
  • http 요청의 body에 (dto) articleId가 url을 통해 받아온 articleId 와 다르다면 예외 던지기
  • 둘 다 만족한다면 new Comment (새로운 엔티티) 생성하여 반환

댓글 수정 (Update)

ApiController

    @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);
    }
  • @PostMapping으로 수정할 댓글의 id를 url로 받아오기

Service

    @Transactional
    public CommentDto update(Long id, CommentDto dto) {
        Comment target = commentRepository.findById(id)
                .orElseThrow(()-> new IllegalArgumentException("댓글 수정 실패! 대상 댓글이 없습니다."));
        target.patch(dto);
        Comment updated = commentRepository.save(target);
        return CommentDto.createCommentDto(updated);
    }
  • commentrepository로 url에서 받아온 id의 댓글이 존재하는지 확인
  • 해당 id의 댓글이 없다면 예외 던지기
  • 존재한다면 target 객체에 저장하고 Comment 엔티티의 patch 메소드 호출
  • 업데이트 된 target을 리파지터리 사용하여 저장하고, 이를 updated 객체에 다시 저장.
  • updated를 dto로 변환하여 반환

Comment Entity

    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();
    }
  • 엔티티의 데이터를 수정하는 patch 메소드.
  • 매개변수로 CommentDto 받아오기 (Http 요청의 body를 담은 dto)
  • 여기서 this.id = target.id (Service의 update 메소드에서 target.patch(dto)으로 호출했기 때문)
  • target의 id (url로 받아온 id)와 dto의 id가 다르다면 예외 던지기
  • dto의 nickname/body에 내용이 있다면 그 내용으로 바꾸기.

댓글 삭제 (Delete)

ApiController

    @DeleteMapping("/api/comments/{id}")
    public ResponseEntity<CommentDto> delete(@PathVariable Long id) {
        CommentDto deletedDto = commentService.delete(id);
        return ResponseEntity.status(HttpStatus.OK).body(deletedDto);
    }

Service

    @Transactional
    public CommentDto delete(Long id) {
        Comment target = commentRepository.findById(id)
                .orElseThrow(()->new IllegalArgumentException("댓글 삭제 실패! 대상이 없습니다!"));
        commentRepository.delete(target);
        return CommentDto.createCommentDto(target);
    }
  • 리파지터리를 사용하여 url로 받아온 id에 해당하는 댓글이 있는지 확인
  • 없다면 예외 던지기
  • 있다면 target 객체에 저장하여 리파지터리 사용하여 delete (삭제)
  • 삭제 대상 객체인 target을 dto로 변환하여 반환
profile
헤맨 만큼 내 땅

0개의 댓글