✔️ Controller 메서드 내에서 try-catch로 처리
✔️ Controller에 @ExceptionHandler 메서드가 처리
✔️ @ControllerAdivice 클래스의 @ExceptionHandler 메서드가 처리
✔️ 예외 종류별로 뷰 지정: SimpleMappingExceptionResolver
✔️ 응답 상태 코드별로 뷰 지정: <error-page>
✔️ try-catch 하기 전 ➡️ Server 에러
package kr.ac.jipark09;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ExceptionController {
@RequestMapping("/ex/")
public void main() throws Exception {
throw new Exception();
}
}
✔️ try-catch를 한 후 ➡️ Client 에러
package kr.ac.jipark09;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ExceptionController {
@RequestMapping("/ex")
public void main() throws Exception {
try {
throw new Exception("예외가 발생했습니다.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
✔️ catch 블럭에 error를 리턴
package kr.ac.jipark09;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ExceptionController {
@RequestMapping("/ex")
public String main() throws Exception {
try {
throw new Exception("예외가 발생했습니다.");
} catch (Exception e) {
return "error";
}
}
}
<%@ page contentType="text/html;charset=utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>error.jsp</title>
</head>
<body>
<h1>예외가 발생했습니다.</h1>
발생한 예외 : ${ex}<br>
예외 메시지 : ${ex.message}<br>
<ol>
<c:forEach items="${ex.stackTrace}" var="i">
<li>${i.toString()}</li>
</c:forEach>
</ol>
</body>
</html>
✔️ Controller계층에서 발생하는 에러를 잡아서 메서드로 처리해주는 기능
package kr.ac.jipark09;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ExceptionController {
// NullPotinerException
@ExceptionHandler(NullPointerException.class)
public String catcher2(NullPointerException e, Model model) {
model.addAttribute("ex", e);
return "error";
}
// Exception
@ExceptionHandler(Exception.class)
public String catcher(Exception e, Model model) {
model.addAttribute("ex", e);
return "error";
}
@RequestMapping("/ex")
public String main() throws Exception {
throw new Exception("예외가 발생하였습니다.");
}
@RequestMapping("/ex2")
public String main2() throws Exception {
throw new NullPointerException("예외가 발생하였습니다.");
}
}
@ExceptionHandler
어노테이션을 쓰고 인자로 캐치하고 싶은 예외클래스를 등록하면 된다.
Controller내에 있는 메서드에서 예외가 발생하면, catcher
메서드가 받는다
➡️ catch블럭으로 인식해도 좋다.
❗️ 만약 @RequestMapping에 들어가서 처리할 예외가 없으면 최고조상인 Exception이 받는다.
✔️ 모든 Controller에서 사용할 수 있게 예외 처리
전역 예외 처리 클래스 작성 가능 (패키지 지정 가능)
예외 처리 메서드가 중복된 경우, Controller 내의 예외 처리 메서드가 우선
예외 처리하는 메서드들을 별도의 클래스에 넣어놓고 그 위에 다가 @ControllerAdive
어노테이션을 붙인다.
➡️ 이 메서드들이 모든 Controller에서 발생하는 예외를 처리하게 된다.
package kr.ac.jipark09;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalCatcher {
// NullPotinerException
@ExceptionHandler(NullPointerException.class)
public String catcher2(NullPointerException e, Model model) {
model.addAttribute("ex", e);
return "error";
}
// Exception
@ExceptionHandler(Exception.class)
public String catcher(Exception e, Model model) {
model.addAttribute("ex", e);
return "error";
}
}
@ExceptionHandler
를 사용해주지 않아도 GlobalCatcher 클래스에서 예외를 처리해 주고 있기 때문에 error.jsp가 잘 나온다.✔️ 이 둘의 model 객체는 다르다.
package kr.ac.jipark09;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ExceptionController {
// NullPotinerException
@ExceptionHandler(NullPointerException.class)
public String catcher2(NullPointerException e, Model model) {
model.addAttribute("ex", e);
return "error";
}
// Exception
@ExceptionHandler(Exception.class)
public String catcher(Exception e, Model model) {
System.out.println("model=" + model);
model.addAttribute("ex", e);
return "error";
}
@RequestMapping("/ex")
public String main(Model model) throws Exception {
model.addAttribute("msg", "message from ExceptionController.main()");
throw new Exception("예외가 발생하였습니다.");
}
@RequestMapping("/ex2")
public String main2() throws NullPointerException {
throw new NullPointerException("예외가 발생하였습니다.");
}
}
[console]
model={}
이 둘의 model 객체가 같다면 콘솔에 main 메서드에서 저장했던 model 객체가 나왔어야 했다.
➡️ 나오지 않음 즉, 이 둘은 같은 model 객체가 아니다.
❗️ Controller 메서드에서 예외를 처리하는 메서드한테 데이터를 전달하는 것이 아님
✔️ 응답 메시지의 상태 코드를 변경할 때 사용
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) // 405 method not allowed
@ExceptionHandler({NullPointerException.class, ClassCastException.class})
public String catcher(Exception ex, Model m) {
m.addAttribute("ex", ex) {
return "error"
}
}
예외 처리 메서드에다가 처리
이때 error.jsp view를 처리하게 되는데, 응답코드가 200이다. ➡️ 요청 처리 성공
: 하지만 예외가 떴는데 요청을 성공적으로 처리했다는 것을 인식주어서는 안된다.
: 200 대신 400번대나 500번을 바꿔줘야 한다.
METHOD_NOT_ALLOWED
: 405
✔️ 사용자정의 예외 클래스
@ResponseStatus(HttpStatus.BAD_REQUEST) // 400 Bad Request.
class MyException extends RuntimeException {
MyException(String msg) {
super(msg);
}
MyException() {
this("");
}
}
✔️ <%@page isErrorPage = "true"> 를 사용하면 해당 페이지는 에러페이지가 됨
<%@ page contentType="text/html;charset=utf-8" isErrorPage="true"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>error.jsp</title>
</head>
<body>
<h1>예외가 발생했습니다.</h1>
발생한 예외 : ${pageContext.exception}<br>
예외 메시지 : ${pageContext.exception.message}<br>
<ol>
<c:forEach items="${pageContext.exception.stackTrace}" var="i">
<li>${i.toString()}</li>
</c:forEach>
</ol>
</body>
</html>
pageContext.exception 객체 사용
<%@page isErrorPage = "true">
로 해 놓으면 상태코드를 바꿔도 항상 500번대로 바뀌게 됨
✔️ 상태 코드 별 View 맵핑
package kr.ac.jipark09;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.BAD_REQUEST) // 500 -> 400
class MyException extends RuntimeException {
public MyException(String msg) {
super(msg);
}
public MyException() {}
}
@Controller
public class ExceptionController2 {
@RequestMapping("/ex3")
public String main() throws Exception {
throw new MyException("예외가 발생하였습니다.");
}
@RequestMapping("/ex4")
public String main2() throws NullPointerException {
throw new NullPointerException("예외가 발생하였습니다.");
}
}
✔️ 예외 종류별 View 맵핑에 사용. serlvet-context.xml에 등록
예외 종류 - 에러 뷰
에러 뷰에 <%@page isErrorPage = "true">
가 설정되어 있으면 항상 500번 대로 뜨게 된다.
➡️ false
로 바꾸면 404로 설정한 상태코드가 잘 나오게 된다.
✔️ Spring의 예외 처리 기본 전략
DispatcherServlet
은 이 예외를 처리할려고 handlerExceptionResolvers
에 가서 등록되어 있는 것들을 본다. 이 예외를 처리할 수 있는지 순서대로 훑어보게 된다.NullPointerException이 뜨면, 첫 번째, ExceptionHandlerExceptionResolver
가 이 예외를 처리할 수 있는 @ExceptionHandler
메서드가 있는지 찾는다.
➡️ Controller ➡️ @ControllerAdvice
/ @ExceptionHandler
➡️ "error" 반환
첫번째 리졸버가 찾지 못하면 2, 3 순으로 찾는다.
500 ➡️ 400 으로 바꿔주는 역할
web.xml에 가서 400번에 해당하는 뷰가 있는지 확인한다. ➡️ /error400.jsp 반환
✔️ DispatcherServlet이 사용하는 기본전략을 적어놓은 곳
Reference
:https://fastcampus.co.kr/dev_academy_nks