min's devlog
Spring Framework의 전역 에러 처리 본문
Spring Framework의 전역 에러 처리
Spring framework는 전역 에러를 처리하기 위해 아래의 인터페이스를 제공한다.
제공되는 interface | |
servlet (webmvc) | HandlerExceptionResolver |
reacitve (webflux) | WebExceptionHandler |
Servlet 전역 에러 처리
HandlerExceptionResolver
handlerExceptionResolver는 servlet에서 전역 에러 처리를 하기 위해 제공되는 인터페이스이다.
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
아래와 같은 구현체가 제공된다.
구현체 | 설명 |
SimpleMappingExceptionResolver | 발생한 exception class 이름과 error view 이름을 맵핑해서 사용 boot 에서 기본 설정대상x |
DefaultHandlerExceptionResolver | 기본적으로 사용되는 exception 들을 공통 처리해주기 위해 제공되는 resolver |
ResponseStatusExceptionResolver | ResponseStatusException이 throw 되거나 @ResponseStatus를 설정한 지점에서 발생한 에러를 처리 |
ExceptionHandlerExceptionResolver | @Controller 또는 @ControllerAdvice에 선언된 @ExceptionHandler 을 통해 에러를 처리 |
SimpleMappingExceptionResolver를 제외한 3개의 ExceptionResolver가 Spring boot를 사용하면 기본 등록된다.
DefaultHandlerExceptionResolver는 스프링이 알아서 처리해주는 에러 처리 모음이기 때문에 따로 구현할 선택항목이 아니며, ResponseStatusExceptionResolver를 사용하는 방법은 해당 에러에 종속되거나 어노테이션을 각 메서드나 클래스에 지정해야 하기 때문에 전역으로 사용하기엔 좋은 방법은 아니라고 한다.
@ExceptionHandler 에러 처리 만들기
@Controller
public class SimpleController {
// ...
@ExceptionHandler
public ResponseEntity<String> handle(IOException ex) {
// ...
}
}
해당 Controller의 메소드에서 발생한 IOException은 위에 @ExceptionHandler로 선언된 handle 메서드를 통해 에러가 처리된다.
@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(IOException ex) {
// ...
}
위와 같이 @ExceptionHandler에 대상 Exception 목록을 정의하여 처리 대상인 Exception 유형을 좀 더 좁힐 수 있다.
IOException 중 FileSystemException과 RemoteException 에 대해서만 해당 handle 메서드가 동작한다.
@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(Exception ex) {
// ...
}
위와 같이 대상 Exception을 @ExceptionHandler로 정의하고 일반적인 에러 유형인 Exception이나 또는 상위인 Throwable을 매개변수로 사용할 수도 있다.
@ControllerAdvice와 같이 사용하기
@Controller에 정의된 @ExceptionHandler는 해당 controller에서만 동작한다.
모든 Controller에 대해 발생한 전역 에러에 대해 처리하는 @ExceptionHandler는 @ControllerAdvice와 같이 사용하면 된다.
@Controller
@ControllerAdvice
public class GlobalExceptionController {
// ...
@ExceptionHandler
public ResponseEntity<String> handle(IOException ex) {
// ...
}
}
위와 같이 @ControllerAdvice로 선언한 controller내에 정의된 @ExceptionHandler는 모든 controller를 대상으로 동작한다.
controller 대상을 좁혀서 지정하고 싶은 경우 아래처럼 사용하면 된다.
// @RestController를 사용한 모든 controller 대상
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}
// 해당 패키지 내 모든 controller 대상
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}
// 해당 클래스 하위로 구현된 controller 대상
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class ExampleAdvice3 {}
@ExceptionHandler에서 지원하는 method arguments
@ExceptionHandler를 사용하면서 아래의 매개 변수들을 사용할 수 있다.
매개 변수 | 설명 |
Exception type | 대상 Exception |
HandlerMethod | 예외를 발생시킨 controller method |
WebRequest, NativeWebRequest | Servlet API를 직접 사용하지않고 request parameter와 request, session attribute에 접근 |
javax.servlet.ServletRequest javax.servlet.ServletResponse |
ServletRequest, HttpServletRequest 또는 스프링의 MultipartRequest, MultipartHttpServletRequest와 같은 유형 |
javax.servlet.http.HttpSession | |
javax.security.Principal | |
HttpMethod | |
java.util.Locale | 현재 요청에 대한 Locale 정보 |
java.util.TimeZone java.time.ZoneId |
현재 요청에 대한 timezone 정보 |
java.io.OutputStream java.io.Writer |
|
java.util.Map org.springframework.ui.Model org.springframework.ui.ModelMap |
error response를 위한 model. empty 상태임 |
RedirectAttributes | |
@SessionAttribute | |
@RequestAttribute |
@ExceptionHandler에서 지원하는 return values
반환 값 | |
@ResponseBody | HttpMessageConverter 객체에 의해 변환되어 반환 |
HttpEntity<B> ResponseEntity<B> |
(header와 body를 포함한) 전체 응답 처리를 HttpMessageConverter 객체를 통해 변환하여 반환 |
String | viewResolver를 통해 처리되는 view 이름 |
View | |
java.util.Map org.springframework.ui.Model |
RequestToViewNameTranslator를 통해 암시적으로 결정된 view 이름을 사용하는 경우 반환 값으로 사용 가능 |
@ModelAttribute | |
ModelAndView object | ModelAndView를 반환, response status도 추가 가능 |
void | |
Any other return value | 위 열거한 값과 일치하지 않고 BeanUtils#isSimpleProperty에 의해 결정된 simple type이 아닌 경우 model에 추가할 model attribute로 처리됨. simple type인 경우 해결되지 않음 |
Reactive 전역 에러 처리
WebExceptionHandler
WebExceptionHandler는 reactive에서 에러 처리를 하기 위해 제공되는 인터페이스이다.
public interface WebExceptionHandler {
Mono<Void> handle(ServerWebExchange exchange, Throwable ex);
}
아래와 같은 구현체가 제공된다.
구현체 | 설명 |
ResponseStatusExceptionHandler | ResponseStatusException 유형에 대한 예외 처리 |
WebFluxResponseStatusExceptionHandler | @ResponseStatus 가 선언된 경우에 대한 예외처리. ResponseStatusExceptionHandler의 확장 |
현재는 2가지만 제공하고 있고 따로 구현하여 사용하는 핸들러는 없는 것 같다.
@ExceptionHandler 에러 처리 만들기
Servlet과 마찬가지로 @ControllerAdvice와 @ExceptionHandler를 사용하여 전역 처리가 가능하다.
@Controller
public class SimpleController {
// ...
@ExceptionHandler
public ResponseEntity<String> handle(IOException ex) {
// ...
}
}
또한 매개 변수와 반환 값도 동일하게 사용 가능하다.
다만 request body나 @ModelAttribute에 관련된 매개변수와 반환 값은 사용 불가능하다.
따라서 Servlet에서 사용 가능한 ModelAndView 같은 반환 값을 쓸 수 없다.
Spring Boot의 에러 처리
Spring Boot는 모든 오류를 적절한 방식으로 처리하여 /error로 매핑하는 전역 오류 페이지 등록을 제공한다.
또한 http 상태와 예외에 대한 적절한 메시지를 json으로 응답하거나 해당 내용을 html 형식으로 렌더링 하는 whitelabel 페이지 뷰를 제공한다.
제공되는 interface | |
servlet (webmvc) | org.springframework.boot.web.servlet.error.ErrorAttributes |
reacitve (webflux) | org.springframework.boot.web.reactive.error.ErrorAttributes |
두 인터페이스는 거의 유사하다.
// servlet ErrorAttributes
public interface ErrorAttributes {
Map<String, Object> getErrorAttributes(WebRequest webRequest,
boolean includeStackTrace);
Throwable getError(WebRequest webRequest);
}
// reactive ErrorAttributes
public interface ErrorAttributes {
Map<String, Object> getErrorAttributes(ServerRequest request,
boolean includeStackTrace);
Throwable getError(ServerRequest request);
void storeErrorInformation(Throwable error, ServerWebExchange exchange);
}
이 인터페이스를 통해 아래와 같은 구현체가 제공된다.
제공되는 구현체 | autoConfiguration 설정 | 구현체 사용 대상 | |
servlet (webmvc) | org.springframework.boot.web.servlet.error.DefaultErrorAttributes | ErrorMvcAutoConfiguration | BasicErrorController |
reacitve (webflux) | org.springframework.boot.web.reactive.error.DefaultErrorAttributes | ErrorWebFluxAutoConfiguration | DefaultErrorWebExceptionHandler |
Spring boot는 에러가 발생하면 각각 사용 대상에서 ErrorAttributes를 사용하여 에러를 처리하게 된다.
DefaultErrorAttributes 사용
스프링이 제공하는 DefaultErrorAttributes는 아래 항목들을 json이나 error view 페이지에 제공한다.
- timestamp - 에러 발생 시간
- status - 상태 코드
- error - 에러 이유
- exception - 에러 class 이름
- message - 에러 메시지
- errors - BindingResult 에러의 경우 ObjectErors
- trace - exception stack trace
- path - 에러 발생 url
json으로 응답되는 결과 페이지는 대략 아래와 같은 형태이다.
HTTP/1.1 500 Internal Server Error
{
"timestamp": 1412685688268,
"status": 500,
"error": "Internal Server Error",
"exception": "com.example.CustomException",
"message": null,
"path": "/example"
}
Custom ErrorAttributes 만들기
boot에서는 Servlet과 Reactive를 되도록 유사한 형태로 사용할 수 있도록 ErrorAttributes interface를 제공하기 때문에 아래 예제를 interface만 servlet/reactive 구별하여 상속하면 된다.
@Component
public class TestErrorAttributes implements ErrorAttributes {
private static final String ERROR_ATTRIBUTE = TestErrorAttributes.class.getName() + ".ERROR";
@Override
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap<>();
errorAttributes.put("timestamp", new Date());
errorAttributes.put("path", request.path());
errorAttributes.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
return errorAttributes;
}
@Override
public Throwable getError(ServerRequest request) {
return (Throwable) request.attribute(ERROR_ATTRIBUTE)
.orElseThrow(() -> new IllegalStateException(
"Missing exception attribute in ServerWebExchange"));
}
// storeErrorInfomation은 reactive에서만 구현하는 method
@Override
public void storeErrorInformation(Throwable error, ServerWebExchange exchange) {
exchange.getAttributes().putIfAbsent(ERROR_ATTRIBUTE, error);
}
}
위와 같이 custom ErrorAttributes를 선언하면 아래처럼 에러 결과가 변경된다.
HTTP/1.1 500 Internal Server Error
{
"timestamp": 1412685688268,
"path": "/example"
}
원하는 대로 에러 응답 결과를 바꿀 수 있다.
주의해야 할 점은 errorAttributes에 status란 키 값은 필수로 저장해야 한다.
'til > Server' 카테고리의 다른 글
Ajax 서버 응답 처리 (0) | 2022.07.05 |
---|---|
Ajax, Asynchronous JavaScript and XML (0) | 2022.07.05 |
[JSP] 로그인 구현 (0) | 2022.06.28 |
[JSP] 게시판 설계 (0) | 2022.06.28 |
[JSP] 메모장 (0) | 2022.06.28 |