1. 컨트롤러 계층 구현
컨트롤러 영역에서는 Swagger UI를 이용해서 테스트와 함께 필요한 기능을 개발
ReplyController는 ReplyService를 주입 받도록 설계
@RestController
@RequestMapping("/api/replies")
@Log4j2
@RequiredArgsConstructor
public class ReplyController {
private final ReplyService replyService;
1) 등록 기능 확인
👩🏻🚀 ReplyController의 등록 기능은 이미 개발된 코드에 JSON 처리를 위해서 추가 코드가 필요
@Operation(summary = "Replies Post", description = "POST 방식으로 댓글 등록")
@PostMapping(value = "/", consumes = MediaType.APPLICATION_JSON_VALUE)
public Map<String, Long> register(@Valid @RequestBody ReplyDTO replyDTO,
BindingResult bindingResult) throws BindException {
log.info(replyDTO);
if (bindingResult.hasErrors()) {
throw new BindException(bindingResult);
}
Map<String, Long> map = new HashMap<>();
Long rno = replyService.register(replyDTO);
map.put("rno", rno);
return map;
}
✓ 프로젝트를 실행하고 Swagger UI를 통해서 테스트를 진행. 등록 작업에서 주의할 점은 bno가 존재하는 게시물 번호여야 함
✓ 정상적으로 동작하면 {"rno": 6}과 같은 결과가 전송되는 것을 확인
에러에 대한 처리
📍 @Valid는 이미 처리를 했지만 연관 관계를 가진 엔티티를 처리할 때마다 항상 문제가 되는 것은 연관된 객체의 안전성을 확보하는 것
➡️ 예를 들어 앞선 테스트에서 bno 값을 사용할 수 없는 번호로 작성하면 다음과 같은 문제가 발생
[(conn=1337) Cannot add or update a child row: a foreign key constraint fails (`boot_ex_app_01_2405`.`reply`, CONSTRAINT `FKr1bmblqir7dalmh47ngwo7mcs` FOREIGN KEY (`board_bno`) REFERENCES `board` (`bno`))]
⚡️ 서버에 기록된 로그를 보면 SQL Exception이긴 하지만, org.springframework.dao.DataIntegrityViolationException 예외가 발생. 예외가 발생한다는 것은 분명 정상적인 결과이지만 서버의 상태 코드는 500으로 '서버 내부의 오류'로 처리
⚡️ 외부에서 Ajax로 댓글 등록 기능을 호출했을 때 500 에러가 발생한다면 호출한 측에서는 현재 서버의 문제라고 생각할 것
➡️ 클라이언트에 서버의 문제가 아니라 데이터의 문제가 있다고 전송하기 위해서는 @RestControllerAdvice를 이용하는 CustomRestAdvice에 DataIntegrityViolationException를 만들어서 사용자에게 예외 메시지를 전송하도록 구성
@ExceptionHandler(DataIntegrityViolationException.class)
@ResponseStatus(HttpStatus.EXPECTATION_FAILED)
public ResponseEntity<Map<String, String>> handleFKException(Exception ex) {
log.error(ex);
Map<String, String> errorsMap = new HashMap<>();
errorsMap.put("time", "" + System.currentTimeMillis());
errorsMap.put("msg", "constraint fails");
return ResponseEntity.badRequest().body(errorsMap);
}
✓ 추가한 handlerFKException()는 DataIntegrityViolationException이 발생하면 "constraint fails" 메시지를 클라이언트로 전송
2) 특정 게시물의 댓글 목록
👩🏻🚀 특정한 게시물의 댓글 목록 처리는 '/api/replies/list/{bno}' 경로를 이용하도록 구성. 이때 bno는 게시물의 번호를 의미.
👩🏻🚀 스프링에서는 @PathVariable이라는 어노테이션을 이용해서 호출하는 경로의 값을 직접 파라미터의 변수로 처리할 수 있는 방법을 제공
ReplyController에 메서드 추가
@Operation(summary = "Replies of Board", description = "GET 방식으로 특정 게시물의 댓글 목록")
@GetMapping(value = "/list/{bno}")
public PageResponseDTO<ReplyDTO> getList(@PathVariable("bno") Long bno, PageRequestDTO pageRequestDTO) {
PageResponseDTO<ReplyDTO> responseDTO = replyService.getListOfBoard(bno, pageRequestDTO);
return responseDTO;
}
✓ getList()에서 bno 값은 경로에 있는 값을 취해서 사용할 것이므로 @PathVariable을 이용하고, 페이지와 관련된 정보는 일반 쿼리 스트링을 이용
✓ Swagger UI로 ReplyController를 호출해 보면 PageResponseDTO가 JSON으로 처리된 결과를 볼 수 있음
3) 특정 댓글 조회
👩🏻🚀 특정한 댓글을 조회할 때는 Reply의 rno를 경로로 이용해서 GET 방식으로 처리
ReplyController에 getReplyDTO() 메서드를 추가
@Operation(summary = "Read Reply", description = "GET 방식으로 특정 댓글 조회")
@GetMapping(value = "/{rno}")
public ReplyDTO getReplyDTO(@PathVariable("rno") Long rno) {
ReplyDTO replyDTO = replyService.read(rno);
return replyDTO;
}
✓ 정상적인 rno 값이 전달되면 ReplyDTO가 JSON으로 처리
데이터가 존재하지 않는 경우의 처리
📍 getReplyDTO()와 같이 특정한 번호를 이용해서 조회할 때 문제가 되는 부분은 해당 데이터가 존재하지 않는 경우
📍 'NoSuchElementException' 예외 전송을 위해 CustomRestAdvice에 기능을 추가
@ExceptionHandler(NoSuchElementException.class)
@ResponseStatus(HttpStatus.EXPECTATION_FAILED)
public ResponseEntity<Map<String, String>> handleNoSuchElementException(Exception ex) {
log.error(ex);
Map<String, String> errorsMap = new HashMap<>();
errorsMap.put("time", "" + System.currentTimeMillis());
errorsMap.put("msg", "No Such Element Exception");
return ResponseEntity.badRequest().body(errorsMap);
}
4) 특정 댓글 삭제
👩🏻🚀 일반적으로 REST 방식에서 삭제 작업은 GET / POST 가 아닌 DELETE 방식을 이용해서 처리
ReplyController에 remove() 메서드를 추가
@Operation(summary = "Delete Reply", description = "DELETE 방식으로 특정 댓글 삭제")
@DeleteMapping(value = "/{rno}")
public Map<String, Long> remove(@PathVariable("rno") Long rno) {
replyService.remove(rno);
Map<String, Long> map = new HashMap<>();
map.put("rno", rno);
return map;
}
존재하지 않는 번호의 삭제 예외
@ExceptionHandler({NoSuchElementException.class, EmptyResultDataAccessException.class})
@ResponseStatus(HttpStatus.EXPECTATION_FAILED)
public ResponseEntity<Map<String, String>> handlerNoSuchElementException(Exception e) {
log.error(e);
Map<String, String> errorMap = new HashMap<>();
errorMap.put("time", "" + System.currentTimeMillis());
errorMap.put("msg", "No Such Element Exception");
return ResponseEntity.badRequest().body(errorMap);
}
5) 특정 댓글 수정
👩🏻🚀 댓글 수정은 PUT방식으로 처리
ReplyController에 modify() 메서드를 추가
📍 주의할 점은 수정할 때도 등록과 마찬가지로 JSON 문자열이 전송되므로 이를 처리하도록 @RequestBody를 적용한다는 점
@Operation(summary = "Modify Reply", description = "PUT 방식으로 특정 댓글 수정")
@PutMapping(value = "/{rno}", consumes = MediaType.APPLICATION_JSON_VALUE)
public Map<String, Long> modify(@PathVariable("rno") Long rno, @RequestBody ReplyDTO replyDTO) {
replyDTO.setRno(rno);
replyService.modify(replyDTO);
Map<String, Long> map = new HashMap<>();
map.put("rno", rno);
return map;
}
[ 내용 참고 : IT 학원 강의 ]
'Spring & Spring Boot' 카테고리의 다른 글
[Spring Boot] 댓글 등록 · 수정 · 삭제 (0) | 2024.06.07 |
---|---|
[Spring Boot] 비동기처리와 Axios, 댓글 목록 처리 (1) | 2024.06.03 |
[Spring Boot] 댓글 서비스 계층의 구현 (0) | 2024.06.01 |
[Spring Boot] 다대일ManyToOne 연관관계, 댓글 수 반영 화면 구현 (0) | 2024.05.31 |
[Spring Boot] REST 방식의 댓글 처리 준비 (0) | 2024.05.30 |