서블릿 예외처리
- 예외가 발생할 경우 ex) RuntimeException...
response.sendError()
로 예외를 전달 할 경우- --> 당장 예외가 발생하는 것이 아니라 서블릿 컨테이너에 전달, 응답전에 컨테이너가 확인
어플리케이션 내에서 예외처리가 되지 않을경우(try~catch) WAS까지 예외 전달 되어 예외페이지 출력된다
예외페이지 등록
예외페이지 등록
- web.xml
<web-app>
<error-page>
<error-code>404</error-code>
<location>/error-page/404.html</location>
</error-page>
</web-app>
- java
@Component
public class WebServerCustomizer
implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
@Override
public void customize(ConfigurableWebServerFactory factory) {
/*
* HttpStatus.NOT_FOUND(404에러)발생시
* /error-page/404 경로로 이동
*/
ErrorPage errorPage404 =
new ErrorPage(HttpStatus.NOT_FOUND, "/error-page/404");
//페이지 등록
factory.addErrorPages(errorPage404);
}
}
요청 흐름
- 컨트롤러(예외발생) -> 인터셉터, 서블릿, 필터 -> WAS
- WAS 지정해둔 경로로 오류정보를 request에포함하여 다시 요청
-> 필터, 서블릿, 인터셉터 -> 컨트롤러 지정경로 호출-> view
필터나 인터셉터가 다시한번 호출 되는것은 너무 비효율적
처음 요청의 dispatcherType = REQUEST,
예외 발생되어 다시 호출 된 것의 dispatcherType = ERROR
DispatcherType
- REQUEST: 클라이언트 요청
- ERROR: 오류발생시 요청
- FORWARD: 서블릿에서 다른 서블릿으로 호출
- INCLUDE
- ASYNC
필터 재호출 방지
필터를 등록하는 과정에서 DispatcherType을 등록을 하여 해당 type에 대해서만 필터 호출
FilterRegistrationBean<Filter> filterRegistrationBean
= new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LogFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.setDispatcherTypes(
DispatcherType.REQUEST, DispatcherType.ERROR
);
REQUEST, ERROR타입에 대해서 필터를 호출한다
Error페이지에서는 호출을 하기를 원치 않는다면 제거하면 된다
인터셉터 재호출 방지
인터셉터는 dispatcherType에 무관하게 항상 호출
단지 에러페이지 호출 경로를 제외를 해주면 된다
registry.addInterceptor(new LogInterceptor())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns("/error-page/**");
//에러 페이지에 대한 모든 호출 제외
스프링 예외처리
@ExceptionHandler
처리하고 싶은 예외에 대해서 지정을 해주면 된다(여러개 지정가능)
@Controller, @RestController가 적용된 bean내에서 발생한 예외에 대해서 하나의 메서드에서 처리한다
@ExceptionHandler({IllegalArgumentException.class}/* 생략가능*/)
public String illegalExHandle(IllegalArgumentException e) {
log.error("[exceptionHandle] ex", e);
return "error-page";
}
위 코드는 IllegalArgumentException에러를 처리하는 메서드이다
예외를 생략을 하면 파라미터로 넘어온 예외에 대해서 처리한다
주의사항
@ExceptionHandler
메서드를 통해서 예외를 처리하면 기본적으로 정상적으로 처리가 된것으로 되어 200코드가 전송이 된다
다른 응답 코드를 전송하고 싶다면 response.setStatus
를 이용하여 응답코드를 지정을 해 주거나@ResponseStatus
를 이용하여 응답코드를 지정을 해 줄 수 있다
@ResponseStatus
은 예외의 응답코드 또한 지정이 가능하다
@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "error.bad")
public class BadRequestException extends RuntimeException{
}
@ControllerAdvice, @RestControllerAdvice
@ExceptionHandler
를 사용을 하면 해당 컨트롤러에서만 예외에 대한 처리가 가능하다
그리고 컨트롤러에 경로에 대한 처리로직과 예외 처리로직이 같이 있으면서 너무 복잡해 잔다
그래서 예외 처리를 글로벌하게 적용을 하고 예외로직을 분리하고자 해당 어노테이션이 사용된다
단순히 예외처리 bean을 등록을 하여 사용을 한다고 생각을 하면된다
공통설정 적용 지정
속성 | 설명 |
---|---|
value | basePackage지정 |
annotations | 해당 어노테이션이 있는 컨트롤러 |
assignable Types | 특정 타입과 하위 타입컨트롤러 지정 |
스프링부트 예외처리
BasicErrorController에 맞게 오류페이지 화면만 처리하면 된다
경로: /error/xxx.html
API예외처리
기존 html화면을 보여주는 것이 아니라 JSON으로 클라이언트에게 전송을 해줘야 한다
서블릿 이용
@RequestMapping(value = "/error-page/500", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Map<String, Object>> errorPage(
HttpServletRequest request, HttpServletResponse response)
{
Map<String, Object> result = new HashMap<>();
Exception ex = (Exception) request.getAttribute(ERROR_EXCEPTION);
result.put("status", request.getAttribute(ERROR_STATUS_CODE));
result.put("message", ex.getMessage());
Integer statusCode = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
return new ResponseEntity<>(result, HttpStatus.valueOf(statusCode));
}
스프링
위에서 봤던 @ExceptionHandler
를 사용을 하면된다
단지 차이점이라고 하면 API통신을 하기 때문에 html전송이 아닌 DTO를 통한 객체를 JSON으로 변환하여 전송을 해야한다 @Controller
에서 @RestController
로 변경이 된것이다
또한 @ResponseStatus
를 통해 응답 코드를 지정해 줄 수 있다
참고
인프런_스프링 MVC 2편 - 백엔드 웹개발 핵심 기술_김영한 님 강의
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard
'공부기록 > Spring' 카테고리의 다른 글
Spring boot CORS 설정 (0) | 2022.08.03 |
---|---|
DTO관련 고민 (0) | 2022.07.13 |
Spring-filter interceptor (0) | 2022.02.03 |
Spring - 로그인 처리 세션쿠키 (0) | 2022.01.28 |
Spring-MVC4 에러검증2 (0) | 2022.01.22 |