← 참고자료📚 전체 맵참고자료 →
Spring Framework

AOP 완전 정복

처음 보는 사람도 순서대로 읽으면 이해되도록 구성한 AOP 완전 가이드

이 문서 읽는 법 — 이해 순서

AOP는 한꺼번에 다 이해하려 하면 머리가 터진다. 순서가 있다. 이 순서대로만 읽으면 된다.

STEP 01
왜 쓰는지부터
"반복 코드를 한 곳에 모은다." 이것만. 나머지 개념은 이게 잡힌 다음에 읽어야 머리에 들어온다.
→ Part 1에서 설명
STEP 02
@Around 하나만
5가지 Advice를 한꺼번에 외우려 하지 말 것. @Around 하나만 완벽히 이해하면 나머지 4개는 저절로 따라온다. 실무에서 90%가 이걸 쓴다.
→ Part 3에서 설명
STEP 03
jp.proceed()가 뭔지
@Around 안에서 원래 메서드를 실행시키는 딱 한 줄. 이게 이해되면 @Around는 완성이다.
→ Part 3, Part 4에서 설명
⚠️ 나머지는 그 다음에

@Before, @After, @AfterReturning, @AfterThrowing → "이런 것도 있구나" 정도로만 읽고 넘어가면 된다.
Pointcut 표현식, JoinPoint API → 실제로 코드 짜면서 필요할 때 찾아보면 된다.
지금 단계에서 이것까지 다 외우려 하면 아무것도 안 남는다.

AOP가 왜 필요한가

AOP를 이해하는 첫 번째 단계. "반복 코드를 한 곳에 모은다"는 게 무슨 말인지 코드로 직접 눈으로 확인하자.

AOP란?

AOP는 여러 클래스에 나뉜 책임을 Aspect(관점)라고 부르는 별도의 클래스에 캡슐화하는 접근 방식이다. 그리고 여러 클래스에 걸쳐 있는 이 책임을 횡단 관심사(Cross-cutting concern)라고 부른다.

횡단 관심사(Cross-cutting concern) 예시
로깅 (Logging) 트랜잭션 관리 캐싱 보안
횡단 관심사를 AOP로 처리하는 순서
자바 클래스(Aspect)를 정의하고, 횡단 관심사에 대한 구현을 그 클래스에 추가한다.
표현식을 사용해 횡단 관심사를 적용할 메서드를 지정한다. (이게 Pointcut)
AOP 용어 한눈에
횡단 관심사를 구현하는 Aspect의 메서드 → Advice(어드바이스)
Advice를 적용할 메서드를 구별하는 표현식 → Pointcut(포인트컷)
Advice가 실제로 적용되는 그 메서드의 실행 지점 → JoinPoint(조인포인트)
호출하는 객체와 대상 객체 사이에 끼어드는 중간 객체 → Proxy(프록시)
피자 가게 시나리오 — AOP 없을 때 vs 있을 때
❌ AOP 없을 때
PizzaService.java
public void 주문받기() {
  // 😩 반복 코드
  로그인확인();
  long s = now();

  System.out.println("주문!");

  시간기록(s);
}
public void 피자만들기() {
  // 😩 또 반복
  로그인확인();
  long s = now();

  System.out.println("제조!");

  시간기록(s);
}
public void 배달하기() {
  // 😩 또또 반복
  로그인확인();
  long s = now();

  System.out.println("배달!");

  시간기록(s);
}
문제점

로그인확인(), 시간기록() 코드가 메서드마다 반복된다. 메서드가 100개면 100번 복붙해야 하고, 나중에 수정하면 100군데를 다 찾아서 바꿔야 한다.

🪄
AOP 적용
✅ AOP 있을 때
PizzaService.java — 핵심만!
public void 주문받기() {
  // ✨ 핵심 코드만!
  System.out.println("주문!");
}
public void 피자만들기() {
  System.out.println("제조!");
}
public void 배달하기() {
  System.out.println("배달!");
}
PizzaAOP.java — 딱 한 번만!
@Aspect @Component
public class PizzaAOP {
  @Around("execution(* PizzaService.*(..))")
  public Object 자동처리(
      ProceedingJoinPoint jp) throws Throwable {
    로그인확인();
    long s = now();
    Object r = jp.proceed();
    시간기록(s);
    return r;
  }
}
해결!

