[Spring] AOP

wilstiffeun·2025년 5월 25일

백엔드

목록 보기
3/3

AOP(Aspect Oriented Programming)?

AOP는 공통 관심 사항(cross-cutting concern)을 핵심 관심 사항(core concern)에서 분리하여 중복 코드를 제거하고, 애플리케이션의 모듈화 수준을 높여 공통기능을 독립적으로 관리함으로서 유지보수를 용이하게 한다.

AOP는 왜 필요할까

단순하게 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 클래스 내부에 핵심기능과 부가기능이 섞여있음
: 부가기능이 로직안에 하드코딩되어 있음

AOP 핵심 개념정리

  • Advice

    • 언제 실행할 것인가? (before, after, around)
    • Join Point에서 수행할 작업을 정의
    • 메서드 전(before), 후(after, after-returning), 예외 발생 시(after-throwing), 또는 전체 영역(around) 에서 실행
  • Join Point

    • Advice가 실제로 적용될 수 있는 실행 지점
    • 예: 메서드 호출, 생성자 호출, 필드 접근 등
  • Pointcut

    • 어떤 JoinPoint에 Advice를 적용할지를 필터링하는 표현식
    • 조건에 따라 적용 여부를 결정
  • Aspect

    • 여러 모듈에 공통적으로 적용되는 기능을 모듈화한 객체
  • Weaving

    • Advice를 JoinPoint에 결합하는 과정
    • 시점에 따라 컴파일 시 위빙, 클래스 로딩 시 위빙, 런타임 시 위빙으로 구분
  • 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();
    }
}

@Component 대신 @Bean으로 AOP 등록하는 이유는?

AOP 적용 클래스인 TimeTraceAop는 위에서 확인할 수 있듯 두 가지 방법으로 스프링 빈으로 등록할 수 있는데
1. @Component 사용

  • 간단하게 빈 등록이 가능하다
  • 클래스에 직접 어노테이션을 붙여 사용하므로 빠르고 직관적이다
  • 그러나 자동 등록되기 때문에 제어가 어렵다
  1. 수동으로 @Bean 등록 (권장)
  • AOP 적용 여부를 설정 파일에서 명시적으로 조절할 수 있다
  • 테스트나 운영 환경에 따라 쉽게 ON/OFF 가능하다
  • 다른 Bean들과의 순서를 명확하게 조정하거나, 조건에 따라 등록할 수도 있다
  • 직접 등록해줘야 하므로 약간 수고스럽다

실무에서는 @Component 대신 @Bean으로 수동 등록하는 것을 권장한다고 한다.
이렇게 하면 AOP를 적용할지 말지에 대한 의사결정을 명확하게 설정하일에 명시할 수 있으며, 환경이나 조건에 따라 유연하게 AOP를 적용할 수 있기 때문이다.
특히 테스트 환경에서 AOP를 끄고 싶을 때, 설정 클래스에서 @Bean 등록을 제거하거나 조건문을 사용하는 등의 방법으로 간편하게 제어할 수 있다.

환경별 조건 처리(@Profile/if문) 예시 :

// 운영환경에서만 AOP 적용 
@Profile("prod")
@Bean
public TimeTraceAop timeTraceAop() {
    return new TimeTraceAop();
}

AOP 동작 원리

AOP는 프록시 방식으로 동작한다.

AOP 프록시

  • AOP 기능을 구현하기 위해 만든 프록시 객체
  • 스프링에서 AOP 프록시는 JDK 동적 프록시와 CGLIB 프록시가 있고, CGLIB 프록시가 기본값
  • 인터페이스 기반이면 JDK 동적 프록시, 클래스 기반이면 CGLIB 사용

[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

0개의 댓글