Spring 예외 처리
Java스럽게 예외처리를 한다면 try catch를 이용해야한다.
모든 예외를 Controller에 throw하고 Controller에서 처리한다.
ex) 이메일 속성이 유니크 설정을 가지고 있을 때
회원가입 시 유저가 이미 DB에 있는 이메일을 입력한다면 예외가 발생한다.
signup 매소드에 throw를 붙이고, Controller 부분에서 예외를 처리한다.
그렇다면 모든 매소드에 throw를 붙이고 처리한다면 너무 번거롭다.
게다가 위의 방법은 본인이 직접 짠 서비스 부분만 예외처리를 할 수 있다.
그럼 Spring의 filter와 dispatcher servlet의 예외는 어떻게 처리해야할까?
위의 클라스를 상속 받아서 매소드들 구현한 다음 예외처리를 하는 것이다.
너무 번거롭지 않은가?
그래서 모든 기능들이 공통적으로 예외처리 라는 기능을 가지고 있다는 것.
이것을 해결하기 위해 AOP를 생각하게 되었다.
AOP(관점 지향 프로그래밍)는 모든 매소드에 예외처리를 추가해야할 때, 써볼 수 있다.
근데 꼭 예외처리에만 쓰이는 건 아닌 점을 명심하자
| AOP 주요 개념
- Aspect : 위에서 설명한 흩어진 관심사를 모듈화 한 것. 주로 부가기능을 모듈화함.
- Target : Aspect를 적용하는 곳 (클래스, 메서드 .. )
- Advice : 실질적으로 어떤 일을 해야할 지에 대한 것, 실질적인 부가기능을 담은 구현체
- JointPoint : Advice가 적용될 위치, 끼어들 수 있는 지점. 메서드 진입 지점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용가능
- PointCut : JointPoint의 상세한 스펙을 정의한 것. 'A란 메서드의 진입 시점에 호출할 것'과 같이 더욱 구체적으로 Advice가 실행될 지점을 정할 수 있음
| 라이브러리 다운로드
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> <scope>test</scope> </dependency>
Spring Aop annotation 정리
@Aspect : 관심사 객체 하나를 만든다고 생각하면 됨.
타겟 메서드의 Aspect 실행 시점을 지정할 수 있는 어노테이션이 있다.
@Around (메소드 실행 전후) : 어드바이스가 타겟 메소드를 감싸서 타겟 메소드 호출전과 후에 어드바이스 기능을 수행
@Before (이전) : 어드바이스 타겟 메소드가 호출되기 전에 어드바이스 기능을 수행
@After (이후) : 타겟 메소드의 결과에 관계없이(즉 성공, 예외 관계없이) 타겟 메소드가 완료 되면 어드바이스 기능을 수행
@AfterReturning (정상적 반환 이후)타겟 메소드가 성공적으로 결과값을 반환 후에 어드바이스 기능을 수행
@AfterThrowing (예외 발생 이후) : 타겟 메소드가 수행 중 예외를 던지게 되면 어드바이스 기능을 수행
//입력값 검증하면서 나오는 에러들을 예외 처리해보자.
이건 Spring web 에 있는 AOP 예외 처리 방식
@RestControllerAdvice // Advice 객체 생성
public class GlobalExceptionAdvise extends ResponseEntityExceptionHandler {
//ResponseEntityExceptionHandler를 상속받은 이유 : 내가 모든 예외를 다 처리할 수 없다. = 귀찮다.
//각종 예외를 처리하는 클래스. 자주 발생하는 에러들의 예외를 처리해둔 클래스이다.
//특별한 예외처리를 하고 싶으면 오버라이드를 통해 처리하면된다.
//예외 처리할 에러를 특정할 수 있는 어노테이션
@ExceptionHandler(SQLIntegrityConstraintViolationException.class) //발생하는 예외를 적는다.
public ResponseEntity errorHandler(SQLIntegrityConstraintViolationException e) { //매개변수로 처리할 예외를 받는다.
e.printStackTrace();
return makeResponseEntity(ErrorCode.DUPLICATED_USER);
}
//ResponseEntityExceptionHandler 에 있는 에러를 오버라이딩하여 처리하는 방법도 있음.
@Override
protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
log.error(String.format("잘못된 입력입니다."));
return makeResponseEntity(ErrorCode.INVALID_INPUT);
}