공통된 예외 처리에 대한 코드가 자주 있다면
이를 통일 시켜서 코드를 줄일 수 있는 방법이 무엇일까?
Spring에서 제공하는 기능
--
Spring에서는 모든 @Controller 클래스에 대해 처리할 수 있도록 제공하는 기능이 있다.
- @ControllerAdvice
- @RestControllerAdvice
@ControllerAdvice는
Spring MVC 패턴일 때 주로 사용되며 주로 HTML와 같은 View를 응답한다.
@RestControllerAdvice는
@ControllerAdvice + @ResponseBody로
RESTful API에서 주로 사용되며 주로 JSON 또는 XML과 같은 데이터를 응답한다.
그리고 Spring에서 예외를 처리하는 데 사용되는 기능이 있다.
- @ExceptionHandler
@ExceptionHandler는
특정 예외에 대해 처리할 수 있도록 도와주는 어노테이션으로
해당 예외가 발생하면 Spring은 @ExceptionHandler로 지정된 메서드를 호출하여 해당 예외에 대해 처리하게 된다.
위의 두 기능을 이용하면
모든 컨트롤러에서 발생하는 특정 예외에 대해 전역적으로 동일하게 처리할 수 있게 된다.
--
전역으로 공통된 예외 처리하기 적용하기
--
전역 예외 처리를 하는 방법은 두 가지 방법이 존재한다.
- ResponseEntityExceptionHandler 추상 클래스를 상속받는 방법
- ResponseEntityExceptionHandler 추상 클래스를 상속받지 않는 방법
ResponseEntityExceptionHandler 추상 클래스는
Spring 에서 기본적인 예외 처리를 만들어 놓아서 제공해주는 추상 클래스이다.
ResponseEntityExceptionHandler 추상 클래스를 상속 받아 사용하는 방법
GlobalExceptionHandler.java
@ControllerAdvice
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse("Internal Server Error", ex.getMessage());
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
이렇게 ResponseEntityExceptionHandler 추상 클래스에 존재하는 기존 메서드를 오버라이딩하여 사용할 수도 있고
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Bad Request: " + e.getMessage());
}
}
@ExceptionHandler 어노테이션을 이용하여 특정 예외에 대해 새로 예외 처리를 생성할 수도 있다.
ResponseEntityExceptionHandler 추상 클래스를 상속 받지 않고 사용하는 방법
GlobalExceptionHandler.java
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Bad Request: " + e.getMessage());
}
}
--
ResponseEntityExceptionHandler 추상 클래스 상속 여부에 대한 차이점
--
ResponseEntityExceptionHandler 추상 클래스를 상속 받으면
해당 추상 클래스에는 Spring이 기본적으로 제공하는 예외 처리 로직을 재사용할 수 있다는 것에는 편리할 수 있다.
하지만 기본으로 제공되는 예외 처리 로직이 현재 애플리케이션의 요구 사항과 일치하지 않을 수도 있어서
기본 로직을 오버라이드해야 하거나 커스텀을 해야하는 경우가 생긴다.
그리고 자유롭게 예외 처리를 확장 또는 변경이 어려울 수 있어서 유연성이 감소할 수 있다.
ResponseEntityExceptionHandler 추상 클래스를 상속 받지 않으면
자유롭게 예외 처리 로직을 설계하고 구현할 수 있어 유연성이 좋아 특정 상황에 맞게 로직을 작성할 수 있다.
다만 모든 예외 처리 로직을 직접 구현해야 하기 때문에 초기 기본 예외 처리 로직의 작성이 필요할 수 있다.
그리고 표준화된 방법이나 패턴이 없어서 그냥 막 작성하다 보면 유지보수성에 문제가 생길 수도 있다.
--
예외 처리에 대한 흐름
--
- 예외 발생시 먼저 해당 예외가 발생한 컨트롤러 내부에 적합한 @ExceptionHandler (예외 처리 로직)이 있는지 확인.
- 있다면 해당 위치에서 예외 처리를 진행하고 만약 없다면 @(Rest)ControllerAdvice로 넘어가서 적합한 @ExceptionHandler가 있는지 확인한다.
- 있다면 해당 위치에서 예외 처리를 진행하고 만약 없다면 다음 처리 방법으로 넘긴다.
--
전역 예외 처리에 대한 장단점
--
장점
- 각각의 컨트롤러나 서비스에서 예외 처리 로직을 반복해서 작성할 필요가 없어서 중복된 코드를 줄일 수 있다.
- 전역에 공통된 예외 처리 로직이 적용되므로 항상 일련된 형식의 응답을 생성하여 반환하게 된다.
- 예외 처리에 대한 로직들이 단일 위치에 작성되므로 관리하기 편리해진다.
단점
- 모든 예외를 일반적인 방식으로 처리하므로 특정 예외에 대해 세부적인 처리가 힘들어진다.
- 모든 예외를 일괄적으로 처리하기 때문에 해당 예외가 발생한 원인을 찾기 어려울 수 있다.
--
'Spring Boot' 카테고리의 다른 글
스케줄러(@Scheduled)를 이용하여 특정 로직을 자동으로 동작하게 하기 (0) | 2024.05.03 |
---|---|
validation을 이용하여 유효성 검사하기 [ feat. 회원가입 ] (0) | 2024.04.26 |
결제 API 포트원 (구 아임포트)로 결제 시스템 구현하기 (0) | 2024.04.23 |
JWT를 이용하여 로그인 구현하기 (+ Refresh Token ) (0) | 2024.04.21 |
Spring Security에 대해서 (0) | 2024.04.18 |