15장 댓글 컨트롤러와 서비스 만들기
앞의 14장에서 엔티티와 리파지터리를 생성했다. 이번에는 댓글 CRUD를 위한 REST API를 완성할 것이다.
API 주소 설계는 다음과 같다.
GET | /articles/articleId/comments |
POST | /articles/articleId/comments |
PATCH | /comments/id |
DELETE | /comments/id |
컨트롤러&서비스 틀 만들기
댓글 컨트롤러와 서비스 틀을 만들것이다.
REST 컨트롤러는 api 패키지 안에 생성.
CommentApiController.java
@RestController
public class CommentApiController {
@Autowired
private CommentService commentService; // 댓글 서비스 객체 주입
// 1. 댓글 조회
// 2. 댓글 생성
// 3. 댓글 수정
// 4. 댓글 삭제
}
service 패키지에는 CommnetService 서비스를 생성했다
CommentService.java
@Service
public class CommentService {
@Autowired
private CommentRepository commentRepository; // 댓글 리파지터리 객체 주입
@Autowired
private ArticleRepository articleRepository; // 게시글 리파지터리 객체 주입
}
댓글 조회하기
댓글 조회 REST API 주소는 다음과 같다.
/articles/articleId/comments
CommentApiController.java
@RestController
public class CommentApiController {
@Autowired
private CommentService commentService; // 댓글 서비스 객체 주입
// 1. 댓글 조회
@GetMapping("/api/articles/{articleId}/comments") // 요청 url 작성
public ResponseEntity<List<CommentDto>> comments(@PathVariable Long articleId) {
// 서비스에 위임
List<CommentDto> dtos = commentService.comments(articleId);
// 결과 응답
return ResponseEntity.status(HttpStatus.OK).body(dtos);
}
// 2. 댓글 생성
// 3. 댓글 수정
// 4. 댓글 삭제
}
반환형이 ResponseEntity<List<CommentDto>> 인 이유는
DB에서 조회한 댓글 엔티티를 DTO로 변환하면 List<CommentDto>가 되기 때문이다.
+ 응답 코드를 같이 보내려면 ResponseEntity클래스를 활용해야함
[ CommentService.java ]
@Service
public class CommentService {
@Autowired
private CommentRepository commentRepository; // 댓글 리파지터리 객체 주입
@Autowired
private ArticleRepository articleRepository; // 게시글 리파지터리 객체 주입
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;
}
}
CommentDto.java
@AllArgsConstructor
@NoArgsConstructor
@Getter
@ToString
public class CommentDto {
private Long id; // 댓글의 id
private Long articleId; // 댓글의 부모 id
private String nickname; // 댓글 작성자
private String body; // 댓글 본문
public static CommentDto createCommentDto(Comment comment) {
return new CommentDto(
comment.getId(),
comment.getArticle().getId(),
comment.getNickname(),
comment.getBody()
);
}
}
4번 게시글의 댓글이 잘 반환되는 것을 확인할 수 있다.
1분 퀴즈)
• ( ㉠ ): 리파지터리가 DB 속 데이터를 조회하거나 전달할 때 사용하는 객체
• ( ㉡ ): 단순 데이터 전송만을 목적으로 하는 객체, 클라이언트와 서버 사이에서 사용됨
• ( ㉢ ): 컨트롤러와 리파지터리의 사이에서 비즈니스 로직, 즉 처리 흐름을 담당하는 객체
• ( ㉣ ): 클라이언트의 요청을 받고 응답하는 객체로, 뷰(view)가 아닌 데이터를 반환
ㄱ: 엔티티
ㄴ: DTO
ㄷ: 서비스
ㄹ: 컨트롤러
댓글 생성하기
댓글 생성 REST API 주소는 다음과 같다.
/articles/articleId/comments
CommentApiController.java
// 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);
}
CommentService.java
@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);
}
Entity 패키지의 Comment.java
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()
);
}
1분 퀴즈) 다음은 댓글 생성을 위한 REST API 요청입니다. 이와 관련한 설명으로 옳지 않은 것을 고르세요.
① HTTP를 통해 POST 요청을 보내고 있다.
② 댓글의 정보를 JSON 형식으로 전송하고 있다.
③ 댓글의 정보를 컨트롤러에서 @RequestBody로 받을 수 있다.
④ 해당 요청을 통해 5번 게시글에 댓글이 작성된다.
답 : 4번 ( url은 4번 게시글에 댓글을 작성하는 것인데, body에는 5번 게시글로 접근해서.. 에러 발생 할 것.)
댓글 수정하기
댓글 수정 REST API 주소는 다음과 같다.
/comments/id
CommentApiController.java
// 3. 댓글 수정
@PostMapping("/api/comments/{id}")
public ResponseEntity<CommentDto> update(@PathVariable Long id, @RequestBody CommentDto dto) {
// 서비스에 위임
CommentDto updateDto = commentService.update(id, dto);
// 결과에 응답
return ResponseEntity.status(HttpStatus.OK).body(updateDto);
}
CommentService.java
@Transactional
public CommentDto update(Long id, CommentDto dto) {
// 1. 댓글 조회 및 예외 발생
Comment target = commentRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("댓글 수정 실패!" +
"대상 댓글이 없습니다."));
// 2. 댓글 수정
target.patch(dto);
// 3. DB로 갱신
Comment updated = commentRepository.save(target);
// 4. 댓글 엔티티를 DTO로 변환 및 반환
return CommentDto.createCommentDto(updated);
}
Entity 패키지의 Comment.java
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 지원 안되어서 post 메서드로 수정함 )
1분 퀴즈) ㉠, ㉡에 들어갈 용어를 쓰세요.
• HTTP 요청 중 수정을 위한 메서드에는 PUT과 ( ㉠ )(이)가 있다.
• 입력값이 잘못된 경우에 ( ㉡ ) 클래스를 사용해 예외 처리를 한다.
ㄱ: PATCH
ㄴ: IllegalArgumentException
댓글 삭제하기
댓글 삭제 REST API 주소는 다음과 같다.
/comments/id
CommentApiController.java
// 4. 댓글 삭제
@DeleteMapping("/api/comments/{id}")
public ResponseEntity<CommentDto> delete(@PathVariable Long id) {
// 서비스에 위임
CommentDto deletedDto = commentService.delete(id);
// 결과 응답
return ResponseEntity.status(HttpStatus.OK).body(deletedDto);
}
CommentService.java
@Transactional
public CommentDto delete(Long id) {
// 1. 댓글 조회 및 예외 발생
Comment target = commentRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("댓글 삭제 실패 ! " +
"대상이 없습니다."));
// 2. 댓글 삭제
commentRepository.delete(target);
// 3. 삭제 댓글을 DTO로 변환 및 반환
return CommentDto.createCommentDto(target);
}
id 1번 댓글 삭제 확인
1분 퀴즈) ㉠~㉢에 들어갈 용어를 쓰세요.
• ( ㉠ ) 어노테이션은 HTTP 삭제 요청을 받아 특정 컨트롤러 메서드를 수행한다.
• ( ㉡ ) 어노테이션은 HTTP 요청 주소에서 특정 값을 매개변수로 가져온다.
• ( ㉢ ) 어노테이션은 예외 발생 시 변경된 데이터를 변경 전으로 롤백한다.
ㄱ: @DeleteMapping
ㄴ: @PathVariable
ㄷ: @Transactional
16장 웹 페이지에서 댓글 목록 보기
서버를 실행시킨 후 http://localhost:8080/articles/5 페이지에 접속하면 다음 화면이 나온다.
위 화면 아래에 댓글을 출력하도록 만들어 볼 예정이다.
ArticleController.java
show.mustache
푸터 바로 위에 _comments.mustache 파일을 연결해주었다.
_comments.mustache
<div>
<!-- 댓글 목록 보기 -->
{{>comments/_list}}
<!-- 새 댓글 작성하기 -->
{{>comments/_new}}
</div>
_list.mustache
<div id="comments-list">
{{#commentDtos}} <!--댓글 목록 순회-->
<div class="card m" id="comments-{{id}}">
<div class="card-header">
{{nickname}}
</div>
<div class="card-body">
{{body}}
</div>
</div>
{{/commentDtos}}
</div>
_new.mustache
이렇게 뷰를 만들어주면 끝이 아니라. 컨트롤러에서 뷰 페이지에서 사용할 변수를 모델에 등록해줘야한다.
이렇게 하고 다시 서버 재실행 하면...
댓글을 확인할 수 있다.
1분 퀴즈 )다음 설명이 참이면 O, 거짓이면 X를 쓰세요.
㉠ 뷰 페이지에서 사용할 변수는 모델에 등록해야 사용할 수 있다. ------------- ( O )
㉡ 머스테치 문법 {{#data}}…{{/data}}에서 data 값이 없는 경우, 내부 영역은 보이지 않는다. -------------( O )
㉢ 머스테치 문법 {{#data}}…{{/data}}에서 data 값이 있는 경우, 내부 영역은 한 번만 출력된다. -------------( X )
㉣ 부트스트랩은 웹 페이지 개발을 위한 다양한 도구를 제공한다. -------------( O )
'스터디 > back' 카테고리의 다른 글
17장 ~ 19장 (0) | 2024.01.04 |
---|---|
12장 ~ 14장 (0) | 2023.12.21 |
Part 3) REST API와 JSON | HTTP와 REST 컨트롤러 (0) | 2023.12.14 |
Part 2) 게시판 만들기 (3) - 리다이렉트 / update / delete / SQL 쿼리 (1) | 2023.12.07 |
Part 2) 게시판 만들기(2) (1) | 2023.11.23 |