AOP란?

최경현·2024년 8월 31일

소프트웨어 개발 방법론 중에서는 AOP (Aspect Oriented Programming , 관점 지향 프로그래밍)
와 OOP(Object Oriented Programming, 객체 지향 프로그래밍)가 있다.

AOP는 OOP와 마찬가지로 모듈화해서 재사용 가능한 구성을 만드는 것이다. 차이점은
OOP는 데이터와 행동을 객체 단위로 모듈화하는데 초점을 두는 방법론이고,
AOP는 이를 보완하여 공통 기능들을 가로채거나 결합하여 처리를 도와주는 방법이다.

그 중에서 AOP에 대해 설명하겠다.

AOP란?

AOP는 (Aspect Oriented Programming)의 약자로 관점 지향 프로그래밍이라고 불린다.
쉽게 말해
어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화 하겠다는 의미이다.
모듈화란 어떤 공통된 로직이나 기능을 하나의 단위로 묶는것을 말한다.

AOP에서 각 관점을 기준으로 로직을 모듈화한다는 것은 코드들을 부분적으로 나누어서 모듈화하겠다는 의미이다.
여기서 소스 코드상에서 다른 부분에 계속 반복해서 쓰는 코드들을 발견할 수 있는데 이것을 흩어진 관심사(Crosscutting Concerns)라 부른다.


위의 사진과 같이 흩어진 관심사를 Aspect로 모듈화하고 핵심적인 비즈니스 로직에서 분리하여 재사용하겠다는 것이 AOP의 목적이다.

AOP를 알기 위해서는 주요 개념들에 대한 확실한 이해가 필요한데, 설명하도록 하겠다.

AOP의 주요 개념

1. Aspect : 흩어진 관심사를 모듈화 한 것. 즉, 공통적인 관심사를 모듈화 한 것이다.
Ex ) 로깅, 보안, 트랜잭션 관리, 캐싱, 예외 처리, 성능 모니터링 등..

@Aspect
@Component
public class AuthenticationAspect {

    @Before("execution(* PaymentService.processPayment(..))")
    public void authenticate() {
        System.out.println("사용자 인증 중...");
    }
}

여기서 AuthenticationAspect는 Aspect이다. 해당 Aspect는 ProcessPayment 메소드가 호출되기 전에 사용자가 인증되었는지 확인하는 구문이다.

2. Join Point : 애플리케이션의 실행 중, Aspect가 적용될 수 있는 특정 지점이다.
Ex ) 메소드 호출, 객체 생성, 예외 처리 등..

public class PaymentService {
    public void processPayment(String account, double amount) {
        System.out.println("계정 결제 처리 중...: " + account + " 금액: " + amount);
    }
}

여기서 PaymentService 클래스의 processPayment가 Join Point이다.
processPayment 메서드가 실행될 때, Aspect가 개입할 수 있는 지점이 된다.

3. Advice : Join Point에서 실제로 수행될 동작이다.

종류
Before : Join Point 전에 실행
After : Join Point 후에 실행
Around : Join Point 전후에 실행
AfterReturning : 성공적으로 반환된 후에 실행
AfterThrowing : 예외가 발생한 후에 실행

@Before("execution(* PaymentService.processPayment(..))")
public void authenticate() {
    System.out.println("사용자 인증 중...");
}

여기서 processPayment 메서드에 접근하기 전에 인증을 확인하는 로직이다.
authenticate 메서드 자체가 Before Advice이다.
즉, 이 Advice는 processPayment 메서드가 호출하기 전에 실행된다.

4. Pointcut : 어떤 Join Point에서 Advice가 적용될지를 결정하는 기준, 특정 메서드나 클래스에서 발생하는 이벤트에 대한 필터링 조건을 정의

@Before("execution(* PaymentService.processPayment(..))")
public void authenticate() {
    System.out.println("사용자 인증 중...");
}

여기서 execution(* PaymentService.processPayment(..))가 Pointcut 표현식이다.
이 표현식은 PaymentServiceprocessPayment 메서드에 Advice를 적용한다.

5. Weaving : Aspect와 실제 비즈니스 로직을 결합하는 과정이다. 위빙은 컴파일 시간, 로드 시간, 런타임 중에 일어날 수 있다.
애플리케이션을 실행할 때, PaymentServiceprocessPayment 메소드가 호출되기 전후로 AuthenticationAspectauthenticate 메소드가 자동으로 호출되도록 결합한다. 이 결합 과정이 바로 위빙이다.

이제 AOP를 활용한 간단한 FLOW에 대해 간단하게 설명하려고 한다.

AOP FLOW 예시

1. 로깅 : AOP를 사용하면 모든 메서드 호출에 대한 로그를 중앙에서 관리할 수 있다. 일일이 로깅 코드를 추가할 필요가 없다.

@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* com.example..*(..))")
    public void logBefore() {
        System.out.println("메서드 실행 전에 호출합니다.");
    }

    @After("execution(* com.example..*(..))")
    public void logAfter() {
        System.out.println("메서드 실행 후에 호출합니다.");
    }
}

로깅을 AOP로 처리하면, 여러 클래스와 메서드에 걸쳐 있는 로깅 코드를 중앙에서 관리할 수 있어 중복을 줄이고 유지보수성을 높일 수 있다.

