예외 처리 이론
컨트롤러 안에 메소드에서 예외가 발생했을 때 예외처리를 처리할 메소드를 만들 수 있다.
메소드 파라미터 안에는 처리할 예외를 넣어주고 @ExceptionHandler 어노테이션을 붙인다.
두 번째와 세 번째 메소드의 Model m은 서로 다른 객체이다.
두번째 메소드는 모델에다 예외를 넣어 전달했다.
@ControllerAdvice로
전역 = "모든 컨트롤러" 에서 처리하는 클래스를 작성 가능하다.
같은 NullPointerExeption 예외를 처리하는 메소드가 있다면,
전역처리 클래스보다는
같은 컨트롤러 내의 예외 처리 메소드가 우선된다.
400번대는 클라이언트 에러
500번대는 서버 에러
첫 번째 catcher2 메소드는 200 ok 상태 코드가 나오는데, 405번으로 바꾸려고 하는 것이다.
예외 처리 메소드에서 어노테이션이 어떻게 쓰이는 지만 알면 된다
응답 메시지 상태코드가 200(성공한 것이 아니니 바꿔주어야 함) -> 400, 500으로 바꾸고 싶을 때
사용자 정의 예외 클래스를 만들 때에도 어노테이션을 붙일 수 있다.
두 번째 메소드는 MyException에 어노테이션을 안 쓰면 디폴트로 500 에러코드가 나오는데,
500이 아닌 다른 에러코드로 바꾸고 싶을 때 어노테이션을 사용하면 된다.
실습
package com.fastcampus.ch2;
import java.io.FileNotFoundException;
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 {
@ExceptionHandler(Exception.class)
public String catcher(Exception ex, Model model) {
model.addAttribute("ex",ex);
return "error"; //<--응답 상태 코드를 200번으로 보여줌
}
@ExceptionHandler({NullPointerException.class,FileNotFoundException.class})
public String catcher2(Exception ex,Model model) {
model.addAttribute("ex", ex);
return "error";
}
@RequestMapping("/ex")
public String main() throws Exception {
throw new Exception("예외가 발생했습니다.");
}
@RequestMapping("/ex2")
public String main2() throws Exception {
throw new FileNotFoundException("예외가 발생했습니다.");
}
}
예외를 일부러 발생시킨 것인데(화면은 유저 친화적이지만), 상태코드는 200이 나왔다.
상태코드를 바꿔보겠다.
@Controller
public class ExceptionController {
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) //에러 상태코드를 500번으로 바꿔줌
public String catcher(Exception ex, Model model) {
model.addAttribute("ex",ex);
return "error"; //<--응답 상태 코드를 200번으로 보여줌
}
@ExceptionHandler({NullPointerException.class,FileNotFoundException.class})
public String catcher2(Exception ex,Model model) {
model.addAttribute("ex", ex);
return "error";
}
@RequestMapping("/ex")
public String main() throws Exception {
throw new Exception("예외가 발생했습니다.");
}
@RequestMapping("/ex2")
public String main2() throws Exception {
throw new FileNotFoundException("예외가 발생했습니다.");
}
}
500번 코드가 발생하도록 만들었다!
error.jsp 맨 위에
<%@ page contentType="text/html;charset=utf-8" 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>
발생한 예외 : ${ex}<br>
예외 메시지 : ${ex.message}<br>
<ol>
<c:forEach items="${ex.stackTrace}" var="i">
<li>${i.toString()}</li>
</c:forEach>
</ol>
</body>
</html>
을 추가해주면,
이 페이지는 에러가 났을 때 보여주는 페이지라는 것이다.
예외 처리 메소드에서 굳이 모델을 만들어주고 error.jsp에서 ${ex}로 주고받을 필요 없이,
<%@ 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>
exception객체를 사용할 수 있다! (pageContext. 를 붙여줘야 한다)
package com.fastcampus.ch2;
import java.io.FileNotFoundException;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice("com.fastcampus.ch2") //지정된 패키지 내에서 발생한 예외만 처리
public class GlobalCatcher {
@ExceptionHandler(Exception.class)
public String catcher(Exception ex, Model model) {
// model.addAttribute("ex",ex); //모델로 넘겨주기
return "error";
}
@ExceptionHandler({NullPointerException.class,FileNotFoundException.class}) //괄호 안에 배열을 넣어준다
public String catcher2(Exception ex,Model model) {
model.addAttribute("ex", ex);
return "error";
}
}
모델(예외객체)을 주는 코드를 주석으로 처리해도,
에러와 에러메시지가 error.jsp에 잘 나오는 것을 확인할 수 있다.
사용자 정의 예외메소드 만들기
package com.fastcampus.ch2;
import java.io.FileNotFoundException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
class MyException extends RuntimeException{ //사용자 정의 예외 메소드
MyException(String msg){ //기본 생성자는 필수
super(msg);
}
MyException() {this("");}
}
@Controller
public class ExceptionController2 {
@RequestMapping("/ex3")
public String main() throws Exception {
throw new MyException("예외가 발생했습니다."); //<--사용자가 정의한 예외를 던지는 코드로 변경
}
@RequestMapping("/ex4")
public String main2() throws Exception {
throw new FileNotFoundException("예외가 발생했습니다.");
}
}
500에러가 뜬다
@ResponseStatus(HttpStatus.BAD_REQUEST) //<--500번에서 400번으로 바뀌게 함
class MyException extends RuntimeException{ //사용자 정의 예외 메소드
MyException(String msg){ //기본 생성자는 필수
super(msg);
}
MyException() {this("");}
}
@Controller
public class ExceptionController2 {
@RequestMapping("/ex3")
public String main() throws Exception {
throw new MyException("예외가 발생했습니다."); //<--사용자가 정의한 예외를 던지는 코드로 변경
}
@RequestMapping("/ex4")
public String main2() throws Exception {
throw new FileNotFoundException("예외가 발생했습니다.");
}
}
상태코드별 뷰 맵핑
web.xml에
에러 코드별로 내가 지정한 페이지를 보여줄 수 있다(위와 같은 차가운 페이지 말고...)
<error-page>
<error-code>400</error-code>
<location>/error400.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error500.jsp</location>
</error-page>
error400.jsp
<%@ page contentType="text/html;charset=utf-8"% isErrorPage="true" %>
[400] 잘못된 요청입니다.
예외 종류별 뷰 맵핑
Servlet-context.xml에 등록하면 된다.
예외 종류와 뷰를 연결시켜준다.
<beans:bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<beans:property name="defaultErrorView" value="error"/>
<beans:property name="exceptionMappings">
<beans:props>
<beans:prop key="com.fastcampus.ch2.MyException">error400</beans:prop>
</beans:props>
</beans:property>
<beans:property name="statusCodes">
<beans:props>
<beans:prop key="error400">404</beans:prop>
</beans:props>
</beans:property>
</beans:bean>
<beans:prop key="com.fastcampus.ch2.MyException">error400</beans:prop>
MyException 에러가 발생하면, error400.jsp를 반환한다
error400.jsp
<%@ page contentType="text/html;charset=utf-8" isErrorPage="true"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>error400.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>
근데 에러코드를 400으로 설정했는데
<%@ page contentType="text/html;charset=utf-8" isErrorPage="true"%>
isErrorPage="true" 로 하면 무조건 에러코드가 500으로 나오기 때문
그럼 false로 바꿔보자
에러코드가 계속 500으로 나오는 이유
C:\Users\khr58\Documents\workspace-sts-3.9.17.RELEASE\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps
work파일은
jsp파일이 서블릿으로 변환되고
서블릿이 컴파일된 결과가 저장되는 곳이다.
if(exception!=null){
response.setStatus(javax.servlet.HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
위 코드때문에 (SC_INTERNAL_SERVER_ERROR)
500번으로 바뀌는 것이다.
예외 발생 후 처리 과정
클라이언트가 요청을 하면 디스패처서블릿이 받고 해당 컨트롤러에 넘겨준다.
컨트롤러에서 예외가 발생하면(폭탄), try-catch로 잡아주어야 하는데 없을 경우
예외가 자신을 호출한 쪽으로 전달하고 예외가 디스패처서블릿으로 간다
디스패처 서블릿이 예외를 처리하기 위해
handlerExceptionResolvers로 등록된 목록들을 순서대로 본다.(예외를 처리할 수 있는지 하나씩 살펴본다)
이것이 예외처리 기본 전략이다.
디스패처서블릿.properties가
디스패처서블릿이 사용하는 기본 전략을 적어놓은 곳이다.
위 3개를 디스패처서블릿이 예외를 누가 처리할지 순서대로 살펴본다.
1. 예외를 처리할 수 있는
@ExceptionHandler메소드가 있는지 찾아본다.
같은 컨트롤러, 그리고 또 @ControllerAdvice를 찾아본다.
있으면 메소드가 처리한다.
만약 못찾으면
2. @ResponseStatus로 넘어가서,
이 어노테이션이 붙어있는 곳을 찾는다.
원래 형태코드가 500인데 400으로 바꿔주는 일을 하는 어노테이션이다.
web.xml로 가서 상태코드에 해당하는 뷰가 있는지 살펴본다.
있다면 error400.jsp가 클라이언트에 응답을 한다.
만약 못찾으면
3.
@DefaultHandlerExceptionResolver
스프링의 정의된 예외의 상태코드의 기본이 500일때 400번대나 500번대로 바꿔주는 일을 한다.
스프링에서의 예외 처리 방법 종류 - 정리
1.
2.
같은 컨트롤러 안에 예외를 처리하는 메소드들을 만드는 것.
3.
예외처리하는 메소드들을 별도로 클래스로 뽑아서
@ControllerAdvice를 붙이면 모든 컨트롤러에서 발생하는 예외를 하나의 클래스 내에서 처리가능하다.
(패키지도 지정가능하다.)
4.
예외 종류, 에러 뷰를 연결해줄 수 있다.
상태코드도 지정가능하다.
5.
web.xml에
<error-page> 태그를 이용해서
어떤 응답상태코드일때 어떤 뷰를 보여줄 지 지정할 수 있다.
본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.
패스트캠퍼스 [직장인 실무교육]
프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.
fastcampus.co.kr
'패캠 챌린지' 카테고리의 다른 글
패스트캠퍼스 챌린지 - 24일차 [스프링의 정석:남궁성과 끝까지 간다] (0) | 2023.03.15 |
---|---|
패스트캠퍼스 챌린지 - 23일차 [스프링의 정석:남궁성과 끝까지 간다] (0) | 2023.03.14 |
패스트캠퍼스 챌린지 - 21일차 [스프링의 정석:남궁성과 끝까지 간다] (0) | 2023.03.12 |
패스트캠퍼스 챌린지 - 20일차 [스프링의 정석:남궁성과 끝까지 간다] (0) | 2023.03.11 |
패스트캠퍼스 챌린지 - 19일차 [스프링의 정석:남궁성과 끝까지 간다] (0) | 2023.03.10 |