Burninghering's Blog
article thumbnail

예외 처리 이론

컨트롤러 안에 메소드에서 예외가 발생했을 때 예외처리를 처리할 메소드를 만들 수 있다.

메소드 파라미터 안에는 처리할 예외를 넣어주고 @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가 없어서 404에러 발생했다....

 

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으로 설정했는데

500으로 나온다...

<%@ 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> 태그를 이용해서

어떤 응답상태코드일때 어떤 뷰를 보여줄 지 지정할 수 있다.

 


본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.

http://bit.ly/3Y34pE0

 

패스트캠퍼스 [직장인 실무교육]

프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.

fastcampus.co.kr

 

profile

Burninghering's Blog

@개발자 김혜린

안녕하세요! 반갑습니다.