Spring

[Spring] 스프링 MVC(11) - @ExceptionHandler, @ControllerAdvice, @ResponseStatus 어노테이션

재담 2022. 3. 16. 23:33

@ExceptionHandler 어노테이션을 이용한 예외 처리

컨트롤러의 @RequestMapping 메서드를 실행하는 과정에서 예외가 발생할 때 직접 예외 처리를 하고 싶다면, @ExceptionHandler 어노테이션을 사용하면 된다. 다음 코드를 보자.

@Controller
public class CalculationController {
    @RequestMapping("/cal/divide")
    public String divide(Model model, @RequestParam("op1") int op1, @RequestParam("op2") int op2) {
        model.addAttribute("result", op1 / op2);
        return "cal/result";
    }

    @ExceptionHandler(ArithmeticException.class)
    public String handleException() {
        return "error/exception";
    }
    
    // 이하 생략
    ...
}

위 코드에서 @RequestMapping 메서드 실행 과정에서 0으로 나누는 등의 ArithmeticException이 발생하면 handleException() 메서드를 통해서 예외를 처리하게 된다.

 

예외 타입을 지정하면, 해당 타입을 포함해 하위 타입까지도 처리할 수 있다. 만약 다음과 같이 @ExceptionHandler 어노테이션의 값으로 RuntimeException.class를 설정하면 RuntimeException의 하위 타입인 ArithmeticException까지 처리하게 된다.

@ExceptionHandler(RuntimeException.class)
public String handleException() {
    return "error/exception";
}

 

@ExceptionHandler 메서드의 기본 응답 코드는 200인데, 만약 500과 같은 응답 코드를 전송하고 싶다면, 다음과 같이 HttpServletResponse를 파라미터를 추가하고 setStatusCode() 메서드를 이용해서 알맞은 응답 코드를 지정하면 된다.

@ExceptionHandler(RuntimeException.class)
public String handleException(HttpServletResponse response) {
    response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    return "error/exception";
}

 

@ExceptionHandler 메서드에서 익셉션 객체에 접근하고 싶다면, 다음과 같이 파라미터를 추가해주면 된다. @ExceptionHandler 어노테이션의 값으로 타입을 지정하지 않으면 파라미터에 있는 익셉션 타입을 사용한다. 따라서 ArithmeticException이 발생하면, 파라미터 중에 ArithmeticException이 포함된 메서드를 이용해서 예외를 처리한다.

@ExceptionHandler
public String handleException(ArithmeticException ex) {
    // 이하 생략
    ...
    
    return "error/exception";
}

 

@ControllerAdvice 어노테이션을 이용한 공통 예외 처리

여러 컨트롤러에서 동일한 예외를 발생시킬 경우, @ControllerAdvice 어노테이션을 이용해서 예외 처리 메서드 중복을 없앨 수 있다. 다음 코드를 보자.

@ControllerAdvice("com.mypackage")
public class CommonExceptionHandler {

   @ExceptionHandler(RuntimeException.class)
   public String handleException() {
      return "error/commonException";
   }
}

위 코드의 경우 "com.mypackage" 패키지 및 그 하위 패키지에 속한 컨트롤러 클래스를 위한 공통 기능을 정의하였다. 이 패키지 및 하위 패키지에 속한 컨트롤러에서 RuntimeException이 발생하면 handleException() 메서드를 통해 예외를 처리하게 된다.

 

@ControllerAdvice 적용 클래스가 동작하려면 해당 클래스를 빈으로 등록해야 한다.

<bean class="[패키지경로].CommonExceptionHandler" />

 

컨트롤러의 메서드를 실행하는 과정에서 예외가 발생하면 다음의 순서로 @ExceptionHandler 메서드를 찾는다.

  1. 같은 컨트롤러에 위치한 @ExceptionHandler 메서드 중 해당 예외를 처리할 수 있는 메서드
  2. @ControllerAdvice 클래스에 위치한 @ExceptionHandler 메서드

 

@ResponseStatus를 이용한 예외 응답 코드 설정

@ResponseStatus 어노테이션을 사용하면 예외 처리를 따로 하지 않고 응답 코드를 변경할 수 있다. @ResponseStatus 어노테이션은 다음과 같이 익셉션 클래스에 적용한다.

@ResponseStatus(HttpStatus.NOT_FOUND)
public class NoFileInfoException extends Exception {
}

만약 @ResponseStatus 어노테이션을 적용하지 않고 NoFileInfoException을 throw 하게 되면 500 응답 코드가 화면에 표시된다.

 

HttpStatus 열거 타입에서 주로 사용되는 값을 다음과 같다.

  • OK(200, "OK")
  • MOVED_PERMANENTLY(301, "Moved Permanently")
  • NOT_MODIFIED(304, "Not Modified")
  • TEMPORARY_REDIRECT(307, "Temporary Redirect")
  • BAD_REQUEST(400, "Bad Request")
  • UNAUTHORIZED(401, "Unauthorized")
  • PAYMENT_REQUIRED(402, "Payment Required")
  • FORBIDDEN(403, "Forbidden")
  • NOT_FOUND(404, "Not Found")
  • METHOD_NOT_ALLOWED(405, "Method Not Allowed")
  • NOT_ACCEPTABLE(406, "Not Acceptable")
  • UNSUPPORTED_MEDIA_TYPE("415, "Unsupported Media Type")
  • TOO_MANY_REQUESTS(429, "Too Many Requests")
  • INTERNAL_SERVER_ERROR(500, "Internal Server Error")
  • NOT_IMPLEMENTED(501, "Not Implemented")
  • SERVICE_UNAVAILABLE(503, "Service Unavailable")

 

참고로 @ResponseStatus 어노테이션은 컨트롤러 영역에서만 사용하고, 서비스 / 도메인 / 영속성 영역에서는 사용하지 않는 것이 좋다. 왜냐하면 @ResponseStatus 어노테이션은 UI 처리의 의미를 내포하고 있기 때문에, 서비스 / 도메인 / 영속성 영역에 @ResponseStatus 어노테이션을 사용하면 UI 영역에 의존하는 결과를 만든다. 즉 UI 변경이 핵심 영역에 영향을 줄 수 있기 때문에 @ResponseStatus 어노테이션은 컨트롤러 영역에서만 사용해야 한다.


Reference

  • 웹 개발자를 위한 Spring 4.0 프로그래밍 (최범균 저)