로그인확인(), 시간기록() 코드를 딱 한 곳에만 작성했다. 수정할 때도 AOP 파일 한 곳만 바꾸면 전체에 적용된다.

실무에서 AOP가 쓰이는 곳
로그 기록
어떤 메서드가 언제 실행됐는지 자동 기록
로그인 확인
로그인 안 한 사람이 기능 쓰려 하면 자동 차단
시간 측정
메서드가 얼마나 걸렸는지 자동 측정
에러 처리
오류 나면 자동으로 슬랙 알림 발송
트랜잭션
@Transactional — AOP로 구현된 대표 기능
AOP가 어떻게 동작하는가

AOP를 처음 보면 "내가 안 쓴 코드가 어떻게 자동으로 실행되지?" 하는 의문이 생긴다. 이 원리를 이해해야 AOP 전체가 납득된다.

① "읽기(등록)"와 "실행"은 분리되어 있다
서버 시작 시 — 읽기(등록)
"@After 있네 → 등록"
"@AfterThrowing 있네 → 등록"
"@Around 있네 → 등록"
→ 전부 메모리에 기억해둠
메서드
호출
대기
메서드 호출 시 — 실행
"주문받기() 호출됨"
"@Around 등록돼 있었지?" → 실행
jp.proceed() → 주문받기() 실행
"@After 등록돼 있었지?" → 실행
핵심 포인트

코드 파일에서 @Around가 맨 뒤에 선언되어 있어도 상관없다. 스프링이 서버 시작할 때 클래스 전체를 먼저 다 읽어서 등록해두고, 실제 메서드가 호출되는 순간에 등록된 것들을 실행 시점 순서에 맞게 꺼내서 실행하기 때문이다. 선언 순서와 실행 순서는 별개다.

② Proxy — AOP가 끼어드는 실제 메커니즘

내가 부르는 PizzaService는 사실 진짜가 아니다. 스프링이 몰래 껍데기(Proxy)를 만들어서 준다. 마치 연예인(진짜 PizzaService) 앞에 매니저(Proxy)를 세워둔 것처럼, 모든 요청은 매니저가 먼저 받아 처리한 뒤 연예인에게 전달한다.

👤
내 코드
Controller
PizzaService 호출
스프링이 Proxy를 끼워줌
🎭 Proxy (대리 객체)
🔹 AOP Before 코드 실행
🍕 진짜 PizzaService 실행
🔹 AOP After 코드 실행
결과 반환
결과 수신
Controller
@Around — 먼저 이것만 이해하자

AOP에는 5가지 Advice(끼어드는 방법)가 있다. 그런데 이걸 한꺼번에 다 외우려 하면 안 된다. @Around 하나만 먼저 완벽히 이해하면 나머지 4개는 저절로 따라온다.

경비원 비유로 이해하기
🚪
@Before
문 들어오기 전에만 서있는 경비원
🚶
@After
문 나간 후에만 서있는 경비원
@AfterReturning
무사히 나간 사람에게만 반응하는 경비원
🚨
@AfterThrowing
사고 난 사람에게만 반응하는 경비원
👮
@Around ⭐
문 앞뒤 양쪽 다 서있는 경비원
혼자서 나머지 4명 다 커버 가능
@Around가 나머지 4개를 대체할 수 있는 이유
@Before — 실행 전
@After — 실행 후 항상
@AfterReturning — 성공시만
@AfterThrowing — 에러시만
@Around 하나로 전부 OK
jp.proceed() 위 = Before 역할
jp.proceed() 아래 = After 역할
try-catch로 에러도 처리 가능
@Around 하나로 4가지 역할 전부 수행
@Around("execution(* PizzaService.*(..))")
public Object 전부처리(ProceedingJoinPoint jp) throws Throwable {

    System.out.println("Before 역할");    // ← jp.proceed() 위 = @Before

    try {
        Object r = jp.proceed();             // ← 원래 메서드 실행
        System.out.println("AfterReturning 역할"); // ← 성공시만
        return r;
    } catch (Exception e) {
        System.out.println("AfterThrowing 역할");  // ← 에러시만
        throw e;
    } finally {
        System.out.println("After 역할");    // ← 항상 실행
    }
}
그럼 나머지 4개는 왜 존재하는가?

