| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- model
- SpringLegacy
- MVC요청플로우
- 클린아키텍처
- 공통기술
- Ajax
- TDD
- 문제선택근거
- DDD
- 냄새라도
- 단위테스트
- 사면초가
- JDBC
- 전전긍긍
- index
- 공통규약
- string
- AOP
- Transaction
- SEQUENCE
- 로그백
- 이벤트핸들러등록
- springsecurity
- MVC
- JPA
- 디스크i/o
- 이벤트핸들러this
- 설정세팅
- OOP
- 커뮤니티서버
- Today
- Total
개발이군고구마
[Spring : Design Pattern] 3. AOP와 Proxy (Self Invocation부터 시작) 본문
1. 이유
고민의 시작
@CircuitBreaker 가 적용된 메서드 안에서 @CircuitBreaker 메서드를 호출할 때 applicationContext 에 있는 bean 을 가져오게끔 되어 있음
application.getBean(this.getClass()).getMethod(adaper, url, json);
기존에 메서드는 같은 클래스라면 getMethod를 호출해주면 되지만,
CircuitBreaker 함수를 호출하는 부분에서는 applicationContext.getBean을 통해서 메서드를 호출해주고 있음
🧐 왜 같은 클래스의 메서드를 호출하는데 "굳이" 컨테이너에 있는 클래스의 메서드를 실행해주는 걸까?
2. AOP
많은 부분에서 '동일하게' 적용되는 코드들은 한데 모아,
특정 조건에 맞을 때 해당 코드를 호출해주는 방식을 프록시 패턴이라 한다.
[어드바이저 : Advisor]
특정 조건 : 포인트컷 Pointcut
동일하게 적용되는 조건 : 어드바이스 Advice
이렇게 어디에, 무엇을 적용할지에 대한 '관점' 을 중심으로 개발 방법을 AOP 라 한다.
스프링에서는 AOP를 구현하기 위해 @Aspect 어노테이션을 만들어 사용하는데,
해당 어노테이션이 붙어 있는 Adviosr를 `@Aspect` 어드바이저 빌더 내부에 저장한다.

💥 조건이 하나라도 만족하면 프록시 적용 대상이 된다.
예를 들어서 메서드 하나만 포인트컷 조건에 만족해도 프록시 적용 대상이 된다.
- Circuitbreaker 어노테이션이 붙어있는 메서드가 서비스 클래스를 통해서 들어오게 되면 프록시의 메서드 -> 실제 타겟 메서드로 호출하게 되면서 필요한 '기능'들(어드바이스) 이 추가되서 실행되지만
- 서비스에서 바로 호출하게 되면 이미 실제 타깃 메서드가 호출된 상태에서 -> 타겟 메서드를 호출하면 프록시가 적용이 되지 않게 됨
- 따라서, 프록시를 적용하기 위해 메서드 내에서 빈에 있는 메서드를 호출하도록 함
3. Proxy 패턴
1) 관점 : 같은 타입 (Proxy / Decorator)

- Proxy : 검증의 추가
- Decorator : 기능의 추가
- @Transactional : 해당 어노테이션 있는 클래스들은 빈이 프록시로 등록이 되어있고, 해당 클래스를 통해 메서드를 호출 -> 메서드에 어노테이션 붙었을 경우 트랜잭션 필요 코드 붙여서 반납함
2) 관점 : 다른 타입 (Adapter)

다른 타입의 기능을 추가하는 방법
3) 프록시 적용 예제
- 어노테이션 (포인트 컷)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Trace {
}
- 어드바이저 (포인트 컷에 대한 어드바이스 정의)
@Slf4j
@Aspect
public class TraceAspect {
@Before("@annotation(hello.aop.exam.annotation.Trace)")
public void doTrace(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
log.info("trace {} args={}", joinPoint.getSignature(), args);
}
}
- 타겟 클래스 (어노테이션을 가지고 있는 메서드)
@Service
@RequiredArgsConstructor
public class ExamService {
private final ExamRepository examRepository;
@Trace
public void request(String itemId) {
examRepository.save(itemId);
}
}
- 실행
- Aspect를 찾아서 어드바이저 빌더에 저장 TraceAspect
- 스프링 객체가 전달되면 어드바이저 빌더 내를 살핀 후, 클래스/메서드 를 하나하나 더 확인하며 포인트 컷에 하나라도 만족하게 되면 @Trace -> request
- 스프링 빈에 프록시가 등록됨 ExamService / TraceAspect
- 프록시 등록된 객체의 메서드가 실행이 되면 -> 포인트 컷 조건인지 확인 후 -> 조건에 만족하면 어드바이스를 실행하고 타겟 메서드 실행 (그렇지 않으면 타켓 메서드만 실행)
examService.request("data" + i);
'SERVER > Spring' 카테고리의 다른 글
| DispatcherServlet의 예외 처리 과정 (4) | 2025.07.11 |
|---|---|
| [Spring/DB]Transaction 과 Lock (@Transactional 사용에 대한 궁금증을 시작으로) (0) | 2025.03.08 |
| [Spring Security] 타기기에서 자동로그인 기능 풀기 (0) | 2024.10.28 |
| Session 사용 이유와 방법 (0) | 2024.10.27 |
| [Spring : Design Pattern] 2. Template Callback (인수 interface) (0) | 2024.10.04 |