이 글에서 얻는 것

  • Spring AOP가 “마법”이 아니라 프록시 기반이라는 점을 이해하고, 왜 특정 상황에서 동작하지 않는지 설명할 수 있습니다.
  • 포인트컷/어드바이스/조인포인트 같은 용어를 외우는 수준을 넘어, 실무에서 로그/트랜잭션/권한/메트릭에 적용할 수 있습니다.
  • self-invocation, final, 실행 순서(@Order) 같은 함정을 알고 설계/디버깅 실수를 줄일 수 있습니다.

들어가며

AOP (Aspect-Oriented Programming, 관점 지향 프로그래밍)는 Spring Framework의 핵심 기능 중 하나입니다. 횡단 관심사(Cross-Cutting Concerns)를 모듈화하여 코드 중복을 제거하고 유지보수성을 높일 수 있습니다.


1. AOP 핵심 개념

1.1 횡단 관심사 (Cross-Cutting Concerns)

문제 상황:

// 모든 서비스 메서드에 로깅 코드가 중복
@Service
public class UserService {
    public User findUser(Long id) {
        log.info("findUser called with id: {}", id);  // 중복 1
        try {
            User user = userRepository.findById(id);
            log.info("findUser returned: {}", user);  // 중복 2
            return user;
        } catch (Exception e) {
            log.error("findUser failed", e);  // 중복 3
            throw e;
        }
    }

    public void createUser(User user) {
        log.info("createUser called");  // 중복 1
        try {
            userRepository.save(user);
            log.info("createUser completed");  // 중복 2
        } catch (Exception e) {
            log.error("createUser failed", e);  // 중복 3
            throw e;
        }
    }
}

AOP 적용 후:

// 비즈니스 로직만 집중
@Service
public class UserService {
    public User findUser(Long id) {
        return userRepository.findById(id);
    }

    public void createUser(User user) {
        userRepository.save(user);
    }
}

// 로깅 관심사를 Aspect로 분리
@Aspect
@Component
public class LoggingAspect {
    @Around("execution(* com.example.service.*.*(..))")
    public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("Method called: {}", joinPoint.getSignature());
        try {
            Object result = joinPoint.proceed();
            log.info("Method returned: {}", result);
            return result;
        } catch (Exception e) {
            log.error("Method failed", e);
            throw e;
        }
    }
}

1.2 AOP 용어

Target Object (대상 객체)
├─ Join Point (결합점)
│   └─ 메서드 실행, 필드 접근 등
│       Advice를 적용할 수 있는 모든 지점
├─ Pointcut (포인트컷)
│   └─ Join Point 중 실제로 Advice가 적용될 지점
│       예: "execution(* com.example.service.*.*(..))"
├─ Advice (어드바이스)
│   └─ 실제로 실행되는 코드
│       - Before: 메서드 실행 전
│       - After: 메서드 실행 후
│       - Around: 메서드 실행 전후
├─ Aspect (관점)
│   └─ Advice + Pointcut의 조합
│       @Aspect 클래스
└─ Weaving (위빙)
    └─ Aspect를 Target에 적용하는 과정
        - 컴파일 타임 (AspectJ)
        - 로드 타임 (AspectJ)
        - 런타임 (Spring AOP - Proxy 생성)


👉 다음 편: Spring AOP (Part 2: Proxy 동작 원리, 실무)