Java를 접해본 사람이라면 누구나 OOP, AOP에 대해서는 간략하게나마 들어본 경험이 있을 것이다. OOP는 익숙하지만 AOP에 대해서는 자세히 모르는 이유로 AOP에 다뤄보고자 한다.
AOP(Aspect-Oriented Programming)는 관점 지향 프로그래밍이라고도 하며, 공통적으로 사용되는 관심사를 핵심 로직과 분리해서 재사용성, 유지보수성을 높이기 위한 프로그래밍 패러다임이다.
좀 더 쉽게 말하면, 모든 클래스에서 공통적으로 수행해야 할 기능(예: 로깅, 보안, 트랜잭션 처리 등)을 따로 빼서 관리하는 방식이라고 보면 된다.

서비스에서 필요한 내용은 비즈니스 로직이라고 불리는 핵심 기능만 수행할 수 있으면 된다.
그 외에 시간을 잰다든지, 권한을 체크한다든지, 트랜잭션을 건다든지 하는 것은 모두 인프라 로직이다.
이 인프라 로직은 전 영역에서 나타나는 경우가 많기 떄문에 중복코드를 만들어 낼 가능성이 있고, 이런 문제로 유지보수에 어려움을 겪을 수 있다. 또한 비즈니스 로직과 섞여있으면 비즈니스 로직을 이해하기가 어렵다.
이렇게 인프라 로직은 로깅, 트랜잭션, 권한 검사 등 하나의 관심사를 갖게 된다. 비즈니스 로직을 수행하는데 있어 부가기능이 되는 인프라 로직의 중복이 횡단으로 나타나기 때문에 이걸을 횡단 관심사(cross-cutting concern)이라고 부른다.

| 용어 | 설명 |
|---|---|
| Aspect | 공통 기능 (예: 로깅, 트랜잭션 등) ▶ 횡단 관심사!(Advice + PointCut) |
| Join Point | Aspect를 적용할 수 있는 지점 (예: 메서드 실행 시점) |
| Advice | 실제 실행되는 공통(부가) 기능 코드 |
| Pointcut | Advice를 적용할 Join Point를 선택하는 표현식 ▶ 어드바이스를 어디에 적용할지 결정 |
| Weaving | Aspect와 핵심 로직을 엮는 과정 (컴파일, 로드, 런타임 중 하나) |
어떤 메서드가 실행될 때마다 실행 시간을 측정해서 로그로 남기고 싶다고 가정해보자.
public void createUser() {
long start = System.currentTimeMillis(); // 로그 시작 시간
// 핵심 비즈니스 로직
userRepository.save(user);
long end = System.currentTimeMillis(); // 로그 종료 시간
System.out.println("실행 시간: " + (end - start));
}
이런 식의 코드를 모든 서비스 메서드에 반복해서 작성하는 것은 비효율적이며 유지보수성도 떨어진다. 이 문제를 해결해주는 것이 바로 AOP이다.
AOP를 적용하면 아래와 같은 방식으로 공통 기능을 분리할 수 있다.
@Aspect // [Aspect] 이 클래스는 공통 관심사를 모아놓은 '관점(Aspect)'임을 나타냄
@Component // Spring Bean으로 등록되도록 설정
public class LoggingAspect {
@Around("execution(* com.example.service.*.*(..))")
// [Pointcut] service 패키지 하위의 모든 메서드에 적용하겠다는 설정
// [Advice] @Around: 메서드 실행 전후로 로직을 실행하겠다는 의미
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis(); // 메서드 실행 전
Object result = joinPoint.proceed(); // [Join Point] 실제 대상 메서드 실행
long end = System.currentTimeMillis(); // 메서드 실행 후
System.out.println(joinPoint.getSignature() + " 실행 시간: " + (end - start));
return result; // 원래 메서드의 반환값을 그대로 리턴
}
}
| AOP 용어 | 코드 예시 | 설명 |
|---|---|---|
| Aspect | @Aspect가 선언된 클래스 | 공통 기능(로깅)을 모아놓은 클래스 |
| Advice | @Around 메서드 | 공통 기능을 실제로 수행하는 코드 블록 |
| Join Point | joinPoint.proceed() | 대상이 되는 실제 메서드 실행 시점 |
| Pointcut | "execution(* com.example.service.*.*(..))" | 어떤 메서드에 적용할지 정의하는 표현식 |
| Weaving | Spring이 런타임에 수행 | Aspect를 실제 객체에 적용하는 과정 (Spring이 자동으로 처리해줌) |
| 구분 | OOP (객체 지향 프로그래밍) | AOP (관점 지향 프로그래밍) |
|---|---|---|
| 핵심 개념 | 클래스와 객체를 통한 기능 분리 | 공통 기능을 관점으로 분리 |
| 관심사 분리 | 데이터/기능 단위로 분리 | 횡단 관심사 분리 (공통 기능) |
| 코드 중복 | 공통 기능이 여러 클래스에 중복될 수 있음 | 공통 기능을 Aspect로 분리 가능 |
| 예시 | UserService, ProductService 등 | LoggingAspect, TransactionAspect 등 |
→ OOP는 "무엇을 할지", AOP는 "언제 어떻게 할지" 에 집중한다고 볼 수 있다.
AOP는 복잡성 증가와 디버깅 어려움 등의 어려움이 있으니 특정한 횡단 관심사를 관리해야하는 경우에 선택적으로 사용하자!