SpringBoot
AOP(Aspect Oriented Programming) - (2)
알파 조
2025. 4. 25. 00:54
SMALL
이어서 AOP의 동작 원리에 대해서 알아보자.
AOP의 동작원리에서 Proxy라는 얘가 등장하는데 중요한 친구이다.
AOP 동작 원리, 프록시(Proxy)를 중심으로 까보자
핵심 키워드 하나만 기억하면 된다. “프록시 = 가짜 껍데기”
진짜 객체 앞에 서서 모든 호출을 대신 받아주고, 필요한 ‘양념(공통 로직)’을 치고 나서 본게임(핵심 로직)을 실행시키는 녀석이다..
1. 스프링 부트가 애플리케이션을 올릴 때 무슨 일이 일어날까
- 컴포넌트 스캔
@Aspect 붙은 클래스 발견 → “오? AOP 쓰네?” - 포인트컷 분석
- execution(* com.example.demo.controller..*(..))
- “controller 패키지에 있는 모든 메서드 잡아라!”
- 프록시 빈 생성
- 포인트컷에 걸린 클래스마다 프록시 객체를 하나씩 만듦.
- 그 프록시를 스프링 컨테이너에 원본 대신 등록.
- 호출 순서 = 클라이언트 → 프록시 → 원본
┌─────────── ┐
│클라이언트 │
└────▲──────┘
│ (1) 호출
┌────┴───────┐
│프록시 빈 │ ←―――――― AOP에서 만들어 준 껍데기
└────▲────── ┘
│ (2) joinPoint.proceed()
┌────┴───────┐
│원본 객체 │ ←―――――― 컨트롤러·서비스 같은 핵심 클래스
└────────────┘
2. 호출 시 컨트롤 플로우
- 프록시가 호출을 낚아챔
“Pointcut 조건 맞나?” 확인 - Advice 체인 실행
- @Before 있으면 먼저 돌고
- @Around → proceed() 안에서 본게임 호출
- @AfterReturning / @AfterThrowing 등 나머지 훅 실행
- 결과(or 예외) 반환
프록시가 결과를 클라이언트한테 넘겨주고 끝
3. 프록시 만드는 방식 두 가지
클래스가 인터페이스 구현 | JDK 동적 프록시 | java.lang.reflect.Proxy 사용 인터페이스 기반이라 가벼움 |
인터페이스 없음 | CGLIB | 원본 클래스를 상속해서 바이트코드 조작 메서드 오버라이드로 Advice 삽입 |
4. 코어 코드 모습 (JDK 프록시 관점)
// 대충 이런 느낌으로 프록시 클래스가 만들어짐
class MemberController$Proxy implements MemberController {
private final InvocationHandler handler;
MemberController$Proxy(InvocationHandler h) {
this.handler = h;
}
@Override
public Member findMember(Long id) {
// Advice 체인 호출
return (Member) handler.invoke(this, // proxy itself
MemberController.class.getMethod("findMember", Long.class),
new Object[]{id});
}
}
- InvocationHandler 안에 우리의 @Around logAndTime() 로직이 들어가 있음.
- 메서드별로 일일이 코딩 안 해도 되게 런타임에 클래스 파일이 튀어나옴.
5. 왜 이렇게까지 해야 되나
- 관심사 분리 -> 핵심 클래스는 비즈니스에만 집중
- 전역 설정 -> 공통 기능 바꾸면 프록시만 수정, 전 애플리케이션에 즉시 반영
- 동적 토글 -> 설정(프로파일·컨피그) 바꿔서 기능 끄고 켜고 가능
- 테스트 편의 -> 프록시 빼고 원본만 가져다 단위 테스트
결국 프록시는 “개발자가 일일이 타이핑할 필요 없는 데코레이터”
비즈니스 로직에만 머리 쓰고, 나머지는 프레임워크에게 떠넘기자는 개념이다.
728x90