열렬히.뛰기

AOP 이해해보기

웹 프로그래밍 > Spring > AOP 이해해보기

AOP란?

  • Aspect Oriented Programming, 관점 지향형 프로그래밍
  • 여기서의 관점이란, 프로그램을 여러 가지 관심사(Concern)로 나눠서 바라보는 것.
  • 스프링으로 웹 페이지를 만들 때, 핵심 로직 관점(비즈니스 로직)과 부가기능 관점(로깅, 보안, 트랜잭션 등)을 나눠 생각한다는 뜻.
  • 즉, 핵심 로직을 담당하는 패키지 / 클래스를 중심으로 코드를 짜고, 부가기능을 담당하는 클래스는 한 곳에 묶어버린다는 뜻.

나누면 뭐가 좋아?

  1. 관심사 분리: 핵심 로직과 부가기능을 분리해 코드의 가독성과 유지보수성을 높여준다.
  2. 재사용성 향상: 여러 클래스에서 공통으로 사용하는 기능을 한 곳에 정의해 재사용 가능.
  3. 유연성 제공: 특정 지점에서 로직을 쉽게 확장할 수 있음.

코드로 만들기

어떻게 부가기능 클래스를 만들고, 묶고, 핵심기능 담당 클래스에 적용시키지?

AOP 적용 구조

보통은 Aspect라는 패키지를 만들고, 그 안에 클래스를 만드는 경우가 대부분.

또한 부가기능을 담당하는 클래스를 aspect라고 함.

bash
com.example
│
├── aspect         // AOP 애스펙트를 모아두는 패키지
│   ├── LoggingAspect.java     // 로그 기록을 담당하는 애스펙트
│   ├── SecurityAspect.java    // 보안 검사를 담당하는 애스펙트
│   └── TransactionAspect.java // 트랜잭션 관리를 담당하는 애스펙트
│
├── service        // 비즈니스 로직(서비스) 클래스
│   ├── LoginService.java
│   └── UserService.java
│
├── controller     // 컨트롤러 클래스(API 엔드포인트)
│   └── LoginController.java
│
├── config         // 스프링 설정 클래스
│   └── AppConfig.java
│
└── AopApplication.java  // 메인 애플리케이션 클래스

주로 쓰는 패키지

  • 스프링 AOP: 프록시(Proxy) 기반으로 동작하며, 런타임에 애스펙트를 적용.
  • AspectJ: 더 강력한 기능을 제공하며, 컴파일 타임과 로드 타임 위빙을 지원.

간단한 AOP는 스프링 AOP로 충분하지만, 더 정밀한 제어 필요 시 AspectJ를 고려하면 좋음!

Aspect 어노테이션

Aspect = 부가기능용 클래스를 만들려면 무엇이 필요할까?

일단, 클래스 위에 어노테이션으로 Aspect를 붙여야 한다.

java
@Aspect     // 이 클래스는 AOP 애스펙트임을 선언
@Component  // 스프링이 빈으로 등록하도록 설정
public class LoggingAspect {

    // LoginService의 login() 메서드에 적용할 포인트컷 정의
    @Pointcut("execution(* com.example.service.LoginService.login(..))")
    public void loginPointcut() {}

    // 메서드 실행 전에 로그 남기기
    @Before("loginPointcut()")
    public void logBefore() {
        System.out.println("로그인 시도 중...");
    }

    // 메서드가 정상적으로 실행된 후 로그 남기기
    @AfterReturning(pointcut = "loginPointcut()", returning = "result")
    public void logAfterReturning(Object result) {
        System.out.println("로그인 결과: " + result);
    }
}

구성 요소

Aspect = 부가기능 클래스의 구성요소는 3가지가 존재한다.

  1. 포인트컷(Pointcut) : 부가기능(애스펙트)을 적용할 타겟 지점을 결정하는 규칙.
  2. 어드바이스(Advice) : 언제 부가기능을 실행할지 정의. (메서드 실행 전/후 등)
  3. 타겟(Target) : 애스펙트가 적용되는 핵심 기능을 담은 클래스나 메서드.

✨ 포인트컷(Pointcut)

부가기능(Aspect)을 어디에 적용할지를 정하는 규칙.

즉, “어떤 클래스의 어떤 메서드에 부가기능을 끼워 넣을까?”를 결정하는 역할을 함.

주로 메서드의 이름, 파라미터, 어노테이션, 클래스 위치 등을 기준으로 적용할 메서드를 선택.

java
@Pointcut("execution(* com.example.service.LoginService.login(..))")
public void loginPointcut() {}
  • execution 표현식은 특정 클래스의 특정 메서드를 대상으로 부가기능을 적용하도록 함
  • 위 예시에서는 LoginService 클래스의 login() 메서드에 부가기능을 적용하도록 지정.
  • 이때 LoginService 클래스의 login() 메서드가 바로 타겟.

✨ 어드바이스(Advice)

어드바이스는 언제 부가기능을 실행할지를 정의.

  • 메서드 실행 전, 후, 성공 후, 예외 발생 시 등 다양한 시점에서 부가기능을 실행할 수 있어요.
  • 어드바이스 = 실제로 실행되는 부가기능의 로직.

📌 어드바이스 종류와 예시

  1. @Before
  • 메서드가 실행되기 전 부가기능 실행
java
@Before("loginPointcut()")
public void logBefore() {
    System.out.println("로그인 시도 중...");
}
  1. @AfterReturning
  • 메서드가 성공적으로 끝난 후 실행
java
@AfterReturning(pointcut = "loginPointcut()", returning = "result")
public void logAfterReturning(Object result) {
    System.out.println("로그인 결과: " + result);
}
  1. @AfterThrowing
  • 메서드에서 예외가 발생한 후 실행
java
@AfterThrowing(pointcut = "loginPointcut()", throwing = "exception")
public void logAfterThrowing(Exception exception) {
    System.out.println("예외 발생: " + exception.getMessage());
}
  1. @Around
  • 메서드 전후 모두 부가기능 실행 (가장 강력한 어드바이스)
java
@Around("loginPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("메서드 실행 전");
    Object result = joinPoint.proceed();  // 실제 메서드 실행
    System.out.println("메서드 실행 후");
    return result;
}