AOP는 공통 관심 사항(cross-cutting concern)을 핵심 관심 사항(core concern)에서 분리하여 중복 코드를 제거하고, 애플리케이션의 모듈화 수준을 높여 공통기능을 독립적으로 관리함으로서 유지보수를 용이하게 한다.
단순하게 AOP를 이해하려고 노력하기 보다는 왜 필요한지를 이해하면 AOP의 의도를 쉽게 파악할 수 있다.
모든 메소드 호출 시간을 측정하고 싶을 때
회원가입/조회에 시간 측정 코드를 반복해서 넣고 싶지 않을 때
공통 관심 사항과 핵심 비즈니스 로직이 뒤섞이면?
💡 공통로직이 섞인 코드 (AOP 적용전):
public void join(Member member) {
long start = System.currentTimeMillis(); //부가기능
repository.save(member); // 핵심기능
long end = System.currentTimeMillis(); // 부가기능
System.out.println("join time: " + (end - start));
}
: Service 클래스 내부에 핵심기능과 부가기능이 섞여있음
: 부가기능이 로직안에 하드코딩되어 있음
Advice
Join Point
Pointcut
Aspect
Weaving
Target
실행흐름 :
1. helloController -> memberService.join() 호출
2. 스프링이 만든 프록시 객체가 먼저 실행됨
3. TimeTraceAop.execute() 메서드에서 joinPoint.proceed() 호출 전/후에 부가기능 실행
4. 핵심 로직은 join() 메서드 내부에서 그대로 실행됨
💡 AOP 적용 핵심코드 :
@Aspect
@Component
public class TimeTraceAop {
@Around("execution(* hello.hellospring..*(..))")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("START: " + joinPoint.toString());
try {
return joinPoint.proceed();
} finally {
long end = System.currentTimeMillis();
long result = end - start;
System.out.println("END: " + joinPoint.toString() + " " + result + "ms");
}
}
}
: memberService.join()이 호출되었지만, 실제로는 Spring이 만든 프록시 객체가 먼저 intercept
: 이 프록시는 TimeTraceAop에 등록된 execute()를 실행
: 그 안에서 joinPoint.proceed()를 호출하여 실제 메서드 실행
: 실행 전/후로 시간 측정 등의 부가 기능이 작동
Component를 사용하지 않고, Bean 등록을 따로 할 수도 있는데,
//Bean 설정 파일
@Configuration
public class SpringConfig {
@Bean
public TimeTraceAop timeTraceAop() {
return new TimeTraceAop();
}
}
AOP 적용 클래스인 TimeTraceAop는 위에서 확인할 수 있듯 두 가지 방법으로 스프링 빈으로 등록할 수 있는데
1. @Component 사용
실무에서는 @Component 대신 @Bean으로 수동 등록하는 것을 권장한다고 한다.
이렇게 하면 AOP를 적용할지 말지에 대한 의사결정을 명확하게 설정하일에 명시할 수 있으며, 환경이나 조건에 따라 유연하게 AOP를 적용할 수 있기 때문이다.
특히 테스트 환경에서 AOP를 끄고 싶을 때, 설정 클래스에서 @Bean 등록을 제거하거나 조건문을 사용하는 등의 방법으로 간편하게 제어할 수 있다.
환경별 조건 처리(@Profile/if문) 예시 :
// 운영환경에서만 AOP 적용
@Profile("prod")
@Bean
public TimeTraceAop timeTraceAop() {
return new TimeTraceAop();
}
AOP는 프록시 방식으로 동작한다.

[Controller]
↓
[프록시 Service] <-- 공통기능 (Advice 실행)
↓
[실제 Service]
AOP는 프록시 패턴을 이용해 동작한다. 프록시 패턴에서는 프록시 객체가 타겟 객체를 감싸고, 클라이언트가 프록시를 통해 타겟 객체에 접근한다.
예를 들어 클라이언트가 메서드를 호출하면 프록시 객체가 이 호출을 가로채고 프록시 객체는 미리 정의된 어드바이스를 실행하고, 그 후에 타겟 객체의 실제 메서드를 호출한다.
이런 방식으로 개발자는 비즈니스 로직을 수정하지 않고도 추가 기능을 쉽게 삽입할 수 있다.
AOP는 핵심 로직을 깨끗하게 유지하면서 공통 기능을 효율적으로 관리할 수 있도록 도와준다
Spring AOP는 프록시 기반이라 기존 코드 변경 없이도 Aspect를 삽입할 수 있다
실무에서는 트랜잭션, 로깅, 성능 측정 등에 AOP를 적극적으로 활용한다
[참고자료]
https://velog.io/@may_yun/Spring-AOP-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%A0%EA%B9%8C
https://jaehoney.tistory.com/389
https://velog.io/@wxxhyeong/AOP
https://codegym.cc/ko/groups/posts/ko.543.aoplan-gwanjeom-jihyang-peulogeulaeming-ui-wonli