AOP (Aspect Oriented Programming)란
AOP는 관점 지향 프로그래밍을 의미한다. 처음 이 단어를 접하면 객체지향(OOP), 절차지향(PP)과 완전히 다른 새로운 개념인지 의문이 들 수 있으나 AOP는 기본적으로 OOP 환경에서만 의미있는 개념이다. 다시 말해 AOP는 OOP를 보완하는 프로그래밍 기법이라고 할 수 있다.
이런 AOP에 대한 정의는 각 기업이나 사이트마다 조금씩 다른데 가장 대표적인 정의는 다음과 같다.
AOP는 횡단 관심사(cross-cutting concern)의 분리를 허용함으로써 모듈성을 증가시키는 것이 목적인 프로그래밍 패러다임이다.
하지만 위 내용만으로는 AOP가 구체적으로 어떤 것인지 와닿지 않는다. AOP를 보다 자세히 이해하려면 다음과 같은 개념을 알고 있어야 한다.
관심사(concern)
컴퓨터 공학(Computer Science)에서 관심사 분리(Separation of Concerns - SoC)란 하나의 프로그램을 여러개의 서로 간섭하지 않는 개별적인 부분으로 나누는 것을 의미한다. 이때 한 부분에 대한 단위를 관심사(concern)라고 정의한다.
따라서 관심사(concern)는 프로그램에 영향을 주는 코드의 집합을 뜻한다.
횡단 관심사(cross-cutting concern)
횡단관심사(cross-cutting concern)란 여러 메소드 또는 클래스에 분산되어 걸쳐있는 관심사를 의미한다. 이런 횡단 관심사의 가장 대표적인 예시는 바로 Logging이다.
만약 모든 메소드가 실행되는데 걸리는 시간을 로그로 남겨야 한다고 하면 Logging은 여러 클래스에 분산되어 걸쳐 있는 관심사, 즉 횡단 관심사가 된다.
관점(Aspect)
관점(Aspect)는 이러한 횡단 관심사를 캡슐화한 모듈을 의미한다.
* 다시 말해 AOP는 횡단 관심사(cross-cutting concern)를 관점(Aspect)으로 모듈화하여 핵심적인 비즈니스 로직에서 분리하여 재사용하겠다는 프로그래밍 개념을 의미한다.
Spring AOP
Spring AOP는 AOP 개념을 구현한 구현체이다. AOP를 구현하는 방법은 다음과 같다.
- 컴파일 시점에 코드에 공통 기능 삽입
- 클래스 로딩 시점에 바이트 코드에 공통 기능 삽입
- 런타임 시점에 프록시 객체를 생성하여 공통 기능 삽입
Spring AOP는 이 중에 3번째 방식을 사용한다.
이러한 Spring AOP의 특징은 다음과 같다.
- 이러한 Spring AOP의 특징은 다음과 같다.
- Spring Bean에만 AOP 적용 가능
- 모든 AOP 기능을 지원하지 않음
(Spring IoC와 연동하여 엔터프라이즈 어플리케이션에서 발생하는 가장 흔한 문제(ex. 중복 코드, Proxy 클래스 작성의 번거로움, 객체들 간 관계 복잡도 증가 등)에 대한 해결책을 지원하는 것이 목적)
Proxy 패턴
프록시 패턴은 대상 원본 객체를 대리하여 대신 처리하게 함으로써 로직의 흐름을 제어하는 행동 패턴이다. 프록시(Proxy)의 사전적인 의미는 '대리인'이라는 뜻이다. 즉, 누군가에게 어떤 일을 대신 시키는 것을 의미하는데, 이를 객체 지향 프로그래밍에 접목해보면 클라이언트가 대상 객체를 직접 쓰는게 아니라 중간에 프록시(대리인)을 거쳐서 쓰는 코드 패턴이라고 보면 된다. 따라서 대상 객체의 메소드를 직접 실행하는 것이 아닌, 대상 객체에 접근하기 전에 프록시(Proxy) 객체의 메서드를 접근한 후 추가적인 로직을 처리한 뒤 접근하게 된다.
예제) 클라이언트는 request() 메소드 호출 -> 프록시 객체가 대신 RealSubject객체의 메소드를 호출하고 그 반환 값을 클라이언트에 전달
[Proxy 패턴의 장점]
- 실제 메소드의 코드를 변경하지 않고 필요한 기능을 추가하는 것이 가능
- 프록시가 내부 캐시를 유지하여 데이터가 캐시에 아직 존재하지 않는 경우에만 대상에서 작업이 실행되도록 할 수 있다.
Spring AOP 예제 코드
- AOP를 사용하여 메소드의 실행 시간을 측정하는 기능 구현
1. 의존성 주입 (SpringBoot, Gradle 기준)
- build.gradle 파일 내 아래 코드 주입
2. 관점(Aspect) 등록
- 방법 1) 특정 패키지에 있는 모든 메소드에 실행시간 측정 메소드를 적용
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class ExecutionTimeAspect {
@Around("execution(* com.example.controlsystemsample.service..*(..))")
public Object measureExecutionTimeByPackage(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long endTime = System.currentTimeMillis();
System.out.println("Execution time of " + joinPoint.getSignature() + " : " + (endTime - startTime) + " ms");
return proceed;
}
}
- 방법 2) 커스텀 어노테이션을 설정해 어노테이션을 적용한 메소드에서만 실행시간 측정 메소드 적용
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MeasureExecutionTime {
}
- 커스텀 어노테이션 생성
@Around("@annotation(com.example.controlsystemsample.common.annotation.MeasureExecutionTime)")
public Object measureExecutionTimeByAnnotation(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long endTime = System.currentTimeMillis();
System.out.println("Execution time of " + joinPoint.getSignature() + " : " + (endTime - startTime) + " ms");
return proceed;
}
- @MeasureExecutionTime 어노테이션이 붙은 메소드에 대해서만 실행시간 측정 메소드 적용
'Spring' 카테고리의 다른 글
@Transactional 동작 방식 (0) | 2024.07.02 |
---|---|
JDK Dynamic Proxy vs CGLIB Proxy (0) | 2024.07.02 |
SL4J와 Logback을 이용한 Logging (0) | 2024.03.03 |
Filter, Interceptor, AOP (0) | 2023.11.29 |
Spring Framework의 특징과 작동 과정 (1) | 2023.11.18 |