프로젝트를 진행하면서 예외를 어떻게 관리할지 결정하는 것이 중요하고 어려운 단계인 지 알게 되었다..😢
AOP 란 ?
AOP는 Aspect Oriented Programming 의 약자로 OOP의 단점을 보완하기 위한 프로그래밍 방식이다. 간략하게 설명하면 여러 곳에서 사용되는 공통기능을 모듈화하고, 필요할 때 연결함으로써 유지보수, 재사용성에 용이하도록 프로그래밍 하는 것을 AOP 라고 한다.
@ExceptionHandler 는 Spring 에서 발생한 Exception을 기반으로 오류를 처리 할 수 있다. 하나의 클래스(컨트롤러 등)의 메서드에 추가되어 해당 클래스의 ExceptionHandler 로써 동작한다. 그렇기 때문에 컨트롤러가 늘어나면 중복코드가 늘어나는 방식이다.
그래서 모든 클래스에서 발생하는 예외를 처리 하고 싶을 때, 사용할 수 있는 개념이 AOP 이다. @ControllerAdvice 과 @RestControllerAdvice 2가지 종류가 있는데, @ControllerAdvice 어노테이션은 모든 컨트롤러에서 발생 할 수 있는 예외를 해당 어노테이션을 추가한 클래스에 존재하는 Exception Handler 로 처리 할 수 있도록 해준다. @RestControllerAdvice 는 @Controller + @ResponseBody 어노테이션이 추가된 어노테이션으로 @RestController + @ControllerAdvice 의 동일한 효과를 볼 수 있다.
AOP + ExceptionHandler 를 알기까지..
상황 : 응답코드가 "0000" 이 아닌 경우에는 화면에서 400 에러로 받으면서 오류내용을 표시해야 하고, 백에서는 해당 오류내용을 로그테이블에 저장시키고 싶음.
response = 외부 api 와의 통신 후 응답 받은 값.
// 통신은 성공하였지만 실패한 상황
if(!"0000".equals(response.resultCd)){
throw new CustomizeException(400, [오류내용]);
}
그런데 throw 하는 순간 트랜잭션이 롤백 되면서 저장이 되지 않는 상황이 발생함.. 😱
그렇게 Exception 과 트랜잭션에 대해서 알아보게 됨.
📕 Excption과 Trasaction 의 롤백!
@Transactional 어노테이션을 통해 트랜잭션을 선언 한 상태.
| UnChecked Exception | Checked Exception |
| 롤백 됨. | 롤백 안됨. |
| RuntimeExeption 을 상속받은 Exception. 실행 중에 발생할 수 있는 예외를 의미. ex)NullPointerException, IndexOutOfBoundesException 등 |
ComplileException 이라고 불림. Exception 을 상속 받음. 컴파일 시점에서 예외에 대한 처리 (try-catch 또는 throw) 를 하지 않는 다면 컴파일 에러가 발생 ex) FileNotFoundExcetion 등 |
※ 아래 블로그를 참고하였음.
위의 코드에서 CustomizeException 은 커스터마이징한 Exception 이며, RuntimeException 을 상속받고 있는 형태이기 때문에 롤백이 될 수 밖에 없는 상황이었다.
이미 개발되어 있는 구조를 바꿀 수 없는 상황이고 새로운 Exception 을 추가할까 고민에 빠지게 되는데... Exception이 발생 하게 되면 해당 오류시점에 catch 되는 설정이 없을까에 대해 엄청 검색하게 됨.
그러다가 ExceptionHandler 에 대해 알게됐음!! 🙄
AOP + ExceptionHandler
@ExceptionHandler : @Controller , @RestController 가 적용된 Bean 내에서 발생한 예외를 받아서 처리 할 수 있는 기능을 한다.
@ExceptionHandler(CustomizeException.class)
public ResponseEntity<ExceptionResponse> handleCustomizeException(CustomizeException e, WebRequest request) {
return new ResponseEntity<>(e, HttpStatus.BAD_REQUEST);
}
ExceptionHandler 에는 value 속성을 가지고 있다. 때에 따라 특정 예외만 처리하고 싶은 경우에
@ExceptionHandler(CustomizeException.class) 와 같이 처리할 수 있다. (value 는 생략 가능!)
만약에 두가지 이상의 예외를 공통으로 처리 하고 싶으면,
@ExceptionHandler(value = { NullPointerException.class, ArithmeticException.class }) 이런식으로 처리 할 수 있다.
🙄 Controller 에서 작성하는 방법
@Controller
public class TestController {
...
@ExceptionHandler(NullPointerException.class)
public ResponseEntity handleNullPointerException(NullPointerException ne) {
...
}
@ExceptionHandler(ArithmeticException.class)
public ResponseEntity handleArithmeticException(ArithmeticException ae) {
...
}
}
위와 같이 Controller 에서 예외처리를 하게 되면 컨트롤러가 생성 될 때마다 @ExceptionHandler 코드를 넣어줘야 하기 때문에 중복코드발생, 유지보수힘듬 이라는 단점이 있다.
이러한 단점을 해결하기 위해 AOP 방식으로 Exception 을 처리 할 수 있는 것이다.
그 때는 상단에 @ControllerAdvice 또는 @RestControllerAdvice 어노테이션을 사용해야 한다.
🙄 @ControllerAdvice 의 속성
// 패키지를 지정하고 싶은 경우
@ControllerAdvice(value = "test.package")
public class TestControllerAdvice {
...
}
@ControllerAdvice(basePackages = "test.package")
public class TestControllerAdvice {
...
}
// 특정클래스 기준으로 패키지를 지정하고 싶은 경우
@ControllerAdvice(basePackageClasses = TestController.class)
public class TestControllerAdvice {
...
}
// 특정클래스를 지정하고 싶은 경우
@ControllerAdvice(assignableTypes = TestController.class)
public class TestControllerAdvice {
...
}
// 어노테이션을 지정하고 싶은 경우
@ControllerAdvice(annotations = Controller.class)
public class TestControllerAdvice {
...
}
🙄 @RestControllerAdvice 의 속성
@RestControllerAdvice 는 @ControllerAdvice 와 @ResponseBody 를 합친 어노테이션이다. @ControllerAdvice 와 동일한 기능을 하지만 예외를 body 에 담아 반환할 수 있다.
@ControllerAdvice
public class TestControllerAdvice {
@ExceptionHandler(NullPointerException.class)
public String handleNullPointerException(NullPointerException ne) {
return "Body Data";
}
}
@RestControllerAdvice
public class TestControllerAdvice {
@ExceptionHandler(NullPointerException.class)
public String handleNullPointerException(NullPointerException ne) {
return "Body Data";
}
}
확인해 보면 @RestContollerAdvice 는 "Body Data" 를 응답하는 것을 확인 할 수 있지만, @ControllerAdvice는 정형화된 포맷으로 응답하는 것을 확인 할 수 있을 것이다.
'Java > AOP' 카테고리의 다른 글
| HttpServletRequestWrapper 에 대해서 ( with. ExceptionHandler) (0) | 2024.08.22 |
|---|---|
| AOP의 @Aspect 에 대해서 (0) | 2024.08.21 |