일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- 상암동
- 아일랜드리솜
- spring
- 고기김치
- @ControllerAdvice
- 서오릉맛집
- 한우오마카세
- 상암동맛집
- 닭껍질만두
- EffectiveJava
- 인생소고기집
- 맛집
- 토비의스프링
- 압구정로데오맛집
- JUnit5
- 데이트
- 셀렉티
- @ExceptionHandler
- Java
- 데이트코스
- 압구정곱떡
- 청춘면가
- 이속우화
- 녹는다녹아
- 인생맛집
- 이펙티브자바
- 서오릉
- 앤드밀
- 경기족발
- exceptionHandler
- Today
- Total
Hyeonuk_.log
[Spring] @ExceptionHandler @ControllerAdvice 본문
오류처리를 어떻게 해야할까?라는 의문의 들때가 많았다. 재직하고 있는 회사에서 맡은 업무에서는 어떤 시스템은 응답코드로 분기를 하여 처리를 했다. 얼마전 읽은 '클린코드'에서는 응답코드 대신 try catch를 이용하여 Exception을 던지라고 되어 있었다. 이것 저것 찾아보던 도중 @ExceptionHandler, @ControllerAdvice에 대해 공부하게 된 내용을 남긴다.
오류코드보다 예외를 사용하라
-클린코드-
순서는 아래와 같다.
1. @ExceptionHandler
2. @ControllerAdvice
1. @ExceptionHandler
@Controller
public class MyController {
...
...
@ExceptionHandler(NullPointerException.class)
public Object nullEx(Exception e) {
log.error("NullPointException!!", e);
return "npe";
}
}
@ExceptionHandler같은 경우는 @Controller, @RestController가 적용된 Bean내에서 발생하는 예외를 잡아서 하나의 메서드에서 처리해주는 기능을 한다.
위의 예제에서 처럼하면 MyController에 해당하는 Bean에서 NullPointerException이 발생한다면, @ExceptionHandler(NullPointerException.class)가 적용된 메서드가 호출될 것이다.
주의해야할 사항들은 아래와 같다.
1) Controller, RestController에만 적용가능하다. (@Service같은 빈에서는 안됨.)
2) 리턴 타입은 자유롭게 해도 된다. (Controller내부에 있는 메서드들은 여러 타입의 response를 할 것이다. 해당 타입과 전혀다른 리턴 타입이어도 상관없다.)
3) @ExceptionHandler를 등록한 Controller에만 적용된다. 다른 Controller에서 NullPointerException이 발생하더라도 예외를 처리할 수 없다.
4) 메서드의 파라미터로 Exception을 받아왔는데 이것 또한 자유롭게 받아와도 된다.
2. @ControllerAdvice
@ExceptionHandler가 하나의 클래스에 대한 것이라면, @ControllerAdvice는 모든 @Controller 즉, 전역에서 발생할 수 있는 예외를 잡아 처리해주는 annotation이다. 공부를 하다 아래의 글을 보았는데 아주 동의한다.
@ControllerAdvice 어노테이션으로 모든 예외를 한 곳에서 처리할 수 있습니다. 해당 코드의 세부적인 것은 중요하지 않으며 가장 기본적이며 필수적으로 처리하는 코드입니다. 추가로 스프링 및 라이브러리 등 자체적으로 발생하는 예외는 @ExceptionHandler 으로 추가해서 적절한 Error Response를 만들고 비즈니스 요구사항에 예외일 경우 BusinessException 으로 통일성 있게 처리하는 것을 목표로 한다. 추가로 늘어날 수는 있겠지만 그 개수를 최소한으로 하는 노력이 필요합니다.(https://cheese10yun.github.io/spring-guide-exception/)
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* javax.validation.Valid or @Validated 으로 binding error 발생시 발생한다.
* HttpMessageConverter 에서 등록한 HttpMessageConverter binding 못할경우 발생
* 주로 @RequestBody, @RequestPart 어노테이션에서 발생
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error("handleMethodArgumentNotValidException", e);
final ErrorResponse response = ErrorResponse.of(ErrorCode.INVALID_INPUT_VALUE, e.getBindingResult());
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
/**
* @ModelAttribut 으로 binding error 발생시 BindException 발생한다.
* ref https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-modelattrib-method-args
*/
@ExceptionHandler(BindException.class)
protected ResponseEntity<ErrorResponse> handleBindException(BindException e) {
log.error("handleBindException", e);
final ErrorResponse response = ErrorResponse.of(ErrorCode.INVALID_INPUT_VALUE, e.getBindingResult());
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
/**
* enum type 일치하지 않아 binding 못할 경우 발생
* 주로 @RequestParam enum으로 binding 못했을 경우 발생
*/
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
protected ResponseEntity<ErrorResponse> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
log.error("handleMethodArgumentTypeMismatchException", e);
final ErrorResponse response = ErrorResponse.of(e);
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
/**
* 지원하지 않은 HTTP method 호출 할 경우 발생
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
protected ResponseEntity<ErrorResponse> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
log.error("handleHttpRequestMethodNotSupportedException", e);
final ErrorResponse response = ErrorResponse.of(ErrorCode.METHOD_NOT_ALLOWED);
return new ResponseEntity<>(response, HttpStatus.METHOD_NOT_ALLOWED);
}
/**
* Authentication 객체가 필요한 권한을 보유하지 않은 경우 발생합
*/
@ExceptionHandler(AccessDeniedException.class)
protected ResponseEntity<ErrorResponse> handleAccessDeniedException(AccessDeniedException e) {
log.error("handleAccessDeniedException", e);
final ErrorResponse response = ErrorResponse.of(ErrorCode.HANDLE_ACCESS_DENIED);
return new ResponseEntity<>(response, HttpStatus.valueOf(ErrorCode.HANDLE_ACCESS_DENIED.getStatus()));
}
@ExceptionHandler(BusinessException.class)
protected ResponseEntity<ErrorResponse> handleBusinessException(final BusinessException e) {
log.error("handleEntityNotFoundException", e);
final ErrorCode errorCode = e.getErrorCode();
final ErrorResponse response = ErrorResponse.of(errorCode);
return new ResponseEntity<>(response, HttpStatus.valueOf(errorCode.getStatus()));
}
@ExceptionHandler(Exception.class)
protected ResponseEntity<ErrorResponse> handleException(Exception e) {
log.error("handleEntityNotFoundException", e);
final ErrorResponse response = ErrorResponse.of(ErrorCode.INTERNAL_SERVER_ERROR);
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
다음에 공부하고 포스팅 해야할 것들
1. Spring MVC의 에러 처리
2. 위 2가지의 동작 원리
'Dev_.log > Spring' 카테고리의 다른 글
토비의 스프링 읽다가 템플릿/콜백 기록 (0) | 2022.03.06 |
---|---|
[Junit5] @BeforeAll를 사용하다...멘붕...(토비스프링 읽다가) (0) | 2022.03.06 |
[Spring] @Service 단위테스트 (0) | 2022.02.21 |
[Spring] RestTemplate 업무 사용할 때 알아야할 점, 주의할 점 (0) | 2022.02.19 |
[Spring] @ControllerAdvice는 어떻게 동작하는 것일까? (0) | 2022.02.18 |