HandlerExceptionResolver 사용하여 Exception 페이지 처리에 대해 유연하게 대응하기
안녕하세요. 오늘은 대선날입니다. 다들 투표는 잘하셨는지요? 저는 사전 투표를 하지 못해서 오늘 할까 합니다. 오전에는 잠깐 볼일이 있어서 잠깐 보느라 못하고.. 나가기 전에 포스트를 작성하려고 정리해뒀던 게 생각나서 지금 이 포스트를 마무리 지은 후 점심을 간단하게 챙겨 먹고 저도 투표를 하러 가야겠습니다. 과연 현재 대통령이 없는 무정부(?) 상태에서 새정부가 오늘 바로 출범한다고 생각하니 매우 기대가 됩니다.
자 그럼, 나가기 전에 정리해뒀던 포스트 내용이 무엇이냐 함은... SpringFramework 의 오류처리에 관련된 Resolver 에 관한 내용입니다. 보통 SpringFramework 를 사용하면서 Exception 발생 시 사용자 브라우저에 지저분한 소스코드와 함께 오류페이지를 보여주지 않기 위해 에러 페이지 대한 처리를 web.xml 에 정의하여 왔습니다. 이런식으로 말이죠.
<error-page>
<error-code>404</error-code>
<location>/exception_404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/exception_500.jsp</location>
</error-page>
</error-page>
그런데 HandlerExceptionResolver 를 사용하면 Exception 발생 시 에러 페이지 처리에 대해 좀 더 유연하게 대처 할 수 있다는 것을 알게 되었습니다. 아... 최근 HttpServletRequest 의 Parameter 값을 변경하는 것도 그렇고 이런 Exception 관련 처리도 그렇고 .. 전 지금까지 대체 무엇을 알고 개발을 해왔던 것인지.. 정말 다시 되돌아 보게 되네요. -_-;;
아무튼 Controller, Service, Dao 를 거치면서 업무 프로세스를 처리 시에 발생된 예외는 DispatcherServlet 이 가장 먼저 전달 받아 예외 처리에 대한 Resolver 가 없다면 ServletContainer 에게로 전달되어 브라우저에는 404, 500 등 오류 메시지가 그대로 노출이 됩니다. 만약 HandlerExceptionResolver 에 대한 처리가 구현되어 있다면 DispatcherServlet 은 해당 HandlerExceptionResolver 에게 발생된 예외 처리에 대한 구현이 되어 있는 지 확인하여 처리가 되어 있다면 해당 HandlerExceptionResolver 에게 처리를 위임을 합니다. 그리고 이 HandlerExceptionResolver 에서는 ModelAndView 를 리턴하여 처리 하는 등 Controller 처럼 처리가 가능하게 해줍니다. HandlerExceptionResolver 를 살짝 살펴보자면 ModelAndView 를 리턴하는 아래 메소드 하나로 정의되어 있는걸 볼 수 있습니다.
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public abstract interface HandlerExceptionResolver {
public abstract ModelAndView resolveException(HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse, Object paramObject, Exception paramException);
}
그러므로 오류 처리에 관한 로직을 구현하려 한다면 이 resolveException 을 Override 하여 구현하면 되겠습니다. 현재 사용중인 부분을 살짝 바꾼 간단한 예제를 보도록 하겠습니다.
아래의 ServiceException 은 현재 진행중인 프로젝트에서 MessageException 을 상속받아 별도 구현한 ServiceException 입니다. 보통 SpringFramework 를 사용하실 때, 아 MVC 패턴을 사용할 때라고 해야 할까요? 아무튼. Service 는 ServiceException, Dao 는 DaoException 으로 별도 구현해서 많이 쓰시고 계신걸로 알고 있습니다. (아닌가요? -_-;; 저만 지금까지 그래왔나요? -_-;;) 아무튼 별도로 구성한 ServiceException 이라는 것을 참고하시고 보시면 되겠습니다.
1. xml 에 resolver 정의.
<bean name="exceptionResolver" class="customer.common.CustomerBaseExceptionResolver" />
2. CustomerBaseExceptionResolver 클래스 구현
public class CustomerBaseExceptionResolver implements HandlerExceptionResolver {
private static final Log logger = LogFactory.getLog(getClass());
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object params, Exception ex) {
Configuration configuration = ConfigService.getInstance().getConfiguration();
ModelAndView mav = null;
// 아래 부분은 ADMIN, FRONT 에 대한 구분으로 개개인의 필요에 따라 없어도 상관없습니다.
String workServer = configuration.getString(ServerUtil.getServerName(request));
if ("ADMIN".equals(workServer)) {
mav = adminException(request, response, params, ex);
} else {
mav = frontException(request, response, params, ex);
}
return mav;
}
/**
* ADMIN EXCEPTION
* @param HttpServletRequest _request, HttpServletResponse _response, Object params, Exception ex
* @return ModelAndView
*/
public ModelAndView adminException(HttpServletRequest _request, HttpServletResponse _response, Object params, Exception ex){
String errorMsg = "";
if(ex instanceof ServiceException){
ServiceException se = (ServiceException)ex;
// 로그 추적을 위해 해당 오류 관련 내용을 서버에 한번 출력합니다..
se.printStackTrace();
errorMsg = "ADMIN ServierException";
}else{
ex.printStackTrace();
errorMsg = "ADMIN Exception";
}
_request.setAttribute("errMsg", errMsg);
return new ModelAndView("/views/admin/exception/exception"); // /view/admin/exception/exception.jsp 호출
}
/**
* FRONT EXCEPTION
* @param HttpServletRequest _request, HttpServletResponse _response, Object params, Exception ex
* @return ModelAndView
*/
public ModelAndView frontException(HttpServletRequest _request, HttpServletResponse _response, Object params, Exception ex){
String errorMsg = "";
if(ex instanceof ServiceException){
ServiceException se = (ServiceException)ex;
// 로그 추적을 위해 해당 오류 관련 내용을 서버에 한번 출력합니다.
se.printStackTrace();
errorMsg = "FRONT ServierException";
}else{
ex.printStackTrace();
errorMsg = "FRONT Exception";
}
_request.setAttribute("errMsg", errMsg);
return new ModelAndView("/views/front/exception/exception"); // /view/front/exception/exception.jsp 호출
}
}
이렇게 위에 먼저 말씀드렸던 resolveException 을 Override 하여 로직을 구현하였습니다. 위의 예제에는 ServiceException 외에는 모두 Exception 으로 처리하였지만 SQLException, IOException 등 원하는 Exception 들을 추가하여 처리하면 될 것 같습니다.
그럼 포스트는 이만 마치도록 하고 어서 투표를 하러가보도록 하겠습니다! 다들 즐거운 하루 되시길 바랍니다.