[Spring] 스프링 MVC(11) - @ExceptionHandler, @ControllerAdvice, @ResponseStatus 어노테이션
@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 메서드를 찾는다.
- 같은 컨트롤러에 위치한 @ExceptionHandler 메서드 중 해당 예외를 처리할 수 있는 메서드
- @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 프로그래밍 (최범균 저)