AOP (Aspect-Oriented Programming) → 관점 지향 프로그래밍이라고 한다
AOP는 "공통 관심사(공통 기능)"를 핵심 로직에서 분리해서 재사용성, 가독성, 유지보수성을 높이는 기법이다
서비스나 컨트롤러 코드를 짜다 보면 아래와 같은 "모든 메서드에서 반복되는 코드"가 많아진다:
로그 출력
실행 시간 측정
트랜잭션 관리
인증/인가 체크
예외 처리
캐시 처리
이걸 매번 메서드마다 반복하면 코드가 더러워지고, 변경도 어렵다
| 구성요소 | 역할 |
|---|---|
| Aspect | 공통 기능이 무엇인지 정의한 클래스 |
| Join Point | 메서드 실행 지점 같은 "코드의 특정 포인트" |
| Advice | 그 시점에 "무슨 일을 할지" (before, after 등) |
| Pointcut | 어떤 Join Point에 적용할지 조건 정의 |
| Weaving | 실제로 대상 코드에 끼워 넣는 작업 |
@Aspect
@Component
public class TimerAspect {
@Around("execution(* com.myapp..*(..))") // Pointcut
public Object measureTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed(); // 핵심 로직 실행
long end = System.currentTimeMillis();
System.out.println("실행 시간: " + (end - start) + "ms");
return result;
}
}
@Transactional 같은 트랜잭션 처리
@LogExecutionTime 같은 실행 시간 로그
사용자 인증, 권한 체크 (@AuthCheck)
예외 감싸기
API 요청/응답 로깅
AOP는 "코드 중복 없이, 필요한 기능만 쏙쏙 껴넣을 수 있는 마법 같은 도구"다
핵심 로직은 핵심에만 집중하고, 나머지 잡일은 AOP가 해주는 구조라고 보면 된다
이제 메서드 실행 전후로 로그 찍어주는 AOP 를 만들어보면,
Spring에서 AOP 쓰려면 먼저 몇 가지 준비가 필요하다.
build.gradle (만약 없다면 추가)
implementation 'org.springframework.boot:spring-boot-starter-aop'
package com.outsourcing.outsourcingproject.common.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
// ⭐ 포인트컷: com.outsourcing.outsourcingproject 패키지 하위의 모든 메서드
@Pointcut("execution(* com.outsourcing.outsourcingproject..*(..))")
public void applicationPackagePointcut() {}
// 🔹 메서드 실행 전
@Before("applicationPackagePointcut()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("➡️ [Before] " + joinPoint.getSignature());
}
// 🔹 메서드 실행 후
@AfterReturning(pointcut = "applicationPackagePointcut()", returning = "result")
public void logAfter(JoinPoint joinPoint, Object result) {
System.out.println("✅ [After] " + joinPoint.getSignature());
System.out.println("📦 [Return] " + result);
}
// 🔺 예외 발생 시
@AfterThrowing(pointcut = "applicationPackagePointcut()", throwing = "error")
public void logException(JoinPoint joinPoint, Throwable error) {
System.out.println("❌ [Exception] " + joinPoint.getSignature());
System.out.println("🚨 [Error] " + error.getMessage());
}
}
예를 들어 OrderService의 메서드 하나 실행하면 콘솔에 이렇게 찍힌다:
➡️ [Before] OrderService.createOrder(..)
✅ [After] OrderService.createOrder(..)
📦 [Return] OrderResponseDto{...}
특정 어노테이션 붙은 메서드만 AOP 적용
메서드 실행 시간 측정
HttpServletRequest 꺼내서 클라이언트 IP 찍기 등등…