AOP란?
Spring에서 AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)는 프로그래밍에서 반복되는 코드를 모듈화하는 프로그래밍 패러다임입니다.
이는 객체 지향 프로그래밍(OOP)을 보완하는 방식으로, 공통의 관심사(Cross-cutting Concerns)를 한 곳에 모아 관리합니다.
AOP는 주로 로깅, 보안, 트랜잭션 관리와 같은 기능에서 사용됩니다.AOP의 핵심 개념은 'Aspect'로 여러 객체에 공통으로 적용되는 기능을 의미합니다.
예를 들어, 로깅은 애플리케이션의 많은 부분에서 필요하지만 각각의 객체나 메소드에 로깅 코드를 직접 삽입하는 것은 비효율적입니다.
AOP를 사용하면, 공통 기능을 별도의 Aspect로 정의하고, 필요한 객체나 메소드에 독립적으로 적용할 수 있습니다.
AOP 개념 용어
- Aspect(관점)
- AOP의 핵심 요소로, 흩어진 관심사를 모듈화한 것입니다.
- 예를 들어 로깅, 트랜잭션 관리 또는 보안과 같은 기능을 Aspect로 정의할 수 있습니다.
- Join Point
- Advice가 적용될 수 있는 위치, 예를 들어 메소드 실행, 객체 생성 등 프로그램 실행 중 특정 지점을 의미합니다.
- Advice
- 실제로 Aspect가 어떤 일을 수행되는지 작업(로직)을 정의한 것입니다.
- @Before: Join Point 전에 실행됩니다.
- @After Returning: Join Point가 정상적으로 완료된 후에 실행됩니다.
- @After Throwing: 메소드 실행 중 예외가 발생했을 때 실행됩니다.
- @After: Joint Point가 실행된 후, 예외 발생 여부에 상관없이 실행됩니다.
- @Arround: Joint Point 전후로 실행되며, 메소드 실행을 감싸는 형태입니다.
- Pointcut
- Join Points 중에서 실제로 Advice가 적용될 위치를 선별하는 표현식입니다.
- Pointcut을 통해 특정 메소드나 클래스의 메소드들에 대한 Advice 적용 여부를 정의할 수 있습니다.
@Pointcut
@Pointcut
은 Advice가 적용될 메소드나 클래스의 집합을 정의하는 데 사용되는 어노테이션입니다.- 쉽게 말해,
@Pointcut
은 어떤 메소드에 Advice를 적용할지 결정하는 "필터"와 같은 역할을 합니다.
@Pointcut을 사용하지 않은 경우
@Component
@Aspect
@Slf4j
public class MyAspect{
// Before Advice: com.example 패키지 내 모든 메소드 실행 전에 적용
@Before("execution(* com.example..*(..))")
public void beforeAdvice(){
log.info("Before method execution");
}
// After Advice: com.example 패키지 내 모든 메소드 실행 후에 적용
@After("execution(* com.example..*(..))")
public void afterAdvice(){
log.info("After method execution");
}
}
@Pointcut을 사용한 경우
@Component
@Aspect
@Slf4j
public class MyAspect{
// Pointcut 정의: com.eample 패키지 내 모든 메소드
@Pointcut("exectuion(* com.example..*(..))")
public void myPointcut() {}
// Before Advice: myPointcut에 정의된 메소드 실행 전에 적용
@Before("myPointcut()")
public void beforeAdvice(){
log.info("Before method execution");
}
// After Advice: myPointcut에 정의된 메소드 실행 후에 적용
@After("myPointcut()")
public void afterAdvice(){
log.info("After method execution");
}
}
@Pointcut을 사용한 경우 이점
- 재사용성
- @Pointcut을 사용하는 경우, @Pointcut을 한 곳에서 정의하고 여러 Advice에서 재사용 할 수 있어 코드의 중복을 줄일 수 있습니다.
- 유지보수
- @Pointcut 을 사용하면, 추후 Pointcut을 변경할 때 한 곳에서만 수정하면 됩니다.
- 가독성
- @Pointcut 을 사용하면, Pointcut의 목적을 이름으로 명확히 할 수 있어 가독성이 좋아집니다.
@Pointcut 사용 방법
execution
execution([수식어][리턴타입][클래스이름][이름]([파라미터])
- 수식어: public, private 등 수식어를 명시합니다. (생략 가능)
- 리턴타입: 리턴 타입을 명시합니다.
- 클래스이름 및 이름: 클래스이름과 메서드 이름을 명시합니다. (클래스 이름은 풀 패키지명으로 명시해야 합니다. 생략 가능)
- 파라미터: 메서드의 파라미터를 명시합니다.
- "*": 모든 값을 표현합니다.
- "..": 0개 이상을 의미합니다.
ex)
execution(public Integer com.example.aop..(*))
-> com.example.aop 패키지의 하위 클래스 중 public 접근자면서 반환타입이 Integer, 한개의 파라미터를 가지는 메소드
execution(com.example...get*(..))
-> com.example 패키지 및 하위 패키지의 클래스 중 이름이 get으로 시작하며 파라미터 수나 타입에 관계없이 모든 메서드
execution(com.example.aop..Service.*(..))
-> com.example.aop 패키지 및 하위 패키지에 속해있고 이름이 Service로 끝나는 파라미터 수나 타입에 관계없이 모든 메서드
execution(com.example.aop.BoardService.(.))
-> com.example.aop 패키지의 BoardService 클래스의 파라미터 수나 타입에 관계없이 모든 메서드
execution(some(,))
-> 메서드 이름이 some으로 시작하고 파라미터가 2개인 모든 메서드
within 명시자
ex)
within(com.example.aop.SomeService)
-> com.example.aop.SomeService 인터페이스의 모든 메서드
within(com.example.aop.*)
-> com.example.aop 패키지의 모든 메서드
within(com.example.aop..*)
-> com.example.aop 패키지 및 하위 패키지의 모든 메서드
bean 명시자
ex)
bean(someBean)
-> 이름이 someBean인 빈의 모든 메서드
bean(some*)
-> 빈의 이름이 some으로 시작하는 빈의 모든 메서드
Advice 사용 방법
- @Around
- 핵심 메서드와 공통 메서드의 실행 시점을 자유롭게 설정할 수 있는
- 어드바이스를 활용하면 메소드의 실행을 둘러싸 여러 가지 추가 기능을 구현할 수 있으며, 비즈니스 로직에 영향을 주지 않으면서 중요한 관심사를 관리할 수 있습니다.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class PerformanceAspect {
// 포인트컷 정의: Service 레이어의 모든 메소드
@Pointcut("execution(* com.example.service.*.*(..))")
public void monitor() { }
// Around 어드바이스 정의
@Around("monitor()")
public Object logPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
try {
// 대상 메소드 실행
Object result = joinPoint.proceed();
return result;
} finally {
long elapsedTime = System.currentTimeMillis() - startTime;
System.out.println(joinPoint.getSignature().getName() + " 실행 시간: " + elapsedTime + "ms");
}
}
}
- @AfterThrowing
- 메소드 실행 중 예외가 발생한 후에 실행됩니다.
- 발생한 예외에 대한 정보에 접근할 수 있습니다.
- 발생한 예외에 따라 적절한 처리 로직을 수행할 수 있습니다.
- 예외 상황에 대한 로그 기록, 리소스 정리, 사용자에게 오류 정보 전달 등 다양한 예외 처리 로직을 구현하는 데 유용하게 사용됩니다.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class ErrorHandlingAspect {
// 포인트컷 정의: Service 레이어의 모든 메소드
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() { }
// AfterThrowing 어드바이스 정의
@AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
public void logAfterThrowingAllMethods(Exception ex) throws Throwable {
System.out.println("Exception caught in AfterThrowing advice: " + ex.getMessage());
}
}
- @AfterReturning
- 메소드가 성공적으로 실행되고 결과를 반환한 후에 실행됩니다.
- 대상 메소드의 반환값에 접근하여 처리할 수 있습니다.
- 메소드 실행의 성공 여부를 로깅하는데 사용될 수 있습니다.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
// 포인트컷 정의: Service 레이어의 모든 메소드
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() { }
// AfterReturning 어드바이스 정의
@AfterReturning(pointcut = "serviceLayer()", returning = "result")
public void logAfterReturning(Object result) {
System.out.println("Method returned value is : " + result);
}
}
- @Before
- 대상 메소드 실행 전에 특정 로직을 수행합니다.
- 메소드가 실행되기 전 필요한 파라미터 검증이나 초기 설정을 수행할 수 있습니다.
- 메소드 실행 전 정보를 로그로 남길 수 있습니다.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
// 포인트컷 정의: Service 레이어의 모든 메소드
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() { }
// Before 어드바이스 정의
@Before("serviceLayer()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before executing method: " + joinPoint.getSignature().getName());
}
}
'개발 > Spring' 카테고리의 다른 글
응집도와 결합도란? (1) | 2023.12.26 |
---|---|
[Querydsl] DTO 조회 방법 (0) | 2023.12.17 |
OAuth2란? OAuth2 예시 (1) | 2023.12.15 |
DTO <-> Entity 변환 이유! (0) | 2023.12.14 |