댓글 REST API의 개요
14장에서 댓글 엔티티와 레파지토리를 만들었다면 15장에서는 댓글 REST 컨트롤러, 서비스, DTO를 만들어 볼 것이다.
댓글 REST 컨트롤러에서는 클라이언트 요청에 응답하며 뷰가 아닌 데이터를 반환할 것이고,
서비스에서는 처리 흐름을 담당하며 예외 상황 발생 시 트랜잭션으로 롤백시킬 것이고,
DTO에서는 사용자에게 보여 줄 댓글 정보를 담은 것으로 단순히 클라이언트와 서버 간에 댓글 JSON 데이터를 전송할 것이다.
댓글 CRUD를 하려면 REST API 주소가 필요한데 아래 그림과 같이 여기에선 설계할 것이다.
댓글 컨트롤러와 서비스 틀 만들기
api 패키기에 CommentApiController라는 이름으로 REST 컨트롤러를 생성해 주는데,
서비스와 협업할 수 있도록 댓글 서비스 객체도 주입해 준다.
package com.example.firstproject.api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CommentApiController {
@Autowired
private CommentService commentService; // 댓글 서비스 객체 주입
}
아직 서비스 파일을 생성하지 않아 CommentService에 빨간색 에러 표시가 날 것인데,
에러를 없애기 위해 service 패키지에 서비스 파일을 생성해 주고 초기 설정을 해준다.
package com.example.firstproject.service;
import com.example.firstproject.repository.ArticleRepository;
import com.example.firstproject.repository.CommentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CommentService {
@Autowired
private CommentRepository commentRepository; // 댓글 레퍼지토리 객체 주입
@Autowired
private ArticleRepository articleRepository; // 게시글 레퍼지토리 객체 주입
}
다시 컨트롤러 파일로 돌아와 컨트롤러가 처리할 기능을 주석으로 작성해 두고 하나씩 구현해 볼 것이다.
댓글 조회하기
컨트롤러에 댓글 조회 메서드를 우선 아래와 같이 만들어준다.
@GetMapping("/api/articles/{articleId}/comments") // 댓글 조회 요청 접수
public ResponseEntity<List<CommentDto>> comments(@PathVariable Long articleId) {
return null;
}
DTO를 아직 생성해 주지 않아 빨간색으로 표시될 것인데,
댓글 서비스에 조회 작업을 위임해 얻은 결과를 클라이언트에 응답하는 과정의 코드를 추가하고 DTO도 생성해 준다.
// 1. 댓글 조회
@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);
}
package com.example.firstproject.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@AllArgsConstructor // 모든 필드를 매개변수로 갖는 생상자 자동 생성
@NoArgsConstructor // 매개변수가 아예 없는 기본 생성자 자동 생성
@Getter // 각 필드 값을 조회할 수 있는 getter 메서드 자동 생성
@ToString // 모든 필드를 출력할 수 있는 toString 메서드 자동 생성
public class CommentDto {
private Long id; // 댓글의 id
private Long articleId; // 댓글의 부모 id
private String nickname; // 댓글 작성자
private String body; // 댓글 본문
}
이제 요청을 처리할 서비스를 생성해 주면 컨트롤러에 에러 없이 코드 작성을 완료할 수 있다.
서비스가 DB에서 데이터를 가져올 때 레파지토리에 시킨다고 했으므로 14장에서 만든 findByArticleId(articleId) 메서드를 호출한다.
이렇게 하면 articleId번 게시글의 모든 댓글을 가져오고 이것을 comments 변수에 저장한다.
그다음은 조회한 댓글 엔티티 목록을 DTO 목록으로 변환하기 위해 CommentDto를 저장하는 빈 ArrayList를 만들고,
List<CommentDto> 타입의 변수에 저장한다.
public List<CommentDto> comments(Long articleId) {
// 1. 댓글 조회
List<Comment> comments = commentRepository.findByArticleId(articleId);
// 2. 엔티티 -> 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); // 엔티티를 DTO로 변환
dtos.add(dto); // 변환한 DTO를 dtos 리스트에 삽입
}
// 3. 결과 반환
return dtos;
}
빨간색으로 표시된 createCommentDto 메서드를 자동완성기능의 도움을 받아 만들어 주도록 한다.
앞에 public static 즉 정적 메서드로 선언돼 있을 것인데 이는 객체 생성 없이 호출 가능한 메서드라는 뜻이라고 한다.
public static CommentDto createCommentDto(Comment comment) {
return new CommentDto(
comment.getId(),
comment.getArticle().getId(),
comment.getNickname(),
comment.getBody()
);
}
댓글 조회 요청이 실제로 잘 처리되는지 확인하기 위해 FirstprojectApplication에서 실행시킨 후,
Talend API Tester로 댓글이 있는 4번 게시글을 조회하면 아래와 같이 잘 나오는 것을 볼 수 있다.
여기서 한발 더 나아가 서비스 코드를 개선해 보자면 서비스 코드의 for문을 스트림 문법으로 개선해 보는 작업을 해 준다.
public List<CommentDto> comments(Long articleId) {
/*
// 1. 댓글 조회
List<Comment> comments = commentRepository.findByArticleId(articleId);
// 2. 엔티티 -> 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); // 엔티티를 DTO로 변환
dtos.add(dto); // 변환한 DTO를 dtos 리스트에 삽입
}
*/
// 3. 결과 반환
return commentRepository.findByArticleId(articleId)
.stream()
.map(comment -> CommentDto.createCommentDto(comment))
.collect(Collectors.toList());
}
댓글 생성하기
댓글 생성을 위해서 조회할 경우와 유사하게 우선 메서드를 컨트롤러에 작성해 주는데 이번에는 PostMapping을 해주어야 한다.
// 2. 댓글 생성
@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);
}
위와 같이 작성을 해 주면 create 메서드가 생성되지 않아 빨간색으로 표시될 것이니 서비스에 메서드 추가해 준다.
코드를 작성 시 주의 점은 create() 메서드는 DB 내용을 바꾸기 때문에 실패 경우를 대비하여 트랜잭션 처리 해주어야 한다.
@Transactional
public CommentDto create(Long articleId, CommentDto dto) {
// 1. 게시글 조회 및 예외 발생
Article article = articleRepository.findById(articleId)
.orElseThrow(() -> new IllegalArgumentException("댓글 생성 실패! 대상 게시글이 없습니다."));
// 2. 댓글 엔티티 생성
Comment comment = Comment.createComment(dto, article);
// 3. 댓글 엔티티를 DB에 저장
Comment created = commentRepository.save(comment);
// 4. DTO로 변환해 반환
return CommentDto.createCommentDto(created);
}
위와 같이 작성하였다면 이제 빨간색으로 표시된 createComment() 엔티티 생성 메서드를 아래처럼 만들어준다.
public static Comment createComment(CommentDto dto, Article article) {
// 예외 발생
if(dto.getId() != null) {
throw new IllegalArgumentException("댓글 생성 실패! 댓글의 id가 없어야 합니다.");
}
if(dto.getArticleId() != null) {
throw new IllegalArgumentException("댓글 생성 실패! 게시글의ㅣ id가 잘못됐습니다.");
}
// 엔티티 생성 및 반환
return new Comment(
dto.getId(),
article,
dto.getNickname(),
dto.getBody()
);
}
이제 정상적으로 처리되는지 테스트를 위해 Talend API Tester로 요청을 보내면 아래와 같이 나오는 것을 볼 수 있다.
예외 상황을 테스트하기 위해 JSON 데이터의 articleId를 5로 수정 후 send 하면 아래와 같이 에러를 볼 수 있다.
댓글 수정하기와 삭제하기도 유사한 과정으로 진행하면 되니 실행 결과만 첨부하도록 하겠다.
'Backend' 카테고리의 다른 글
코딩자율학습단 스프링부트_17장 (0) | 2024.08.26 |
---|---|
코딩자율학습단 스프링부트_16장 (0) | 2024.08.24 |
코딩자율학습단 스프링부트_14장 (0) | 2024.08.23 |
코딩자율학습단 스프링부트_13장 (0) | 2024.08.20 |
코딩자율학습단 스프링부트_12장 (0) | 2024.08.19 |