Spring AOP는 관심사의 분리(Separation of Concerns)를 통해
비즈니스 로직과 공통 기능을 깔끔하게 분리하기 위한 프로그래밍 기법이다.
즉, 핵심 로직에 집중할 수 있도록
공통 기능을 코드 밖으로 빼주는 역할을 한다.
대표적인 공통 기능에는 다음과 같은 것들이 있다.
• 로깅(logging)
• 트랜잭션 처리(transaction)
• 보안(authorization)
• 성능 측정
• 예외 처리
이런 기능들은 여러 클래스에서 반복적으로 등장하지만,
핵심 비즈니스 로직은 아니다.
AOP는 이런 횡단 관심사(Cross-cutting Concern) 를 한 곳에 모아 관리한다.
public void order() {
log.info("주문 시작");
// 비즈니스 로직
log.info("주문 종료");
}
문제점:
• 중복 코드 증가
• 핵심 로직 가독성 저하
• 공통 로직 수정 시 여러 클래스 수정 필요
👉 AOP는 이 공통 코드를 비즈니스 코드 밖으로 분리한다.
Spring AOP를 이해하려면 아래 용어들이 중요하다.
@Aspect
@Component
public class LogAspect {
}
@Before("execution(* com.example.service.*.*(..))")
public void beforeLog() {
log.info("메서드 실행 전");
}
execution(* com.example.service..*(..))
의미:
• com.example.service 패키지 하위
• 모든 메서드
• 모든 파라미터
👉 Spring AOP는 프록시 기반 AOP다.
Spring AOP 동작 원리 (중요 ⭐⭐⭐)
1. 스프링이 빈을 생성
2. AOP 적용 대상이면 프록시 객체 생성
3. 클라이언트는 실제 객체가 아닌 프록시를 주입받음
4. 메서드 호출 → 프록시 → Advice → 실제 메서드
📌 이 때문에 다음 제약이 있다:
• 같은 클래스 내부 메서드 호출(Self Invocation)은 AOP 적용 안 됨
• 기본적으로 public 메서드만 대상
Aspect 정의
@Aspect
@Component
@Slf4j
public class TimeTraceAspect {
@Around("execution(* com.example.service..*(..))")
public Object trace(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed(); // 실제 메서드 실행
long end = System.currentTimeMillis();
log.info("{} 실행 시간 = {}ms",
joinPoint.getSignature(), end - start);
return result;
}
}
Service
@Service
public class OrderService {
public void order() {
// 비즈니스 로직
}
}
📌 order() 호출 시 자동으로 실행 시간 측정 로직이 적용된다.
| 구분 | Spring AOP | AspectJ |
|---|---|---|
| 방식 | 프록시 기반 | 바이트코드 조작 |
| 적용 시점 | 런타임 | 컴파일 / 로드타임 |
| 적용 범위 | 메서드 실행 | 필드, 생성자 등 |
| 난이도 | 쉬움 | 상대적으로 어려움 |
📌 대부분의 스프링 애플리케이션에서는 Spring AOP로 충분하다.
언제 AOP를 쓰면 좋을까?
✅ 적합한 경우
• 로그, 트랜잭션, 보안
• 공통 정책 적용
• 성능 측정, 모니터링
❌ 부적합한 경우
• 핵심 비즈니스 로직
• 흐름이 복잡해 가독성이 떨어질 때
• 남용하는 경우 (디버깅 어려움)
마무리
Spring AOP는
비즈니스 로직을 깔끔하게 유지하면서 공통 관심사를 분리하는 강력한 도구다.
하지만:
“모든 중복 코드를 AOP로 해결하려 하지 말자.”
필요한 곳에 명확한 목적을 가지고 사용하는 것이 중요하다.