🏗️ Build Up
- 관점이라는 용어는 개발자들에게 관심사라는 말로 융통된다.
- 관심사는 개발 시 필요한 고민이나 염두에 두어야 하는 일이라고 할 수 있는데, 코드를 작성하면서 염두에 두어야 하는 일들은 주로 이렇다.
- 파라미터가 올바르게 들어왔을까?
- 로그를 적절하게 남기고 있는가?
- 사용자가 적절한 권한을 가진 사용자인가?
- 해당 작업에서 발생할 수 있는 모든 예외는 어떻게 처리해야 하는가?
Spring Proxy 자료
💨What
AOP란?
실제 핵심로직(core concern)을 수행하면서 발생하는 횡단 관심사(Cross Cutting Concern)를 한데 모아 처리하는 것을 AOP라고 한다.
- AOP가 추구하는 것은 관심사의 분리이다.
ex) 나눗셈을 구현한다고 하면, 핵심 로직은 두 개의 숫자를 나누는 것이지만, 주변 로직은 0을 나누는 것이 아닌지 등을 체크하는 것이다.
실행 방향은 위에서 아래로 그에 따른 주변 로직은 오른쪽에 있는 로그처리, 보안 처리, 트랜잭션 처리 이다.

AOP 도식화 및 용어


Target
- Advice가 적용될 객체이며, 개발자가 작성한 비즈니스 로직은 가지는 객체(Service)를 뜻한다.
- 순수한 비즈니스 로직을 가지고 있고, 어떠한 관심사들과도 관계를 맺지 않는다.
Proxy
- target을 전체적으로 감싸고 있는 존재(껍질)
- 내부적으로 Target을 호출하지만, 중간에 필요한 관심하들을 거쳐서 Target을 호출하도록 자동 혹은 수동으로 작성된다.
- proxy 존재는 직접 코드를 통해서 구현하는 경우도 있지만, 스프링 AOP 기능을 이용해서 자동으로 생성되는 auto-proxy를 이용한다.
PointCut 과 JoinPoint 그리고 weaving

