Controller - @RestControllerAdvice

jihan kong·2022년 10월 4일
0

JUnit5

목록 보기
22/25
post-thumbnail

본 시리즈는 메타 코딩님의 Junit 강의를 학습한 내용을 바탕으로 정리하였습니다.

지난 포스팅에서 책을 등록할 때 수행되어야하는 유효성 검사에 대한 부분을 구현했다. 그러나 실제 앱을 실행해서 여러가지를 테스트 해보면 아직 미흡한 부분들이 눈에 띈다. 요청 Body의 값에 따라 Controller 단에서 잡아내지 못하는 오류가 바로 그 것인데 어떤 것들이 있는지 postman을 통해 한 번 살펴보자.


1. 오류를 잡아내는 경우 😄

먼저 유효성 검사를 통해 요청 Body 값이 잘못되었다고 알려주는 경우가 있다.

위와 같이 titleauthor 가 공백일 경우는 의도한 대로 msg를 전달해주고, 상태코드 또한 우리가 핸들링할 수 있는 400 BAD request이다.


2. 오류를 잡아내지 못하는 경우 😥

key 값인 author를 아예 포함하지 않고 전송을 하면 어떻게 될까?

위와 같이 500 에러와 우리가 의도하지 않은 즉, 유효성 처리를 하지않은 trace 에러가 발생한다.

위의 경우처럼 value 값에 긴 문자를 전송해도 오류를 필터링 하지 못한다.
그렇다면... 이를 해결하기 위해선 어떻게 해야할까?

예외처리

요청에 따라 return되는 Http 상태코드에 위에 해당하는 경우들의 exception 처리를 모두 해주어야 한다.

이를 위해 web 하위 디렉토리에 다음과 같이 handler 폴더와 GlobalExceptionHandler.java 를 생성한다.

GlobalExceptionHandler.java

package site.metacoding.junitproject.web.handler;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import site.metacoding.junitproject.web.dto.response.CMRespDto;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<?> apiException(RuntimeException e) {

        return new ResponseEntity<>(CMRespDto.builder().code(-1).msg(e.getMessage()).build(), HttpStatus.BAD_REQUEST);
    }
    
}

@RestControllerAdvice

@ControllerAdvice@ResponseBody 를 합쳐놓은 어노테이션. 단순히 예외만 처리하고 싶다면 @ControllerAdvice 를 적용하고 응답으로 객체를 리턴해야 한다면 @RestControllerAdvice 를 적용한다.

@ExceptionHandler

예외처리를 위해 사용되는 어노테이션. 메서드에 선언하고 특정 예외 클래스를 지정해주면 해당 예외가 발생했을 때, 메서드에 정의한 로직으로 처리할 수 있다. 단, Controller , RestController 에만 적용 가능하고 @Service 같은 Bean에서는 적용되지 않는다.

(출처 : https://velog.io/@banjjoknim/RestControllerAdvice)

우리는 GlobalExceptionHandler 를 통해 Controller 의 전역적인 예외처리를 할 수 있게 되었다. 이제 postman을 통해 다시 한번 body를 요청해보자.

위처럼 key 값이 없는 경우는 미리 설계한 예외처리에 의해 에러코드와 메세지를 보여준다. body에 null 값이 들어왔다는 것 또한 요청 값이 DB에 저장이 안되었다는 뜻이기 때문에 예외처리가 잘 되고 있음을 의미한다.

이제, author 의 value 를 공백으로 해서 다시 전송해보자.

이번에는 조금 이상하다. 요청 메세지는 2에서 20 사이어야 한다고 잘 뜬다. 그런데 body에 내용이 들어와있다. 저장이 된다는 뜻이다. 저장이 되지 않도록 수정해야한다. BookApiController 를 다음과 같이 수정한다.


BookApiController 수정

BookApiController.java

    // 1. 책등록
    // key=value&key=value
    // { "key": value, "key": value }
    @PostMapping("/api/v1/book")
    public ResponseEntity<?> saveBook(@RequestBody @Valid BookSaveReqDto bookSaveReqDto, BindingResult bindingResult) {
        
        if (bindingResult.hasErrors()) {
            Map<String, String> errorMap = new HashMap<>();
            for (FieldError fe : bindingResult.getFieldErrors()) {
                errorMap.put(fe.getField(), fe.getDefaultMessage());
            }
            System.out.println("=============================");
            System.out.println(errorMap.toString());
            System.out.println("=============================");

            throw new RuntimeException(errorMap.toString());
        }

        BookRespDto bookRespDto = bookService.책등록하기(bookSaveReqDto);
        return new ResponseEntity<>(CMRespDto.builder().code(1).msg("글 저장 성공").body(bookRespDto).build(),
                HttpStatus.CREATED); // 201 = insert
    }

if 문에 아예 throw 문을 포함시켜 오류가 발생하면 RuntimeException을 작동하게끔 한다. 이제 파싱과정 중에서 오류가 발생하든 바인딩과정에서 오류가 발생하든지 간에 RuntimeException이 모두 처리해줄 수 있다.

이제 마지막으로 다시 테스트해보자.


author 의 value 값이 공백("")일 때 오류를 잡아내고 있다.

마찬가지로 author 의 value 값의 크기가 20이상일 때도 오류를 잡아내고 있다.

key값이 아예 null 일때도 오류를 잘 캐치해내고 있는 모습이다.
세 경우 모두 400 BAD Request 요청을 통해 클라이언트에서 요청 자체가 잘못되었다고 알려주고 있다.

profile
학습하며 도전하는 것을 즐기는 개발자

0개의 댓글