@Aspect - 어드바이스의 여러 종류

Drumj·2023년 2월 15일
0

오늘의 학습

지난 게시글에 이어서
어드바이스의 여러 종류에 대해서 알아보자.

어드바이스의 종류
@Around : 가장 강력한 어드바이스, 메서드 호출 전후에 수행
@Before : 조인 포인트 실행 이전에 실행
@AfterReturning : 조인 포인트가 정상 완료 후 실행
@AfterThrowing : 메서드가 예외를 던지는 경우 실행
@After : 조인 포인트가 정상 또는 예외에 관계없이 실행

이렇게 5가지의 종류가 있고 @Around를 가장 많이 사용한다고 한다.


이전 게시글에서 나오던 해당 코드를 뜯어서 알아보자

@Around("hello.aop.order.aop.Pointcuts.orderAndService()")
    public Object doTransaction(ProceedingJoinPoint joinPoint) throws Throwable {

        try {
            //@Before
            log.info("[트랜잭션 시작] {}", joinPoint.getSignature());

            //조인포인트 실행
            Object result = joinPoint.proceed();

            //@AfterReturning
            log.info("[트랜잭션 커밋] {}", joinPoint.getSignature());
            return result;
        } catch (Exception e) {
            //@AfterThrowing
            log.info("[트랜잭션 롤백] {}", joinPoint.getSignature());
            throw e;
        } finally {
            //@After
            log.info("[리소스 릴리즈] {}", joinPoint.getSignature());
        }
    }

요렇게 변한다.

@Before("hello.aop.order.aop.Pointcuts.orderAndService()")
public void doBefore(JoinPoint joinPoint) {
	log.info("[before] {}", joinPoint.getSignature());
	//조인포인트 직전까지만 개발. 끝나면 알아서 조인포인트를 실행한다.
}

@AfterReturning(value = "hello.aop.order.aop.Pointcuts.orderAndService()", returning = "result")
public void doReturn(JoinPoint joinPoint, Object result) {
	log.info("[return] {}, result ={}", joinPoint.getSignature(), result);
}

@AfterThrowing(value = "hello.aop.order.aop.Pointcuts.orderAndService()", throwing = "ex")
public void doThrowing(JoinPoint joinPoint, Exception ex) {
	log.info("[ex] {}, message ={}",ex);
}

@After(value = "hello.aop.order.aop.Pointcuts.orderAndService()")
public void doAfter(JoinPoint joinPoint) {
	log.info("[after] {}", joinPoint.getSignature());
}

@Before

조인 포인트 실행 전

@Around와 다르게 작업 흐름을 변경할 수 없다.
해당 애노테이션이 붙으면 메서드 종료시 자동으로 다음 타겟이 호출된다.
@Around가 있는 코드를 보면 joinPoint.proceed()가 있지만 여기서는 없는 걸 볼 수 있다.


@AfterReturning

메서드 실행이 정상적으로 반환될 때 실행
returning 속성에 사용된 이름은 어드바이스 메서드의 매개변수 이름과 일치해야 한다.
@Around와는 다르게 반환되는 객체를 변경할 수 없다! 단 반환 객체를 조작할 수는 있다고 한다.


@AfterThrowing

메서드 실행이 예외를 던져서 종료될 때 실행
throwing 속성에 사용된 이름은 어드바이스 메서드의 매개변수 이름과 일치해야 한다.


@After

메소드 실행이 종료되면 실행된다. try-catch의 finally를 생각하면 된다고 한다.
일반적으로 리소스를 해제하는데 사용한다.


@Around

모든 것의 신
메서드 실행 전후에 작업을 수행한다. 가장 강력한 어드바이스

  • 조인 포인트 실행 여부 선택 joinPoint.proceed()호출 여부 선택
  • 전달 값 변환 `joinPoint.proceed(args[])
  • 반환 값 변환
  • 예외 변환

어드바이스의 첫번째 파라미터는 ProceedingJoinPoint를 사용해야 한다.
proceed()를 통해 대상을 실행하고 여러번 실행할 수도 있다고 한다(재시도)


그럼 왜 @Around 외에 다른 어드바이스가 존재하는 걸까?

@Around가 있는 어드바이스는 반드시 joinPoint.proceed()를 호출해야하는데
실수로 호출하지 않으면 타겟이 호출되지 않는 치명적인 버그가 발생한다고 한다.

@Around("hello.aop.order.aop.Pointcuts.orderAndService()")
public void doBefore(ProceedingJoinPoint joinPoint) {
 log.info("[before] {}", joinPoint.getSignature());
 //proceed 없음
}

설명했듯이 이렇게 코드가 짜여있으면 버그가 발생한다. 하지만

@Before("hello.aop.order.aop.Pointcuts.orderAndService()")
public void doBefore(JoinPoint joinPoint) {
 log.info("[before] {}", joinPoint.getSignature());
}

이렇게 @Before를 사용하면 proceed를 호출하지 않아도 된다.

@Around가 가장 넓은 기능을 제공하지만 이처럼 proceed를 호출하지 않는 실수를 할 가능성이 있다. 이처럼 @Before@After 등을 사용하면 기능은 적지만 실수할 가능성이 낮아지고 코드가 단순해진다.
그리고 가장 중요한 코드를 작성한 의도가 명확해진다.

좋은 설계는 제약이 있는 것이다.
제약은 실수를 미연에 방지하고 일종의 가이드 역할을 한다.
제약 덕분에 역할이 명확해지고 다른 개발자도 이 코드를 보고 고민해야 하는 범위가 줄어들고 코드의 의도도 파악하기 쉽다.


마무리

@Aspect와 관련된 기능들을 알아봤다.
대충 AOP가 어떻게 흘러가는지 이해하게 되었다.
하지만 아직까지 Proxy를 정확하게 정리하여 머릿속에 집어넣지 못했다.

스프링 핵심원리 - 고급편 강의도 이제 몇 챕터 남지 않았다.
이때까지 코드를 따라치면서 라이브코딩 형식으로 수강을 했으니
다시 처음으로 돌아가 MVC, DB 강의들도 정리를 하면서 온전히 내것으로 만드는 시간을 가져야겠다.

올해의 공부 프로세스
스프링 고급편 마무리(2월 21일까지) - 자바의 정석 책 마무리 (3월) - 스프링 로드맵 복습 (4월~) - 정보처리기사 공부 (3월 자바의 정석과 병행 ~ 5월,7월 2,3회 필기시험 치기)

0개의 댓글