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 학원 강의 ]

+ Recent posts