2. 보안 : 특정 메서드나 리소스에 접근할 때 사용자의 권한을 확인. AOP로 이 과정을 처리하면 각 메서드에 일일이 보안 검사를 추가하지 않고, Aspect를 통해 처리할 수 있다.

@Aspect
@Component
public class SecurityAspect {

    @Before("execution(* com.example.PaymentService.*(..))")
    public void checkAuthentication() {
        System.out.println("유저 권한 쳌쳌...");
    }
}

보안 로직을 비즈니스 로직에서 분리하면 코드가 더 간결해지고, 인증 및 권한 검사 관련 기능을 한 곳에서 집중적으로 관리할 수 있다.

3. 트랜잭션 관리 : 데이터베이스 작업에서 핵심적인 역할을 한다. 여러 작업이 하나의 트랜잭션 단위로 묶여야 할 때, AOP를 사용하면 트랜잭션 처리 코드를 비즈니스 로직과 분리할 수 있다.

@Aspect
@Component
public class TransactionAspect {

    @Before("execution(* com.example.TransactionService.*(..))")
    public void startTransaction() {
        System.out.println("트랜잭션 시작하는 중...");
    }

    @AfterReturning("execution(* com.example.TransactionService.*(..))")
    public void commitTransaction() {
        System.out.println("트랜잭션 커밋 성공쓰...");
    }

    @AfterThrowing("execution(* com.example.TransactionService.*(..))")
    public void rollbackTransaction() {
        System.out.println("트랜잭션 예외 발생. 롤백 중...");
    }
}

AOP를 사용하면 트랜잭션 관리 로직을 비즈니스 로직에서 분리할 수 있다. 트랜잭션 처리 코드는 한 곳에서 관리되며, 오류가 발생할 경우 자동으로 롤백 처리가 된다.

4. 캐싱 : 데이터베이스나 외부 API를 반복적으로 호출하는 비용을 줄이기 위해, 자주 사용되는 데이터를 메모리에 저장하고 재사용하는 기법. AOP를 사용하면 특정 메서드에 대한 캐싱을 자동으로 처리할 수 있다.

@Aspect
@Component
public class CachingAspect {

    private Map<String, Object> cache = new HashMap<>();

    @Around("execution(* com.example.DataService.getData(..))")
    public Object cacheData(ProceedingJoinPoint joinPoint) throws Throwable {
        String key = Arrays.toString(joinPoint.getArgs());
        if (cache.containsKey(key)) {
            System.out.println("키에 대한 캐시 데이터 반환: " + key);
            return cache.get(key);
        } else {
            Object result = joinPoint.proceed();
            cache.put(key, result);
            System.out.println("키에 대한 캐시 대이터 캐싱: " + key);
            return result;
        }
    }
}

캐싱이란?
자주 사용하는 데이터를 메모리에 저장해두고, 이후 동일한 데이터를 요청할 때 메모리에서 바로 가져와서 응답속도를 높이는 기법.
일반적으로 DB나 외부 API와 같은 느린 자원에서 데이터를 가져오는 대신, 캐시 메모리에서 데이터를 빠르게 조회할 수 있다. 캐싱은 성능을 향상시키고 시스템 부하를 줄이는 유용한 방법이다.

5. 예외처리 : 애플리케이션에서 발생하는 오류를 처리하는데 사용. AOP로 예외 처리를 구현하면, 모든 예외 처리를 한 곳에서 중앙 집중식으로 관리할 수 있다.

@Aspect
@Component
public class ExceptionHandlingAspect {

    @AfterThrowing(pointcut = "execution(* com.example.*.*(..))", throwing = "exception")
    public void handleException(JoinPoint joinPoint, Throwable exception) {
        System.out.println("메서드 예외쓰..: " + joinPoint.getSignature());
        System.out.println("예외 메세즹..: " + exception.getMessage());

    }
}

AOP로 예외 처리를 구현하면, 애플리케이션의 모든 예외를 한 곳에서 관리할 수 있어 코드의 일관성이 높아지고, 예외 처리 로직의 중복을 줄일 수 있다.

Spring 설정

스프링 애플리케이션에서 모든 Aspect를 활성화하기 위해서는 설정 추가가 필요하다.

@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.example")
public class AppConfig {
    // 이런 식으로 설정 추가하면 됨. 예시임
}

요약하자면, AOP(Aspect Oriented Programming)는 프로그램에서 공통적으로 반복되는 부분을 핵심 로직에서 분리해서 따로 관리하는 방법이다.
AOP를 비즈니스 로직을 방해하지 않고, 꼭 필요한 부가적인 작업을 깔끔하게 처리해주는 도우미라고 생각하면 된다. 이 방법을 사용하면 코드가 더 깨끗해지고, 유지보수가 쉬워진다.

알아보고 이해했을때는 코드를 작성하면서 적용하면 엄청나게 효율적인 방법 중 하나라고 생각한다.
하지만 나는 이러한 AOP를 유연하게 적용 시킬 수는 없을거라고 생각한다. 아직은..
많은 연습을 해봐야겠다.

출처
https://engkimbs.tistory.com/746

profile
ㅇㅇ

0개의 댓글