우리가 만든 여러 서비스 클래스가 있다고 상상해 봅시다.
MemberService, OrderService, ProductService 모두 각자의 핵심 비즈니스 로직을 가지고 있습니다.
그런데 모든 서비스의 메서드가 시작하고 끝날 때, 실행 시간을 로그로 남겨야 한다는 요구사항이 추가되었습니다.
가장 단순한 방법은 모든 메서드의 시작과 끝에 아래와 같은 코드를 직접 추가하는 것입니다.
public void someMethod() { long startTime = System.currentTimeMillis(); // 부가 기능 (시작 시간 측정) // ... 핵심 비즈니스 로직 ... long endTime = System.currentTimeMillis(); // 부가 기능 (종료 시간 측정) System.out.println("Execution time: " + (endTime - startTime)); // 부가 기능 (로그 출력)}
이 방식은 몇 가지 심각한 문제를 가집니다.
중복 코드
모든 메서드에 똑같은 코드가 반복적으로 들어갑니다.
핵심 로직 오염
비즈니스 로직과 관련 없는 부가 기능 코드가 섞여 있어, 코드를 읽고 이해하기 어렵게 만듭니다.
유지보수의 어려움
로그 출력 방식을 변경하려면, 관련된 모든 메서드를 찾아가 일일이 수정해야 합니다.
이처럼 여러 클래스와 메서드에 걸쳐 공통적으로 나타나는 부가 기능을
횡단 관심사(Cross-cutting Concerns)
라고 부릅니다.
AOP는 바로 이 '횡단 관심사'를 핵심 비즈니스 로직으로부터 분리하여 별도의 모듈로 관리하는 기술입니다.
Spring AOP의 가장 대표적이고 강력한 활용 사례입니다.
개발자는 트랜잭션 처리를 위한 복잡한 try-catch-commit-rollback 코드를 작성할 필요 없이, 그저 메서드 위에 @Transactional 어노테이션 하나만 붙이면 됩니다.
@Transactionalpublic void transferMoney(String from, String to, int amount) { // ... 출금 및 입금 로직 ...}
이 @Transactional 어노테이션이 바로 AOP의 애스펙트입니다.
Spring은 이 어노테이션을 보고, 메서드가 시작하기 전에 트랜잭션을 시작하고(Advice), 메서드가 성공적으로 끝나면 커밋하며(Advice), 예외가 발생하면 롤백하는(Advice) 부가 기능을 자동으로 적용해 줍니다.
을 기반으로 동작합니다.
Spring 컨테이너는 AOP가 적용된 타겟 객체에 대한 '프록시 객체'를 대신 생성하여 Bean으로 등록합니다.
클라이언트가 이 프록시 객체의 메서드를 호출하면, 프록시는 먼저 AOP 어드바이스(부가 기능)를 실행한 뒤, 실제 타겟 객체의 메서드를 호출하는 방식으로 동작합니다.
이러한 방식을 통해, 기존 타겟 객체의 코드를 전혀 수정하지 않고도 부가 기능을 적용할 수 있습니다.