Exception 처리
컨트롤러에서 메소드를 실행하는 과정에서 Exception이 발생한다면 어떻게 될까? 아마도 에러 메시지를 담은 전형적인 에러 화면을 보여주게 될 것이다. 하지만 실제 서비스를 제공한다면, 이러한 화면을 사용자에게 제공해서는 안 될 것이다. 따라서, 오늘은 스프링에서 Exception이 발생한 경우 이를 처리하는 방법에 대해 알아보고자 한다.
Spring에서는 에러 처리와 관련해 크게 3가지의 방법을 제공하고 있다.
1. @ExceptionHandler 어노테이션을 사용한 Exception 처리
2. @ControllerAdvice 어노테이션을 사용한 공통 Exception 처리
3. @ResponseStatus 어노테이션을 사용한 Exception 처리
1. @ExceptionHandler을 사용한 Exception 처리
Controller에서 @RequestMapping 메서드를 실행하는 과정에서 예외가 발생했을 때 해당 예외를 직접 처리하고자 할 때, @ExceptionHandler를 사용한다.
@Controller
public class SampleController {
@RequestMapping("/board")
public String getBoardInfo(Model model, @RequestParam("boardNo") int boardNo) {
Board board = BoardService.getBoardInfo(boardNo);
// boardNo에 해당하는 데이터가 없는 경우, NullPointerException 발생
model.setAttribute("boardInfo", board);
return "board/detail";
}
@ExceptionHandler(NullPointerException.class)
public String handleException() {
return "error/exception";
}
// 또는 전달인자로 처리할 예외 클래스를 받아 정의, 위의 메서드와 동일한 예외 클래스에 대한 처리
@ExceptionHandler
public String handleException(NullPointerException exception) {
// exception에 직접 접근 가능
return "error/excpetion";
}
}
위 코드에서 getBoardInfo() 메서드 실행 시, boardNo에 해당하는 정보가 없는 경우 NullPointerException이 발생하게 된다. 이런 경우, 아래 @ExceptionHandler 메서드를 통해 예외가 발생했을 때 직접 처리 로직을 구현할 수 있다. 위 코드에서처럼 Exception 타입을 지정하게 되면 해당 타입을 포함한 하위 타입까지도 처리할 수 있다.
기본적으로 @ExceptionHandler 메서드를 통해서 예외를 처리하게 되면 응답 코드가 200으로 날아간다. 따라서 다른 응답 코드를 전송하고 싶다면 HttpServletResponse를 이용해 전송하고자 하는 응답 코드를 지정하면 된다.
@ExceptionHandler(NullPointerExecption.class)
public String handleException(HttpServletResponse response) {
// 원하는 응답 코드 설정
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return "error/exception";
}
@ExceptionHandler 처리는 누가 하는가?
Spring은 Controller에서 예외가 발생하면 HandlerExceptionResolver에게 처리를 위임한다. MVC 설정인 <mvc:annotation-driven> 이나 @EnableWebMvc 어노테이션을 사용할 경우, 내부적으로 ExceptionHandlerExceptionResolver를 등록한다. 이는 @ExceptionHandler 어노테이션이 적용된 메서드를 이용해 예외를 처리하는 기능을 제공한다.
MVC 설정을 사용했을 시에는 다음 순서에 따라 HandlerExceptionResolver를 사용한다.
1. ExceptionHandlerExceptionResolver
-> 발생한 예외와 매칭되는 @ExceptionHandler 메서드를 통해 예외를 처리한다.
2. DefaultHandlerExceptionResolver
-> Spring이 발생시키는 예외에 대한 처리를 한다.
3. ResponseStatusExceptionResolver
-> 예외 타입에 @ResponseStatus 어노테이션이 적용되어 있는 경우, @ResponseStatus 어노테이션 값을 이용해 응답 코드를 전송한다.
2. @ControllerAdvice를 이용한 공통 예외 처리
@ExceptionHandler를 이용하면 해당 컨트롤러에서 발생한 예외만 처리할 수 있다. 만약 모든 컨트롤러에서 공통으로 발생하는 예외에 대해 처리하고자 한다면, @ControllerAdvice를 이용하면 된다.
@ControllerAdvice("com.donggun.springMaster.controller")
public class CommonExceptionHandler {
@ExceptionHandler(RuntimeException.class)
public String handleRuntimeException() {
return "error/runtimeException";
}
}
@ControllerAdvice 어노테이션을 이용하면 적용할 컨트롤러의 범위를 지정해 해당 컨트롤러들에 대해 공통으로 예외를 처리할 수 있게 해준다. 위 코드에서는 "com.donggun.springMaster.controller" 패키지가 적용 범위가 된다.
@ControllerAdvice 클래스를 사용하기 위해서는 해당 클래스를 Bean으로 등록해줘야 한다.
<bean class="com.donggun.springMaster.exceptionHandler.CommonExceptionHandler" />
@ControllerAdvice의 @ExceptionHandler 메서드와 컨트롤러의 @ExceptionHandler 중에서는 후자가 우선순위를 갖게 된다.
3. @ResponseStatus를 이용한 예외 응답 코드 설정
@ExceptionHandler를 설명하며 앞서 말했듯이 전송하고자 하는 예외 응답 코드를 직접 설정하고자 할 때, HttpServletResponse를 파라미터로 받아 원하는 응답 코드를 설정하면 된다고 했다. 이를 좀 더 편하게 사용하기 위해 예외 클래스에 @ResponseStatus를 통해 응답 코드를 설정하면 해당 예외가 발생했을 때 해당 예외 응답 코드가 반환된다.
@ResponseStatus(HttpStatus.NOT_FOUND)
public class SampleException extends Exception {
...
}
참고 문헌 : 웹 개발자를 위한 Spring 4.0 프로그래밍 (저자: 최범균, 출판사: 가메출판사)