이 글에서 얻는 것
- 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 생성)
💬 댓글