1. 전역 예외 처리
Spring은 전역적으로 ExceptionHandler를 적용할 수 있는 @ControllerAdvice와 @RestControllerAdvice annotation을 제공
2. ControllerAdvice
@ControllerAdivce는 여러 컨트롤러에 대해 전역적으로 ExceptionHandler를 적용
에러를 핸들링하는 클래스를 만들어 어노테이션을 붙여주면 에러 처리를 위임하게 됨
만약 특정 클래스에만 제한적으로 적용하고 싶다면, @RestControllerAdvice의 basePackages 등을 설정함으로써 제한이 가능
@RestControllerAdvice는 @ControllerAdvice와 달리 @ResponseBody가 붙어 있어 응답을 Json으로 반환
2.1 ControllerAdvice 장점
하나의 클래스로 모든 컨트롤러에 대해 전역적으로 예외 처리가 가능
직접 정의한 에러 응답을 일관성 있게 클라이언트에게 반환 가능
별도의 try-catch문이 없어 코드의 가독성이 높아짐
2.2 ControllerAdvice 단점
한 프로젝트당 하나의 ControllerAdivce만 관리하는 것이 좋음
만약 여러 ControllerAdvice가 필요하다면 basePackages나 annotations 등을 지정해야 함
직접 구현한 Exception 클래스들은 한 공간에서 관리
3. ResponseEntityExceptionHandler 추상 클래스
Spring은 스프링 예외를 미리 처리해 둔 ResponseEntityExceptionHandler를 추상 클래스로 제공
ResponseEntityExceptionHandler에는 스프링 예외에 대한 ExceptionHandler가 모두 구현되어 있으므로 @ControllerAdvice 클래스가 이를 상속해서 사용하면 됨
이 추상 클래스를 상속받지 않는다면 스프링 예외들은 DefaultHandlerExceptionResolver가 처리하게 되는데, 그러면 예외 처리기가 달라지므로 클라이언트가 일관되지 못한 에러 응답을 받지 못하므로 ResponseEntityExceptionHandler를 상속시키는 것이 좋음
@RestControllerAdvice
public class ExceptionAdvice extends ResponseEntityExceptionHandler {
// NotFoundAccountException 발생시 에러 처리
@ExceptionHandler(NotFoundAccountException.class)
public ResponseEntity<?> handleNotFoundEntity(NotFoundException e) {
return handleExceptionInternal(e.getExceptionCode());
}
}
3.1 handleExceptionInternal()
ResponseEntityExceptionHandler의 handleExceptionInternal() 메소드를 오버라이딩하여 응답을 customizing 가능
4. 예외 처리 흐름
- ExceptionHandlerExceptionResolver: 에러 응답을 위한 Controller나 ControllerAdvice에 있는 ExceptionHandler를 처리함
- 예외가 발생한 컨트롤러 안에 적합한 @ExceptionHandler가 있는지 검사
- 컨트롤러의 @ExceptionHandler에서 처리가 가능하다면 처리하고, 그렇지 않으면 ControllerAdivce로 넘어감
- ControllerAdvice 안에 적합한 @ExceptionHandler가 있는지 검사하고, 없으면 다음 처리기로 넘어감
- ResponseStatusExceptionResolver: Http 상태 코드를 지정하는 @ResponseStatus 또는 ResponseStatusException를 처리함
- @ResponseStatus가 있는지 또는 ResponseStatusException인지 검사
- 맞으면 ServletResponse의 sendError()로 예외를 서블릿까지 전달하고, 서블릿이 BasicErrorController로 요청을 전달
- DefaultHandlerExceptionResolver: 스프링 내부의 기본 예외들을 처리
- Spring의 내부 예외인지 검사하여, 맞으면 에러를 처리하고 아니면 넘어감
- 적합한 ExceptionResolver가 없으므로 예외가 서블릿까지 전달되고, 서블릿은 SpringBoot가 진행한 자동 설정에 맞게 BasicErrorController로 요청을 다시 전달
5. 적용 예시
@Slf4j
@RestControllerAdvice(annotations = {RestController.class})
public class ExceptionAdvice extends ResponseEntityExceptionHandler {
@ExceptionHandler(value = {GlobalException.class})
protected ResponseEntity<ErrorResponse> handleGlobalException(GlobalException e) {
log.error("{}: {}", e.getGlobalErrorCode(), e.getMessage());
return handleExceptionInternal(e.getGlobalErrorCode());
}
private ResponseEntity<ErrorResponse> handleExceptionInternal(GlobalErrorCode globalErrorCode) {
return ResponseEntity.status(globalErrorCode.getHttpStatus().value())
.body(new ErrorResponse(globalErrorCode));
}
}
@Getter
public class ErrorResponse {
private final LocalDateTime timestamp = LocalDateTime.now();
private final Boolean isSuccess;
private final String code;
private final String message;
public ErrorResponse(GlobalErrorCode globalErrorCode) {
this.isSuccess = false;
this.code = globalErrorCode.getCode();
this.message = globalErrorCode.getMessage();
}
}
'Dev > SpringBoot' 카테고리의 다른 글
카카오 로그인 구현하기(1) - 카카오 로그인 (0) | 2025.02.10 |
---|---|
SpotlessApply 및 Pre-Commit (0) | 2025.02.10 |
Redis를 이용하여 Write Back 구현하기 (0) | 2025.02.10 |
SpringBoot에서 Redis 연동하기 (0) | 2025.02.10 |
주기적인 작업 자동화 구축 (0) | 2025.02.10 |