AOP
AOP는 Aspect Oriented Programming의 약자로 관점 지향 프로그래밍이라고 불린다. 관점 지향은 쉽게 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화 하는 것이라고 이해할 수 있다. 여기서 모듈화란 어떤 공통된 로직이나 기능을 하나의 단위로 묶는 것을 말한다.
Exception같은 전체 로직 어디에서든지 동작할 수 있는 기능을 하나로 묶어 전역으로 처리하는 것으로 쉽게 이해할 수 있겠다.
@ExceptionHandler
@ExceptionHandler 어노테이션을 메서드에 선언하고 특정 예외 클래스를 지정해 주면해당 예외가 발생했을 때 메서드에 정의한 로직을 처리를 할 수 있다.
즉, 처리하고 싶은 Exception을 정의한 다음 해당 예외가 발생했을 때 처리하고 싶은 방법으로 처리할 수 있다는 것이다.
@ExceptionHandler는 @Controller 가 적용된 Bean에서 발생하는 예외를 잡아 하나의 메서드에서 처리하는 기능을 수행한다.
@Controller
public class ExampleController {
@ExceptionHandler(value = RuntimeException.class)
public ResponseEntity<String> exceptionHanble(IOException ex) {
// ...
}
public ResponseEntity<String> notExceptionHanble(IOException ex) {
// ...
}
}
즉, 위와 같은 상황에서 exceptionHanble 메서드에서 발생한 RuntimeException에 대해서는 @ExceptionHandler가 동작하지만, notExceptionHanble에서는 RuntimeException이 발생해도 @ExceptionHandler가 동작하지 않는다.
따라서 컨트롤러마다 중복된 @ExceptionHandler를 정의해 주어야 모든 에러에 대한 커버가 가능해진다.
@ControllerAdvice
위와 같은 이유로 @ControllerAdvice를 사용한다.
@ControllerAdivice는 @Controller 어노테이션이 적용된 모든 곳에서의 발생되는 예외에 대해 Catch 한다.
@ControllerAdvice가 적용된 클래스에 정의되어 있는 @ExceptionHandler는 모든 컨트롤러에서 발생하는 예외에 대해서 동작하게 된다.
@ControllerAdvice
public class ExampleExceptionHandler {
@ExceptionHandler(value = CustomException.class)
public ResponseEntity<String> customExceptionHanble(CustomException ex) {
// ...
}
@ExceptionHandler(value = BindException.class)
public ResponseEntity<String> BindExceptionHanble(BindException ex) {
// ...
}
}
이렇게 정의하면 모든 Controller에서 발생하는 CustomException과 BindException에 대해서 해당 메서드가 실행되는 것이다.
또한, 이용해서 원하는 컨트롤러나 패키지만 선택할 수도 있다.
@RestControllerAdvice(basePackageClasses = MembersPostController.class)
@RestControllerAdvice(basePackages = "com.mogakko.be_final.domain")
이렇게 하위 로직에서 예외가 발생하게 되면, 상위로 타고 올라가 Controller까지 왔을 때 @ControllerAdvice이 에러를 잡아서 @ExceptionHandler에 선언된 Exception 인지 체크한다.
@RestControllerAdvice
@RestControllerAdvie는 @ControllerAdvice + @ResponseBody라고 할 수 있다.


따라서 @RestControllerAdvie로 선언하면 따로 @ResponseBody를 붙여주지 않아도 객체를 리턴할 수 있다.
에러 메시지를 DTO에 담아 리턴해 주는 방식으로 사용할 수 있다.
활용
@Getter
@AllArgsConstructor
public class CustomException extends RuntimeException {
private final ErrorCode errorCode;
}
최근에 진행했던 프로젝트에서 활용했었던 CustomException을 예로 들어 설명하겠다.
@Getter
@AllArgsConstructor
public enum ErrorCode {
PLZ_INPUT_REASON_OF_REPORT(HttpStatus.BAD_REQUEST, "신고 이유를 입력해주세요.");
private final HttpStatus httpStatus;
private final String data;
}
throw new CustomException(INVALID_EMAIL);
이런 식으로 예외가 발생했을 때,
@Getter
@Builder
public class ErrorResponse {
private String message;
private String data;
// 에러 반환 형식
public static ResponseEntity<ErrorResponse> toResponseEntity(ErrorCode errorCode) {
return ResponseEntity
.status(errorCode.getHttpStatus())
.body(ErrorResponse.builder()
.message(errorCode.getData())
.build()
);
}
}
정의한 DTO클래스에 따라서,
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = {CustomException.class})
public ResponseEntity<ErrorResponse> handleCustomException(CustomException e) {
return ErrorResponse.toResponseEntity(e.getErrorCode());
}
}
@Controller 빈에서 던져지는 에러에 대해 전역적으로 처리해 줄 @ExceptionHandler를 @ControllerAdivce 혹은 @RestControllerAdivce가 정의된 클래스에 있는 CustomException Hanbler에 의해서 처리되는 것이다.
{
"message": "신고 이유를 선택해주세요.",
"data": null
}
정의한 DTO 클래스의 형식에 따라 리턴되는 것을 볼 수 있다.
'Spring' 카테고리의 다른 글
@SpringBootTest / @WebMvcTest (0) | 2023.07.25 |
---|---|
Spring Rest Client (0) | 2023.07.25 |
스프링 컨테이너 (Spring Container) (0) | 2023.07.24 |
의존성 주입(Dependency Injection, DI) (0) | 2023.07.23 |