34. try ~ catch 을 제거하고 throws Exception 을 사용 해 코드의 가독성을 높이자.
그리고 에러는 스프링의 @ControllerAdvice 에게 맞기도록 설정 해보자.
try catch
많이 사용하는 예외처리 방식이며, 흔히 볼 수 있는 예외처리이다.
예외처리라기 보다는 방어코드라는 얘기도 있다.
장점
- 직관적이며 간편하게 사용할 수 있다.
단점
- 가독성이 떨어 진다.
- 중복이 많다.
- 예외관리가 어렵다.
try catch 로 특별한 에러가 발생해서 특별한 처리를 하고 싶지 않는 한 throws Exception 처리로 예외 처리를 하고자 한다.
Controller 쪽에서 Exception 을 처리하기 위해서 다음과 같은 방식들 사용한다.
@ExceptionHandler 애노테이션을 이용한 처리
@ControllerAdvice 를 이용한 처리
@ResponseStatus 를 이용한 Http 상태 코드 처리
@ControllerAdvice , @ExceptionHandle, @ResponseStatus 에 대한 상세 내용은 구글링을 통해 알아보자.
@ControllerAdvice 의 @ResponseStatus 의 응답 상태 코드 에러는 스프링 프레임워크 에 부담을 줄 수 있다.
400 , 404, 414, 500 에러는 web.xml 추가 하자.
web.xml 는 /WEB-INF/web.xml 에 파일이 있다.
400(잘못된 요청): 서버가 요청의 구문을 인식하지 못했다.
404(찾을 수 없음): 서버가 요청한 페이지를 찾을 수 없다.
예를 들어 서버에 존재하지 않는 페이지에 대한 요청이 있을 경우 서버는 이 코드를 제공 한다.
414(요청 URI가 너무 긺): 요청 URI(일반적으로 URL)가 너무 길어 서버가 처리할 수 없다.
500(내부 서버 오류): 서버에 오류가 발생하여 요청을 수행할 수 없다.
이미지 처럼 파일을 만들자.
header.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <title>Macaronics</title> <meta charset="utf-8"> <!-- 합쳐지고 최소화된 최신 CSS --> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> <!-- 부가적인 테마 --> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css"> <!-- 합쳐지고 최소화된 최신 자바스크립트 --> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script> <!-- IE8 에서 HTML5 요소와 미디어 쿼리를 위한 HTML5 shim 와 Respond.js --> <!-- WARNING: Respond.js 는 당신이 file:// 을 통해 페이지를 볼 때는 동작하지 않습니다. --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <![endif]--> <style type="text/css"> html, body, div{ margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } #bagImg{ width: 120%; height: 120% top:0; z-index: 1; margin: 0; padding: 0; } .container{ z-index: 555; position: fixed; top:30%; left:5%; height:30%; background-color: rgba(255, 255, 255, 0.5); margin-bottom: 20px; } body{ font-family: "굴림"; } .link a{ margin:10px; } </style> </head> <body> <div> <img src="/resources/images/c2.png" id="bagImg">
error.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <jsp:include page="header.jsp"/> <!-- 시작 --> <div class="container"> <div class="page-header text-center"> <h1> 에러 페이지 입니다.</h1> </div> <div class="text-center"> <h2>error </h2> <h4 class="m-b-0 m-t-10px"></h4> <h1 class="link"><a class="btn btn-danger" href="/">홈가기</a><a class="btn btn-success" href="javascript:history.go(-1);">이전페이지 가기</a></h1> </div> </div> </div> </body> </html>
notfound.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <jsp:include page="header.jsp"/> <!-- 시작 --> <div class="container"> <div class="page-header text-center"> <h1>여긴 당신이 찾는 웹페이지가 아닙니다.</h1> </div> <div class="text-center"> <h2>404 page error </h2> <h4 class="m-b-0 m-t-10px"></h4> <h1 class="link"><a class="btn btn-danger" href="/">홈가기</a><a class="btn btn-success" href="javascript:history.go(-1);">이전페이지 가기</a></h1> </div> </div> </div> </body> </html>
internalservererror.jsp, badrequest.jsp , requesturitoolong.jsp 는 유사하다.
400 , 404, 414, 500 제외한 나머지 에러는 @ControllerAdvice 처리하기로 한다.
package config.error;
패키지에 에러를 담당한 CommonException 클래스를 다음과 같이 만들었다.
package config.error; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.ModelAndView; @ControllerAdvice public class CommonException { private static final Logger logger = LoggerFactory.getLogger(CommonException.class); // 개발자 버전 - 에러메시지 상세 확인 error_datail 페이지로 이동 public static final String DEFAULT_ERROR_VIEW = "error/error_developer"; // 일반 - 에러메시지로 error 페이지로 이동 // public static final String DEFAULT_ERROR_VIEW = "error/error"; @ExceptionHandler(Exception.class) public String commonErrorMessage(Exception e) { logger.error("@ExceptionHandler(Exception.class) -- {}", errorMessge(e.getStackTrace())); return DEFAULT_ERROR_VIEW; } @ExceptionHandler(RuntimeException.class) private ModelAndView handleRuntimeExceptionErrorMessage(RuntimeException e) { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName(DEFAULT_ERROR_VIEW); modelAndView.addObject("exception", e); logger.error("@ExceptionHandler(RuntimeException.class) -- {}", errorMessge(e.getStackTrace())); return modelAndView; } @ExceptionHandler(Throwable.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public String methodAnnotationException(Throwable e, HandlerMethod handlerMethod) { ResponseBody methodAnnotation = handlerMethod.getMethodAnnotation(ResponseBody.class); if (methodAnnotation != null) { // @ResponseBody 어노테이션이 있으면 이렇게 처리 하시오 . - ajax logger.error(" @ResponseBody 에서 일어난 에러"); } logger.error("methodAnnotation() , {}" , errorMessge(e.getStackTrace())); ModelAndView modelAndView =new ModelAndView(); modelAndView.setViewName(DEFAULT_ERROR_VIEW); modelAndView.addObject("exception", e); return DEFAULT_ERROR_VIEW; } private String errorMessge(StackTraceElement[] stes){ StringBuffer buffer =new StringBuffer(); buffer.append("\n\n"); buffer.append("★ ★ ★ 에러 메시지 시작 ★★★ \n\n"); for(int i=0; i<stes.length; i++){ if(i==0){ buffer.append("★ 에러 시작 클래스 이름 className : " + stes[i].getClassName()+"\n" ); buffer.append("★ 에러 시작 메서드 이름(methodName) : " + stes[i].getMethodName() + ", line : " + stes[i].getLineNumber() +"\n\n"); }else{ buffer.append("STACKTRACE - className : " + stes[i].getClassName() + ", 메서드 이름(methodName) : " + stes[i].getMethodName() + ", line : " + stes[i].getLineNumber() +"\n"); } } return buffer.toString(); } }
@ControllerAdvice 클래스의 메소드는 발생한 Excetpion 객체의 타입만을 파라미터로 사용할 수 있고, 일 반 Controller와 같이
Model 을 파라미터로 사용하는 것은 지원하지 않기 때문에 직접 ModelAndView 타입을 사용하는 형태로 해야 한다.
일반 유저가 아닌 개발 자는 에러의 상세 내용을 확인해야 한다. 따라서 다음과 같이 페이지를 분리하였다.
개발시에는 "error/error_developer"; 이동한다.
개발자 페이지 에러 - public static final String DEFAULT_ERROR_VIEW = "error/error_developer";
일 반 유저 페이지 에러 -public static final String DEFAULT_ERROR_VIEW = "error/error";
그리고 콘솔 화면에서도 에러 메시지를 봐야 하기때문에 errorMessge(StackTraceElement[] stes) 메소드를 만들어 처리를 하였다.
메소드를 보면 시작부분에 별표로 구분해 놓았고 첫출 에도 별표가 나오도록 하였다. 보통 에러가 스택으로 쌓이기 때문에
에러 첫줄이 에러 발생 원인이 대부분이라 개발자가 빠르게 찾을 수 있도록 특별히 분리해서 콘솔에 나오도록 하였다.
ex) 서비스 영역에서 숫자를 0 으로 나눈는 에러 ArithmeticException 를 고의적으로 만들었을 때 다음과 같은 에러가 콘솔에 나온 모습이다.
서비스
@Override public List<MemberVO> errorReadListMember() { int a=1; int b=a/0; logger.info(" ajaxError ( ) - {} " , b); return dao.readListMember(); }
INFO : net.macaronics.web.controller.SampleController - errorReadListMember() ERROR: config.error.CommonException - @ExceptionHandler(RuntimeException.class) -- ★ ★ ★ 에러 메시지 시작 ★★★ ★ 에러 시작 클래스 이름 className : net.macaronics.web.service.MemberServiceImpl ★ 에러 시작 메서드 이름(methodName) : errorReadListMember, line : 64 STACKTRACE - className : net.macaronics.web.controller.SampleController, 메서드 이름(methodName) : errorReadListMember, line : 134 STACKTRACE - className : sun.reflect.NativeMethodAccessorImpl, 메서드 이름(methodName) : invoke0, line : -2 STACKTRACE - className : sun.reflect.NativeMethodAccessorImpl, 메서드 이름(methodName) : invoke, line : 62 STACKTRACE - className : sun.reflect.DelegatingMethodAccessorImpl, 메서드 이름(methodName) : invoke, line : 43 STACKTRACE - className : java.lang.reflect.Method, 메서드 이름(methodName) : invoke, line : 498 STACKTRACE - className : org.springframework.web.method.support.InvocableHandlerMethod, 메서드 이름(methodName) : doInvoke, line : 221
그리고 try catch 문의 가독성 문제, 반복적인 작업을 제거하고 다음과 같이 throws Exception 를 한다.
@Controller 영역이다. try catch 문은 볼수 없다. throws Exception 을 처리해서 @ControllerAdvice 에서 전부 에러를 제어하도록 한 것이다.
try catch문의 일종의 방어코드라 할수있는 에러제어는 스프링의 @ControllerAdvice 에게 맞기자.
@Controller public class SampleController { @RequestMapping("/testError") public String errorTest() throws Exception{ logger.info(" errorTest ( ) - start " ); int a=1; int b=a/0; logger.info(" errorTest ( ) - {} " , b); return "home"; } @RequestMapping("/ajaxError") public @ResponseBody String ajaxErrorTest() throws Exception{ logger.info(" ajaxError ( ) - start " ); int a=1; int b=a/0; logger.info(" ajaxError ( ) - {} " , b); return "home"; } @RequestMapping("/memberList") public String errorReadListMember() throws Exception{ logger.info(" errorReadListMember() " ); List<MemberVO> list =service.errorReadListMember(); return "home"; } }
서비스 영역이이다. try ~ catch 문 대신에 throws Excption 처리를 하였다.
package net.macaronics.web.service; import java.util.List; import net.macaronics.web.domain.MemberVO; public interface MemberService { //DB 시간정보 물러오기 public String getTime() throws Exception; //멤버 생성 public void createMember(MemberVO vo) throws Exception; //회원 한명 정보 불러오기 public MemberVO getReadMember(String userid, String userpw) throws Exception; //회원 목록 출력 public List<MemberVO> readListMember() throws Exception; //회원 업데이트 public void updateMember(MemberVO vo) throws Exception; //회원 삭제 public void deleteMember(String userid) throws Exception; //회원수 public Integer getCount() throws Exception; //에러 테스트 public List<MemberVO> errorReadListMember() throws Exception; }
개발자를 위한 에러 페이지 화면이다.
error_developer.jsp
서버 배포 후 일반 유저에게는 다음과 같은 화면을 보여주자.
error.jsp
35. 한글 처리
/WEB-INF/web.xml
web.xml 에 다음을 추가 하자.
<!-- 한글 처리를 위한 인코딩 필터 --> <filter> <filter-name>encoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
제작 : macaronics.net - Developer Jun Ho Choi
소스 : 소스가 필요한 분은 비밀 댓글로 작성해 주세요.
댓글 ( 5)
댓글 남기기