*위빙이란? 공통코드(advice)를 핵심 로직 코드에 삽입하는 것 (Aspect 클래스에 정의 한 Advice 로직을 타깃에 적용하는 것을 의미)
JoinPoint : Advice가 적용될 수 있는 메서드 혹은 위치
- 대부분 메소드 단위로 지정한다.
Pointcut: Advice를 주입시킬 Target을 선정하기 위한 방법
- Advice를 어떤 JoinPoint에 결합할 것인지를 결정하는 설정
- execution: 메서드 기준으로 PointCut 설정
- withIn: 특정 타입(클래스)을 기준으로 설정
- this: 주어진 인터페이스를 구현한 객체 대상 설정
- arg: 특정 파라미터를 가지는 대상들만 PointCut 설정
- annotation: 특정 어노테이션 적용된 대상만
Weaving: Advice를 핵심 코드와 연결 및 적용
- 지정된 객체에 aspect를 적용해 새로운 proxy를 생성하는 과정
- @Transaction Aop로 예를 들면, A라는 객체에 트랜잭션 aspect가 지정되어 있다면, A라는 객체가 실행되기 전 커넥션을 오픈하고 실행이 끝나면 커넥션을 종료하는 기능 추가된 프록시 객체가 생성된다. 이 프록시 객체가 앞으로 A라는 객체가 호출되는 시점에 사용된다.
Aspect와 Advice
Aspect: Advice와 함께 공통 코드 또는 관심사라는 용어로 사용
- Aspect는 관심사 자체를 의미하는 추상명사 이며, 부가 기능을 정의한 Advice와 Advice를 어디에 적용할지를 결정하는 pointcut을 함께 갖는다.
Advice: Aspect를 구현한 코드
Before: Target의 JointPoint를 호출하기 전에 실행되는 코드
AfterReturniung: 모든 실행이 정상적으로 이루어진 후에 동작하는 코드
AfterThrowing: 예외 발생한 뒤 동작하는 코드
AfterAdvice: 정상적으로 실행되거나 예외가 발생했을 때 구분 없이 실행되는 코드
Around: 메서드의 실행 자체를 제어할 수 있는 가장 강력한 코드(직접 대상 메서드 호출하고 결과나 예외처리가 가능하다.)
스프링 3버전 이후 해당 애노테이션으로 모든 설정이 가능해졌다.
❓Why
- 핵심 비즈니스 로직만을 남겨 가독성을 높이고 코드 중복을 없애기 위해 사용된다.
✅How
@Pointcut 사용법
- 포인트컷 분리
@Slf4j @Aspect public class AspectV2 { // hello.aop.order 패키지와 하위 패키지 @Pointcut("execution(* hello.aop.order..*(..))") // pointcut expression private void allOrder() { // -> around로 들어감 } // pointcut signature @Around("allOrder()") public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable { log.info("[log] {}", joinPoint.getSignature()); return joinPoint.proceed(); } }
- 포인트컷 혼합(advice 추가)
- 포인트컷은 이렇게 조합할 수 있다. && (AND), || (OR), ! (NOT) 3가지 조합이 가능하다.
- hello.aop.order 패키지와 하위 패키지 이면서 타입 이름 패턴이 *Service 인 것을 대상으로 한다.
- 결과적으로 doTransaction() 어드바이스는 OrderService 에만 적용된다.
- doLog() 어드바이스는 OrderService , OrderRepository 에 모두 적용된다.
@Slf4j @Aspect public class AspectV3 { // hello.aop.order 패키지와 하위 패키지 @Pointcut("execution(* hello.aop.order..*(..))") public void allOrder() { } // 클래스 이름 패턴이 *Service @Pointcut("execution(* *..*Service.*(..))") private void allService() { } @Around("allOrder()") public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable { log.info("[log] {}", joinPoint.getSignature()); return joinPoint.proceed(); } // hello.aop.order 패키지와 하위 패키지 이면서 클래스 이름 패턴이 *Service @Around("allOrder() && allService()") public Object doTransaction(ProceedingJoinPoint joinPoint) throws Throwable { try { log.info("[트랜잭션 시작] {}", joinPoint.getSignature()); Object result = joinPoint.proceed(); log.info("[트랜잭션 커밋] {}", joinPoint.getSignature()); return result; } catch (Exception e) { log.info("[트랜잭션 롤백] {}", joinPoint.getSignature()); throw e; } finally { log.info("[리소스 릴리즈] {}", joinPoint.getSignature()); } } }
@Around("allOrder() && allService()")
@Around 사용법
// 특정 어노테이션이 명시된 모든 메써드의 실행 전후로 끼어들 수 있다. @Around("@annotation(sampleAnnotation)") public Object somethingAround(final ProceedingJoinPoint joinPoint, final SomeAnnotation someAnnotation) { // 실행 전 Object result = joinPoint.proceed(); // 실행 후 return result; } // 특정 어노테이션이 설정된 모든 method 실행 앞/뒤 @Around("@annotation(scheduled)") public Object process(final ProceedingJoinPoint joinPoint, final Scheduled scheduled) throws Throwable { } // 클래스 레벨에 특정 어노테이션이 명시된 모든 method @Around("@within(org.springframework.web.bind.annotation.RestController) || @within(org.springframework.stereotype.Service) || @within(org.springframework.stereotype.Repository)") public Object process(final ProceedingJoinPoint joinPoint) throws Throwable { }
왜? Around만 쓰지 다른것은 왜쓰는가?
- Around는 ProceedingJoinPoint joinPoint를 매개변수로 받고 다른 것들은 JoinPoint joinPoint 를 매개변수로 받는 것에 차이를 시작으로 ProceedingJoinPoint.proceed()를 호출하지 않으면 target에 도달하지 않는 버그 발생의 위험이 있다.
- 이에반해 다른 것들은 proceed()를 호출하지 않아도 되며 이름만으로도 어떤 AOP인지 바로 확인가능하다는 점에서 같이 사용되고 있다.
📌 REFER
- expression 의 설명 조금