@Around만 써도 되는데 나머지 4개가 있는 이유는 코드를 더 간결하고 명확하게 만들기 위해서다. "이 메서드는 실행 전에만 끼어들면 돼" 라는 의도가 명확하면 @Before를 쓰는 게 읽기 좋다. @Around는 강력하지만 코드가 길어질 수 있다.

Object r = jp.proceed() 완전 해부

@Around에서 가장 중요한 한 줄. 이걸 정확히 이해해야 @Around를 제대로 쓸 수 있다.

Object  r  =  jp.proceed();
Object
반환 타입.
어떤 타입이든 담을 수 있는
모든 클래스의 조상
jp.proceed()
jp = JoinPoint
(원래 메서드 정보)
proceed() = "지금 실행해줘"
r
원래 메서드가 실행되고
나서 돌려준 값.
그걸 r에 담아둠
실행 흐름
① AOP 코드 먼저 실행 — System.out.println("실행 전")
② jp.proceed() → 진짜 주문받기() 실행, 결과를 r에 저장
③ AOP 후처리 — System.out.println("걸린 시간: Xms")
④ return r → 원래 반환값을 호출자에게 전달
왜 반환 타입이 Object인가?

AOP는 어떤 메서드에도 범용으로 끼어들어야 한다. 반환 타입이 void일 수도, String일 수도, User 객체일 수도 있다. 그래서 어떤 타입이든 받을 수 있는 Object를 쓴다.

반환 타입별 r의 값
void    주문받기()      → r = null               // 반환값 없으면 null
String  메뉴가져오기()   → r = "페퍼로니"          // String이면 String
User    회원조회()      → r = User{name="라희"}  // 객체면 객체
int     재고확인()      → r = 42                  // 기본형은 박싱돼서 담김
⚠️ jp.proceed() 빠뜨리면 어떻게 될까?

원래 메서드가 아예 실행이 안 된다. 주문받기()를 호출했는데 피자가 안 만들어지는 상황이 된다. @Around 쓸 때 jp.proceed() 호출은 거의 필수다.

위험한 실수 — proceed() 누락
@Around("execution(* PizzaService.*(..))")
public Object 실수(ProceedingJoinPoint jp) throws Throwable {
    System.out.println("실행 전");
    // ❌ jp.proceed()를 깜빡했다!
    return null;
    // 결과: 주문받기() 호출했는데 피자가 영원히 안 만들어짐 😱
}
jp = 지금 실행된 메서드에 대한 정보 꾸러미

조금 더 풀면 @Around가 끼어드는 순간, 스프링이 jp 안에 이런 정보들을 담아서 줘요.

jp 안에 들어있는 것들:
jp.getSignature().getName()()   → "주문받기" (메서드 이름)               
jp.getArgs()()                  → ["페퍼로니", 2] (파라미터)          
jp.getTarget()()                → 진짜 PizzaService 객체  
jp.proceed()                    → 진짜 메서드 실행!                  
  

그래서 jp를 받으면 "지금 어떤 메서드가 실행됐는지" 다 꺼낼 수 있고, proceed()로 그 메서드를 직접 실행도 할 수 있는 거예요.

나머지 4가지 Advice

@Around를 이해했다면 나머지는 쉽다. 이 4가지는 @Around의 특정 시점만 담당하는 간소화 버전이다. "이런 게 있구나" 정도로만 읽어도 충분하다.

메서드 실행 타임라인
@Before
메서드 실행 직전 끼어들기
🍕 주문받기() 실행 — 핵심 비즈니스 로직
@After @AfterReturning @AfterThrowing
항상 실행 (성공/실패 무관)
성공했을 때만 실행
예외 발생했을 때만 실행
@Around
앞뒤 전부 감싸기 — 위 4가지를 모두 대체 가능
@Before
실행 전에 끼어들기
메서드가 시작되기 직전에 실행. 원래 메서드 실행을 막을 수는 없다. @Around에서 jp.proceed() 위에 코드 쓰는 것과 같다.
Before ✓
메서드
After
@Before("execution(* PizzaService.*(..))")
public void 실행전에(JoinPoint jp) {
    System.out.println("로그인 확인!");
}
로그인 확인파라미터 검증
@After
실행 후 항상 실행
성공이든 에러든 무조건 실행. Java의 finally와 같은 개념. @Around에서 finally 블록 안에 코드 쓰는 것과 같다.
Before
메서드
After ✓
@After("execution(* PizzaService.*(..))")
public void 실행후에(JoinPoint jp) {
    System.out.println("성공이든 실패든 실행!");
}
자원 정리공통 로그
@AfterReturning
성공했을 때만 실행
메서드가 정상 반환됐을 때만 실행. returning 속성으로 반환값도 받을 수 있다.
Before
메서드
성공시만 ✓
@AfterReturning(
    pointcut="execution(* PizzaService.*(..))",
    returning="result")
