[스프링SPRING]Controller에서 예외처리

스프링에서 예외를 처리하기위해 아래 3가지 방법이 있다.

  1. 컨트롤러 메서드 내에세 try-catch로 처리
  2. 컨트롤러에 @ExceptionHandler메서드가 처리
  3. @ControllerAdvice클래스의 @ExceptionHandler메서드가 처리

예외를 처리하게되면 클라이언트에게 보여줄 View를 지정해야하는데 그 방식에는 크게 2가지 방법이 있다.

  1. 응답상태코드별로 뷰 지정: error-page
  2. 예외 종류별로 뷰 지정: SimpleMappingExceptionResolver




1 Controller에서 예외처리 어노테이션 종류

스프링프레임워크에서 예외처리 할때 사용되는 어노테이션에는 3가지가 있다.

어노테이션 사용처 예시
@ExceptionHandler 특정 controller만 예외처리할때 사용
@ControllerAdvice 글로벌 예외처리할때 사용
@ControllerAdvice(“특정패키지명”) 괄호안에 특정 package명을 기재하면 해당 package만 예외처리함 @ControllerAdvice(“com.test.springeprjt”): excom.test.springeprjt를 라는 특정 package 예외처리
@ReqeustStatus사용 HTTP 상태코드를 원하는 대로 변경할 수 있음

간단한 예시로 @ExceptionHandler를 사용하기 전과 후를 비교해보자.




@ExceptionHandler 사용전

다양한 에러처리 메서드를 사용하는 경우 try catch문 중복이 발생할 수 있다.
이런 경우 공통적인 try Catch문을 @ExceptionHandler로 처리해줄수있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Controller
public class ExceptionController {

@RequestMapping("/example1")
public String main() throws Exception {
try{
throw new Exception("500 에러가 발생함");
}catch(Exception e){
return "common_error"
}
}

@RequestMapping("/example2")
public String main2() throws Exception {
try{
throw new NullPointerException("NPE가 발생함");
}catch(Exception e){
return "common_error"
}
}
}




@ExceptionHandler 사용후

Exception를 받아주는 컨트롤러 catcher메서드를 만들었다. 이를 통해 main메서드와 main2메서드의 중복코드가 해소되었다.
또한 컨트롤러 catcher메서드와 catcher2메소드를 Exception별로 나누었다. catcher메서드는 Exception를 받고 catcher2메소드는 NullPointerException만 받게 된다. 이를 통해 특정Exception에 맞게 예외를 처리해줄 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Controller
public class ExceptionController {
@ExceptionHandler(Exception.class)
public String catcher(Exception e, Model model){
model.addAttribute("e", e);
return "common_error"
}

@ExceptionHandler(NullPointerException.class)
public String catcher2(Exception e, Model model){
model.addAttribute("e", e);
return "common_error"
}

@RequestMapping("/example1")
public String main() throws Exception {
throw new Exception("500 에러가 발생함");
}

@RequestMapping("/example2")
public String main2() throws Exception {
throw new NullPointerException("NPE가 발생함");
}
}

그럼 이제 실제로 사용해보자!




CommonExceptionAdvice.java를 컨트롤러 패키지에 생성

테스트용으로 advice객체를 생성한다. advice란 AOP용어 중의 하나이다.
@ControllerAdvice는 글로벌예외처리할 때 사용하므로 해당 클래스가 컨트롤러에 발생하는 모든 예외를 처리하는 클래스임을 의미한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.ModelAndView;

@ControllerAdvice
public class CommonExceptionAdvice {
private static final Logger l = LoggerFactory.getLogger(CommonExceptionAdvice.class);

// @ExceptionHandler(Exception.class)
// public String common(Exception e, Model model) {
// model.addAttribute("e", e); // Model객체 지원x기에 ModelAndView 객체를 사용
// return "common_error";
// }

@ExceptionHandler(Exception.class)
private ModelAndView errorModelAndView(Exception e) {
ModelAndView mav = new ModelAndView();
mav.setViewName("common_error");
mav.addObject("e", e);
return mav;
}

//@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public String commonMismatch(Exception e) {
return "common_error";

//출력값
//INFO : com.itwiilbs.controller.CommonExceptionAdvice - E: commonMismatch(e) 호출 org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is java.lang.NumberFormatException: For input string: ""
//E: commonMismatch 호출org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is java.lang.NumberFormatException: For input string: ""

}
}

CommonExceptionAdvice 객체에서는 Model객체를 파라미터로 사용하는것을 지원하지 않기때문에 ModelAndView 객체를 사용해야한다.




common_error.jsp 페이지 생성

error.jsp는 어떻게 표현하면 좋을까? 보통 “관리자에게 문의하세요”라는 문구가 사용자에게 보여진다. 가장 간단한 형태로 만들어보면 아래와 같다.
isErrorPage=”true”이면 항상 500에러가 나타나므로 isErrorPage=”false”로 설정해줘야한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isErrorPage="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>common_error</title>
</head>
<body>
<h2>common_error페이지</h2>
<h2>웁스! 잘못된 접근입니다. 관리자에게 문의하세요</h2>
e : ${e }
<hr>
메세지 : ${e.getMessage() }
<hr>
<c:forEach items="${e.getStackTrace() }" var="stack" >
${stack.toString() }<br>
</c:forEach>
</body>
</html>




2 뷰지정하기

응답상태코드별로 error-page 이용하기

위 CommonExceptionAdvice.java 코드를 보면 return값으로 특정 jsp 페이지(common_error.jsp)를 호출하고 있다.
해당 컨트롤러에서 처리하지 못한 error들은 어떻게 될까? 톰캣에서 기본적으로 제공하는 error 페이지를 호출된다.
톰캣에서 제공하는 에러페이지는 예쁘지도않고 사용자친화적이지도 않다.
이때 커스텀한 에러 페이지를 사용하게 되는데 서버에서 주는 상태코드별로 디폴트페이지를 web.xml에서 지정해줄 수 있다.
자주 처리하는 HTTP 상태코드는 400,401,403,404,500이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<web-app>
<error-page>
<error-code>400</error-code>
<location>/WEB-INF/view/system/exception/400.jsp</location>
</error-page>
<error-page>
<error-code>401</error-code>
<location>/WEB-INF/view/system/exception/401.jsp</location>
</error-page>
<error-page>
<error-code>403</error-code>
<location>/WEB-INF/view/system/exception/403.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/view/system/exception/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/view/system/exception/500.jsp</location>
</error-page>
</web-app>




SimpleMappingExceptionResolver 이용하기

예외종류별로 error를 보여주고 싶을때는 어떻게 할까?
SimpleMappingExceptionResolver를 사용하면 된다.
sevlet-cntext.xml 파일에 예외종류에 맞는 에러뷰를 등록할 수 있다.
MyErrorPage에 대한 상태코드를 원하는 대로(아래 예제에선 404) 지정할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
<beans:bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<beans:property name="defaultErrorView" value="common/error/defaultError" />
<beans:property name="exceptionMappings">
<beans:props>
<beans:prop key="MyException">common/error/MyErrorPage<beans:/prop>
</beans:props>
</beans:property>
<beans:property name="statusCodes">
<beans:props>
<beans:prop key="MyErrorPage">404<beans:/prop>
</beans:props>
</beans:property>
</beans:bean>




참고