본문 바로가기

Development/Spring

[Spring] 스프링 부트의 예외 처리 방식

스프링 부트의 예외 처리 방식

  • 웹 서비스 애플리케이션에서는 외부에서 들어오는 요청에 담긴 데이터를 처리하는 경우가 많다.
  • 그 과정에서 예외가 발생하면 예외를 복구해서 정상으로 처리하기보다는 요청을 보낸 클라이언트에 어떤 문제가 발생했는지 전달하는 경우가 많다.

예외가 발생했을 때 클라이언트에 오류 메시지를 전달하려면 각 레이어에서 발생한 예외를 엔드포인트 레벨인 컨트롤러로 전달해야 한다. 이렇게 전달받은 예외를 스프링 부트에서 처리하는 방식으로 크게 두 가지가 있다.

  • (Rest)ControllerAdvice@ExceptionHandler를 통해 모든 컨트롤러의 예외를 처리
  • ExceptionHandler를 통해 특정 컨트롤러의 예외를 처리

@RestControllerAdvice

@RestController
@RequestMapping("/exception")
public class ExceptionController {

    private final Logger LOGGER = LoggerFactory.getLogger(ExceptionController.class);

    @GetMapping
    public void getRuntimeException() {
        throw new RuntimeException("getRuntimeException 메소드 호출");
    }

    @ExceptionHandler(value = RuntimeException.class)
    public ResponseEntity<Map<String, String>> handleException(RuntimeException e,
        HttpServletRequest request) {
        HttpHeaders responseHeaders = new HttpHeaders();
        responseHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpStatus httpStatus = HttpStatus.BAD_REQUEST;

        LOGGER.error("클래스 내 handleException 호출, {}, {}", request.getRequestURI(),
            e.getMessage());

        Map<String, String> map = new HashMap<>();
        map.put("error type", httpStatus.getReasonPhrase());
        map.put("code", "400");
        map.put("message", e.getMessage());

        return new ResponseEntity<>(map, responseHeaders, httpStatus);
    }

@ControllerAdvice@RestControllerAdvice@Controller@RestController에서 발생하는 예외를 한 곳에서 관리하고 처리할 수 있게 하는 기능을 수행한다.

@ControllerAdvice@RestControllerAdvice의 차이는 @RestControllerAdvice는 결괏값을 JSON 형태로 반환한다.

 

다음과 같이 별도 설정을 통해 예외를 관제하는 범위를 지정할 수도 있다.
@RestControllerAdvice(basePackages = "com.springboot.valid_exception")
범위를 설정하지 않으면 전역 범위에서 예외를 처리하게 된다.

 

@ExceptionHandler@Controller@RestController가 적용된 Bean에서 발생하는 예외를 찾아 처리하는 메서드를 정의할 때 사용한다.

 

어떤 예외 클래스를 처리할지는 value속성으로 등록한다. value는 배열 형식으로도 전달받을 수 있어 여러 예외 클래스를 등록할 수도 있다.

 

위 예제에서는 RuntimeException이 발생하면 처리하도록 코드가 작성되어 있다.
그리고 Map객체에 응답할 메시지를 구성하고 ResponseEntityHttpHeader, HttpStatus, Body 값을 담아 전달한다.

 

위 핸들러 메서드는 다음과 같은 응답을 출력합니다.

{
 "code": "400",
  "error type": "Bad Request",
  "message": "getRuntimeException 메소드 호출"
}

예외 타입 레벨에 따른 예외 처리 우선순위

만약 컨트롤러 또는 @ControllerAdvice 클래스 내에 동일하게 핸들러 메서드가 선언된 상태에서는 위 그림 처럼 더 구체적인 구현이 있는 예외 클래스가 우선순위를 갖게 된다.

다른 경우로는 다음과 같은 상황이 있다.

@ControllerAdvice의 글로벌 예외 처리와 @Controller 내의 컨트롤러 예외 처리에 동일한 타입의 예외 처리를 하게 되면 범위가 좁은 컨트롤러의 핸들러 메서드가 우선순위를 갖게 된다.

참고