항시 프로젝트를 할 때 예외 처리가 중요하다고 생각하여 예외를 커스텀하여 ControllerAdvice를 통해 전역 처리를 했었다.
그런데 그 메커니즘에 대해서는 궁금해하지 않았던 것 같아,
이번에는 Spring이 예외를 처리하는 메커니즘에 대해서 순서도와 같이 정리를 해보려한다.
Spring 의 예외 처리 프로세스
보이는 대로 해석을 해보자면,
① @ExceptionHandler 가 같은 Controller에 존재하는가 ?
- Yes : 예외는 @ExceptionHandler 로 처리된다.
- No : ② 로 이동
② @ExceptionHandler 가 @ControllerAdvice 클래스에 존재하는가 ?
- Yes : 예외는 @ExceptionHandler 로 처리된다.
- No : ③ 로 이동
③ 예외에 @ResponseStatus 가 있나 ?
- Yes : 예외는 스프링의 'ResponseStatusExceptionResolver' 로 처리된다.
- No : 예외는 스프링의 'DefaultHandlerExceptionResolver' 로 처리된다.
글로는 잘 와닿지않아 코드를 통해 직접 확인해보았다.
# 아래에서 사용할 테스트 API
1. MemberController
@RestController
@Slf4j
public class MemberController {
@PostMapping("/new")
public ResponseEntity<?> createMember(@Valid MemberRequest memberRequest){
..
}
}
createMember 메서드의 MemberRequest에 email과 password에 대한 유효성 검증을 설정해 둔 상태이다.
email은 xxxx@도메인의 형식이어야 하고, password는 6자이상 12자 미만이어야 한다.
2. Request Sample
Postman을 통해 유효성 검증에 실패할 데이터 전달.
1. @ExceptionHandler 로 예외 처리하기
- @Exceptionhandler
..
public class MemberController {
..
@ExceptionHandler(BindException.class) // Validation에 대한 예외 처리
protected ResponseEntity<?> handleException(BindException e) {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(e.getMessage());
}
}
유효성 검증에 실패하면,
BindException 에러가 발생하고 이에 대한 예외 처리를 같은 클래스의 @ExceptionHandler 로 처리한다.
- 실행 결과
보이는 바와 같이, ExceptionHandlerExceptionResolver에서 처리한 것을 확인할 수 있다. ( @ExceptionHandler )
2. @ControllerAdvice 로 예외 처리하기
- @ControllerAdvice
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BindException.class)
protected ResponseEntity<?> handleException(BindException e) {
log.error("###### 예외 처리 ######");
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(e.getMessage());
}
}
나는 현재, Rest 방식으로 테스트하고 있기 때문에 @RestControllerAdvice를 적용했다.
폼이 있는 경우 @ControllerAdvice로 하면 된다.
- 실행 결과
보이는 바와 같이, GlobalEceptionHandler에서 예외를 캐치하여 ExceptionhandlerExceptionResolver로 처리되었음을 확인할 수 있다. ( @ExceptionHandler )
3. @ResponseStatus 로 예외 처리하기
- @ResponseStatus
@ResponseStatus(HttpStatus.BAD_REQUEST)
public class MemberValidException extends RuntimeException {
..
}
@ResponseStatus 는 이 처럼 예외 클래스를 커스텀하여 상태코드를 지정할 수 있다.
그런데 이렇게 쓰기보다는 @ExceptionHandler에 같이 주석으로 사용하는 편이 더 유용한 것 같다.
- 실행 결과
보이는 바와 같이, ResponseStatusExceptionResolver로 처리되었음을 확인할 수 있다.
정리
@ControllerAdvice를 통해 전역처리를 할 Controller 생성 후 개별 예외에 때한 ExceptionHandler를 구현하는 것이 제일 가독성이 좋아보인다. 필요에 따라 @ResponseStatus 로 상태 코드를 정의하는 것으로 한다.
순서도에 대해서는 한 번 살펴보는 것이 좋을 것 같다.
참고
https://reflectoring.io/spring-boot-exception-handling/
'Web' 카테고리의 다른 글
[Spring] MapStruct 사용하기 (0) | 2023.07.08 |
---|---|
[Spring] 라이브러리 버전 관리하기 ( Gradle ) (0) | 2023.07.06 |
[Spring] @Builder 와 Builder 패턴 (0) | 2023.06.25 |
[Spring] @Email 과 이메일 정규식 표현 (0) | 2023.06.24 |
[Spring] Gradle 과 Maven 에 대해서 (0) | 2023.06.22 |