public void 성공했을때(Object result) {
    System.out.println("성공! 결과: "+result);
}
성공 로그반환값 가공
@AfterThrowing
에러났을 때만 실행
예외가 발생했을 때만 실행. throwing 속성으로 예외 객체도 받을 수 있다.
Before
메서드
에러시만 ✓
@AfterThrowing(
    pointcut="execution(* PizzaService.*(..))",
    throwing="error")
public void 에러났을때(Exception error) {
    System.out.println("에러: "+error.getMessage());
}
에러 알림슬랙 발송
AOP 핵심 용어 정리

AOP 문서나 강의에서 갑자기 튀어나오는 낯선 단어들. 여기서 한 번에 정리하자.

Pointcut — 한 줄 요약
"어떤 메서드에 AOP를 끼어들게 할지 고르는 필터"
execution(* PizzaService.*(..)) 이 부분이 Pointcut,
어떤 메서드에 끼어들게 할지 고르는 필터
JoinPoint — 한 줄 요약
"끼어든 그 순간의 메서드 정보 꾸러미"
jp = 지금 실행된 메서드에 대한 정보 꾸러미,
jp.getSignature(), jp.getArgs(), jp.proceed() 를 꺼낼 수 있는 객체
Proxy — 한 줄 요약
"진짜 PizzaService는 Proxy 안에 들어있다"
내가 PizzaService를 부르면 스프링이 만든 껍데기(Proxy)가 먼저 받아서 AOP 실행 후 진짜에게 넘김
Aspect
AOP 클래스 전체
= Pointcut + Advice 묶음
@Aspect가 붙은 클래스. "어디에(Pointcut) 무엇을(Advice) 끼울지"를 한 곳에 담는 그릇.
Advice
끼어드는 코드
= 실제로 실행되는 부가 기능
로그인 확인, 시간 측정처럼 핵심 로직 옆에 끼워 넣을 코드 자체. @Before, @Around 등이 붙은 메서드.
Pointcut
어떤 메서드에 끼어들지 고르는 필터
= @Around("여기 부분")
AOP를 모든 메서드에 다 적용할 수는 없다. "나는 PizzaService의 메서드에만 끼어들게 해줘" 라고 지정하는 게 Pointcut. @Around 따옴표 안에 있는 그 표현식이 바로 Pointcut이다.
@Around("execution(* PizzaService.*(..))")
↑ 이 따옴표 안이 Pointcut
→ PizzaService 안에 있는 모든 메서드에 끼어들어줘!
JoinPoint
지금 실행된 메서드에 대한 정보 꾸러미
jp = 메서드 정보가 담긴 객체
@Around가 끼어드는 순간, 스프링이 jp 안에 실행된 메서드의 모든 정보를 담아서 줌. jp를 받으면 지금 어떤 메서드가 실행됐는지 다 꺼낼 수 있고, proceed()로 그 메서드를 직접 실행도 할 수 있다.
jp.getSignature().getName()"주문받기"
jp.getArgs()["페퍼로니", 2]
jp.getTarget()진짜 PizzaService 객체
jp.proceed()진짜 메서드 실행!
Proxy
대리 객체 — PizzaService가 Proxy 안에 있다
= 스프링이 자동 생성하는 껍데기
내가 코드에서 pizzaService.주문받기() 를 호출하면, 사실 진짜 PizzaService가 아니라 스프링이 몰래 만든 껍데기(Proxy)가 먼저 받는다. 마치 사장님한테 전화했는데 비서가 받은 것처럼 — 비서(Proxy)가 먼저 처리하고 사장님(진짜 PizzaService)한테 넘겨주는 것.
jp.proceed() 가 "Proxy 안에 있는 진짜 PizzaService 지금 실행해줘" 하는 거예요.
내 코드 → Proxy 껍데기
AOP 코드 실행 (Before)
진짜 PizzaService 실행 ← jp.proceed()
AOP 코드 실행 (After)
결과 반환
Target
진짜 객체
= AOP가 감싸는 원본
Proxy 안에 들어있는 실제 PizzaService 객체. Proxy가 전처리를 마친 뒤 Target의 메서드를 실제로 실행한다.
설정용 애너테이션
@Aspect
"나는 AOP야!" 선언
이 클래스가 AOP 역할을 한다고 스프링에 알리는 애너테이션. 반드시 붙여야 AOP로 인식된다.
@Component
스프링 빈으로 등록
@Aspect만 붙여서는 동작 안 함. 스프링 컨테이너가 이 클래스를 인식하려면 @Component도 함께 붙여야 한다.
@EnableAspectJAutoProxy
AOP 기능 켜기
설정 파일에 딱 한 번만 선언. AOP 스위치 역할. Spring Boot는 자동으로 활성화되므로 생략 가능.
기본 설정 구조
@Aspect      // ← "나 AOP야!"
@Component   // ← "스프링아 나 좀 써줘!" — 이 둘은 항상 같이 붙임
public class MyAOP {
    // 여기에 끼어드는 코드 작성
}

