Controller를 작성할 때 예외 상황을 고려하면 처리해야 하는 작업이 엄청 늘어날 수 밖에 없습니다.
이럴 때 스프링 MVC에서는 이러한 작업을 다음과 같은 방식으로 처리할 수 가 있는데요?
- @ExceptionHandler와 @ControllerAdvice를 이용한 처리
- @ResponseEntity를 이용하는 예외 메시지 구성
@ContollerAdvice
@ContollerAdvice는 뒤에서 배우게 되는 AOP(Aspect-Oriented-Programming)를 이용하는 방식입니다.
AOP에 대해 간단히 언급하자면 핵심적인 로직은 아니지만 프로그램에서 필요한 '공통적인 관심사'(cross-concern)는
분리하자는 개념입니다.
Controller를 작성할 때는 메서드의 모든 예외사항을 전부 핸들링해야 한다면 중복적이고 많은양의 코드를 작성해야 하지만, AOP 방식을 이용하면 공통적인 예외사항에 대해서는 별도로 @ControllerAdvice를 이용해서 분리하는 방식입니다.
package org.zerock.exception;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import lombok.extern.log4j.Log4j;
@ControllerAdvice
@Log4j
public class CommonExceptionAdvice {
@ExceptionHandler(Exception.class)
public String exceppt(Exception ex, Model model) {
log.error("Exception ......." + ex.getMessage());
model.addAttribute("exception", ex);
log.error(model);
return "error_page";
}
}
CommonExceptionAdvice 클래스에는 @ControllerAdvice는 어노테이션과 @ExceptionHandler라는 어노테이션을
사용하고 있습니다.
@ControllerAdvice는 해당 객체가 스프링의 컨트롤러에서 발생하는 예외를 처리하는 존재임을 명시하는 용도이며,
@ExceptionHandler는 해당 메서드의 예외 타입을 처리한다는 것을 의미합니다.
@ExceptionHandler의 속성으로는 Exception 클래스 타입을 지정할 수 있습니다.
위의 코드의 경우는 Exception.class를 지정하였으므로 모든 예외에 대한 처리가 exceppt()만을 이용해 처리합니다.
만약 특정한 타입의 예외를 다루고 싶다면 Exception.class 대신에 구체적인 예외의 클래스를 지정해야 합니다.
JSP 화면에서도 구체적인 메시지를 보고 싶다면 Model을 이용해서 전달하는 것이 좋습니다.
그리고 org.zerock.exception 패키지는 servlet-context.xml서 인식하지 않기 때문에
<component-scan>을 이용해서 해당 패키지의 내용을 조사하도록 해야 합니다.
<!-- servlet-context.xml>
<context:component-scan base-package="org.zerock.controller" />
<context:component-scan base-package="org.zerock.exception" />
CommonExceptionAdvice의 exceppt()의 리턴값은 문자열이므로 jsp 파일의 경로가 됩니다.
jsp는 error_page.jsp 이므로 /WEB-INF/views 폴더내에 작성해야 합니다.
<!-- error_page.jsp -->
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
<h4><c:out value="${exception.getMessage()}"></c:out></h4>
<ul>
<c:forEach items="${exception.getStackTrace() }" var="stack">
<li><c:out value="${stack }"></c:out></li>
</c:forEach>
</ul>
</body>
</html>
예외의 메시지가 정삭적으로 출력되는지 확인해보려면 고의로 파리미터 값의 변환에 문제가 있게 만들어서
호출해보는 방법이 있습니다.
package org.zerock.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.zerock.domain.SampleDTO;
import lombok.extern.log4j.Log4j;
@Controller
@RequestMapping("/sample/*")
@Log4j
public class SampleController {
@GetMapping("/ex04")
public String ex04(SampleDTO dto, @ModelAttribute("page")int page) {
log.info("dto : " + dto);
log.info("'page : " + page);
return "/sample/ex04";
}
}
ex04메서드의 파라미터 값은 dto이며, dto 안에는 namer과 page가 있습니다.
이제 서버를 구동시켜 확인해보겠습니다.
page의 반환값은 int인데 문자열을 주었기 때문에 다음과 같은 에러가 발생합니다.