이전 파트에서 댓글 엔티티와 댓글 리파지터리를 만들어두었으니 오늘은 댓글 api컨트롤러와 서비스를 만들어서 rest api를 구현해보자!
@RestController 어노테이션 선언, @Autowired 로 CommentService 객체 주입@RestController
public class CommentApiController {
@Autowired
private CommentService commentService;
@Service 어노테이션 선언, @Autowired로 CommentRepository, ArticleRepository 객체 주입@Service
@Slf4j
public class CommentService {
@Autowired
private CommentRepository commentRepository;
@Autowired
private ArticleRepository articleRepository;
@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로 변환해서 반환해야 함.
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;
commentRepository의 findByArticleId 메서드를 사용해서 url로 받아온 articleId와 comment엔티티의 article_id와 같은 데이터들을 list로 가져옴. public static CommentDto createCommentDto(Comment comment) {
return new CommentDto(
comment.getId(),
comment.getArticle().getId(),
comment.getNickname(),
comment.getBody()
);
}
@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);
}
@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);
}
.orElseThrow(()-> new IllegalArgumentException("댓글 생성 실패! 대상 게시글이 없습니다.") 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()
);
}
@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로 받아오기 @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);
}
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();
}
this.id = target.id (Service의 update 메소드에서 target.patch(dto)으로 호출했기 때문) @DeleteMapping("/api/comments/{id}")
public ResponseEntity<CommentDto> delete(@PathVariable Long id) {
CommentDto deletedDto = commentService.delete(id);
return ResponseEntity.status(HttpStatus.OK).body(deletedDto);
}
@Transactional
public CommentDto delete(Long id) {
Comment target = commentRepository.findById(id)
.orElseThrow(()->new IllegalArgumentException("댓글 삭제 실패! 대상이 없습니다!"));
commentRepository.delete(target);
return CommentDto.createCommentDto(target);
}