// Spring Boot는 아래 생략 가능 (자동 활성화됨)
@EnableAspectJAutoProxy
@Configuration
public class AppConfig { }
Pointcut 표현식 — 어디에 끼울지

execution만 있는 게 아니다. 상황에 따라 골라 쓰는 3가지 주요 표현식. 처음엔 execution만 알아도 충분하다.

종류의미예시언제 씀
execution
메서드 직접 지정
"execution(* PizzaService.*(..))"
"execution(void PizzaService.주문받기())"
"execution(* com.myapp..*(..))"
가장 많이 씀. 클래스/메서드/패키지 단위로 세밀하게 지정 가능
@annotation
애너테이션으로 지정
"@annotation(시간측정필요)"
원하는 메서드에만 골라서 적용. execution 다음으로 많이 씀
within
클래스/패키지 전체
"within(com.myapp.service.*)"
service 패키지 전체에 한 번에 적용할 때
&& || !
표현식 조합
"execution(..) && @annotation(..)"
"execution(..) && !execution(..재고확인(..))"
AND/OR/NOT으로 조합해 정밀하게 제어
Pointcut 한 눈에 이해하기
예시
@Around("execution(* PizzaService.*(..))")
'''
여기서 따옴표 안에 있는 이게 PointCut이에요.
'''

execution(* PizzaService.*(..))
          ↑              ↑
     모든 반환타입      모든 메서드, 모든 파라미터
'''

쉽게 보면
'''
PizzaService 안에 있는
모든 메서드에 끼어들어줘!
@annotation 방식 — 원하는 메서드에만 골라서 적용
@annotation 방식 전체 예시
// ① 내 커스텀 애너테이션 만들기
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface 시간측정필요 {}

// ② 적용하고 싶은 메서드에만 붙이기
public class PizzaService {
    @시간측정필요                      // ← AOP 적용 O
    public void 주문받기() { ... }
    public void 재고확인() { ... }   // ← AOP 적용 X
}

// ③ AOP에서 @annotation으로 연결
@Around("@annotation(시간측정필요)")
public Object 시간측정(ProceedingJoinPoint jp) throws Throwable {
    // @시간측정필요가 붙은 메서드에만 자동 적용!
}
@Pointcut — 이름 붙여서 재사용하기

같은 표현식을 여러 Advice에서 반복해서 쓸 때, @Pointcut으로 이름을 붙여두면 재사용할 수 있다.

@Pointcut 재사용
@Aspect @Component
public class PizzaAOP {

    // 이름 붙이기 — 빈 메서드, 이름 역할만 함
    @Pointcut("execution(* PizzaService.*(..))")
    public void 피자서비스전체() {}

    // 재사용 ①
    @After("피자서비스전체()")
    public void 호출로그(JoinPoint jp) { ... }

    // 재사용 ②
    @Around("피자서비스전체()")
    public Object 시간측정(ProceedingJoinPoint jp) throws Throwable { ... }
}
JoinPoint API — 실행 정보 꺼내기

AOP 코드 안에서 "지금 어떤 메서드가 실행됐는지" 다양한 정보를 꺼낼 수 있다. 주로 로그를 찍을 때 사용한다.

