
API를 만들다 보면 “모든 요청에서 공통으로 하고 싶은 일”이 생긴다. 예를 들면 요청/응답 로그를 남기거나, API별 응답 시간을 재서 병목 구간을 찾는 작업이다. 이런 코드를 각 컨트롤러/서비스 메서드마다 복붙하면 중복이 폭발하고, 핵심 로직이 지저분해진다. 이 문제를 해결하는 대표적인 방법이 AOP(관점 지향 프로그래밍) 이다.
각 API의 핵심 기능(주문 생성, 상품 조회 등)은 “핵심 관심사(core concern)”다. 반면 다음 같은 기능은 대부분의 API에 공통으로 붙는다.
이런 것들을 “공통 관심사(cross-cutting concern)”라고 부른다.
AOP의 주된 목적은 공통 관심사와 핵심 관심사를 분리해서, 비즈니스 코드(핵심 로직)를 깔끔하게 유지하는 것이다.
스프링에서 요청이 들어왔을 때 공통 처리를 할 수 있는 지점은 여러 개다.
여기서 핵심은 “유연성”이다.
이런 이유로, “모든 컨트롤러 진입 직전에 공통코드” 같은 요구에 AOP가 자주 사용된다. (인터셉터라는 표현을 같이 쓰기도 하는데, 핵심 아이디어는 ‘중간에서 가로채기’다.)
AOP가 가능한 이유는 프록시(Proxy) 기술 때문이다.
즉, “사용자의 요청을 스프링한테 혼선을 줘서(= 진짜가 아니라 프록시를 보게 해서) 중간에서 낚아채는” 방식이 AOP의 본질이다.
AOP를 이해하려면 용어가 먼저 정리되면 좋다.
Join Point
관점 코드(부가기능)가 적용될 수 있는 “실행 지점”이다. 실무에서는 보통 “어떤 메서드 실행 시점”으로 이해하면 된다.
Pointcut
“어떤 Join Point에 적용할지”를 고르는 규칙이다.
예: 특정 패키지의 컨트롤러만, 특정 어노테이션이 붙은 메서드만 등.
Advice
실제로 실행되는 부가 로직이다.
실행 시점에 따라 Before/After/Around 같은 형태가 있다.
그리고 가장 많이 쓰는 흐름이 이거다.
특히 성능 측정/로깅은 보통 @Around로 한 번에 처리하는 게 편하다.
이 클래스가 “AOP 관점(부가기능 모듈)”임을 나타낸다.
즉, 로깅/성능 측정 같은 공통 관심사를 모아둔 클래스라는 선언이다.
어드바이스가 적용될 위치(메서드/클래스/패키지)를 정의한다.
“어디를 낚아챌지” 정하는 필터 규칙 같은 느낌이다.
메서드 실행 전/후를 모두 감싸서 실행한다.
성능 측정(시작 시간/종료 시간), 공통 로깅, 예외 로깅 같은 작업에 특히 적합하다.
1) @Aspect를 클래스 레벨에 선언한다.
2) 스프링 빈으로 등록되도록 @Component(또는 @Configuration + @Bean)로 등록한다.
3) 공통 관심사 메서드에 @Around를 붙이고 적용 범위를 설정한다(패키지/어노테이션/클래스 등).
4) 어드바이스 내부에서 joinPoint.proceed()를 호출한다.
아래는 “컨트롤러 메서드 실행 전/후로 시간을 재고 로그를 남기는” 전형적인 형태다.
(개념 설명용으로 간단히 작성)
@Aspect
@Component
public class ApiLoggingAspect {
@Around("execution(* com.example..controller..*(..))")
public Object logAndMeasure(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
// 진입 전 로그(원하면 요청 파라미터/메서드명 등 기록)
String methodName = joinPoint.getSignature().toShortString();
System.out.println("[ENTER] " + methodName);
try {
// 실제 컨트롤러 메서드 실행(핵심 로직)
Object result = joinPoint.proceed();
return result;
} finally {
long elapsed = System.currentTimeMillis() - start;
// 종료 후 로그(응답 시간)
System.out.println("[EXIT ] " + methodName + " took " + elapsed + "ms");
}
}
}
이렇게 하면 컨트롤러 코드에는 성능 측정/로그 코드가 전혀 없는데도, 모든 요청에 대해 공통으로 동작한다.
또한 Pointcut만 바꾸면 “특정 컨트롤러만”, “특정 어노테이션 붙은 API만”처럼 적용 대상을 정교하게 조절할 수 있다.
반대로 “HTTP 레벨에서 무조건 처리해야 하는 일”이라면 필터/인터셉터가 더 자연스러운 경우도 많다.
하지만 “메서드 실행 전후를 유연하게 낚아채서 공통 코드를 넣고 싶다”면 AOP는 강력한 선택지다.