JoinPoint 사용 예시
@Before("execution(* PizzaService.*(..))")
public void 로그(JoinPoint jp) {

    String 메서드 = jp.getSignature().getName();
    System.out.println("실행된 메서드: " + 메서드);
    // → 실행된 메서드: 주문받기

    Object[] 파라미터 = jp.getArgs();
    System.out.println("파라미터: " + Arrays.toString(파라미터));
    // → 파라미터: [페퍼로니, 2판]

    String 클래스 = jp.getTarget().getClass().getSimpleName();
    System.out.println("클래스: " + 클래스);
    // → 클래스: PizzaService
}
실전 전체 코드

지금까지 배운 모든 것을 하나의 코드로 합친 것. 처음엔 이해 안 돼도 괜찮다. Part 1~8을 다 이해하고 나서 다시 보면 전부 읽힌다.

실전 AOP — @Pointcut 재사용 + 다중 Advice + JoinPoint 정보 활용
@Aspect
@Component
public class PizzaAOP {

    /* ─── @Pointcut: 이름 붙여서 재사용 ─── */
    @Pointcut("execution(* PizzaService.*(..))")
    public void 피자서비스전체() {}


    /* ─── ① 항상 남기는 호출 로그 (@After + JoinPoint 활용) ─── */
    @After("피자서비스전체()")
    public void 호출로그(JoinPoint jp) {
        String 메서드 = jp.getSignature().getName();
        Object[] 파라미터 = jp.getArgs();
        System.out.println(메서드 + " 호출됨 / 파라미터: " + Arrays.toString(파라미터));
    }


    /* ─── ② 에러나면 슬랙 알림 (@AfterThrowing) ─── */
    @AfterThrowing(pointcut="피자서비스전체()", throwing="e")
    public void 에러알림(Exception e) {
        System.out.println("🚨 에러! 슬랙 전송: " + e.getMessage());
    }


    /* ─── ③ 시간 측정 (@Around + jp.proceed()) ─── */
    @Around("피자서비스전체()")
    public Object 시간측정(ProceedingJoinPoint jp) throws Throwable {
        long start = System.currentTimeMillis();

        Object r = jp.proceed();            // 원래 메서드 실행, 결과를 r에 저장

        System.out.println("⏱ " + (System.currentTimeMillis() - start) + "ms");
        return r;                          // 원래 반환값 그대로 돌려줌
    }
}

/* 실행 결과:
   ⏱ 2ms                              ← @Around
   주문받기 호출됨 / 파라미터: []      ← @After + JoinPoint
   ⏱ 5ms                              ← @Around
   피자만들기 호출됨 / 파라미터: []    ← @After + JoinPoint
   🚨 에러! 슬랙 전송: ...            ← @AfterThrowing (에러시만)
*/
AOP = 귀찮은 반복 코드를 한 곳에 모아 자동으로 끼워주는 마법
Aspect · Advice · Pointcut · JoinPoint · Proxy — 5가지 개념이 합쳐져서 동작한다
전체 정리표

헷갈릴 때 바로 꺼내 쓰는 레퍼런스.

개념 / 애너테이션역할핵심 키워드실무 빈도
@AspectAOP 클래스 선언이 클래스가 AOP야! 라고 스프링에 알림필수
@Component스프링 빈 등록@Aspect와 항상 함께 붙임필수
@Pointcut적용 위치 이름 짓기재사용 가능한 표현식에 이름 붙이기권장
@Before실행 전로그인 확인, 파라미터 검증보통
@After실행 후 항상자원 정리, 공통 로그 (finally와 동일)보통
@AfterReturning성공시만성공 로그, 반환값 후처리가끔
@AfterThrowing예외시만에러 알림, 슬랙/이메일 발송가끔
@Around앞뒤 전부 — 나머지 4개 대체 가능시간 측정, 트랜잭션, 캐싱⭐ 가장 많음
jp.proceed()원래 메서드 실행@Around에서 필수. 안 부르면 원래 메서드 실행 안 됨@Around마다
jp.getSignature()실행된 메서드 정보.getName()으로 메서드 이름 꺼냄로그 찍을 때
jp.getArgs()파라미터 목록배열로 반환, Arrays.toString()으로 출력로그 찍을 때
execution메서드 직접 지정클래스명, 메서드명, 패키지로 세밀 제어⭐ 가장 많음
@annotation애너테이션으로 지정원하는 메서드에만 골라서 적용많음
within패키지/클래스 전체service 패키지 전체 등 범위 지정가끔
← 참고자료📚 전체 맵참고자료 →