Framework/Spring

[Spring] AOP(Aspect-Oriented Programming)에 대해서 - 컴도리돌이

컴도리돌이 2024. 9. 4. 09:00
728x90

개발을 하면서 "이 부분은 코드가 반복되는데, 더 좋은 방법이 없을까"라는 생각을 정말 매 순간 하는 거 같아요. 🥲 그중 하나의 해결 책으로 AOP가 그 해답이 될 수 있어요 🤔


AOP(Aspect-Oriented Programming)

AOP는 프로그램의 특정 동작을 관심사라는 개념으로 분리해서 관리하는 기법이에요. 이게 무슨 말이냐면, 코드에서 여러 곳에서 반복되는 공통된 로직을 한 곳에 모아둔 후, 필요할 때마다 그 로직을 실행하게 할 수 있다는 거예요. 이렇게 하면 코드의 중복을 줄이고, 유지 보수가 훨씬 쉬워지죠 👍 
예를 들어, 로깅이나 트랜잭션 관리와 같은 로직은 다양한 메서드에서 공통적으로 사용돼요. 만약 이런 로직을 각 메서드에 직접 작성한다면 코드가 지저분해질 뿐 아니라, 유지보수도 어려워질 거예요. 더 나아가, 만약 모든 메서드에서 로깅 코드를 변경해야 한다면,,, 🫠🫠
이 작업은 아주 번거롭고 시간이 많이 들겠죠?? AOP를 사용하면, 이런 불편한 점을 해결할 수 있어요. 
 
AOP를 사용하면, 로깅이나 트랜잭션 관리와 같은 공통된 기능을 횡단 관심사(Cross-Cutting Concern)로 정의하고, 애플리케이션의 핵심 비즈니스 로직과 분리해서 관리할 숭 있어요. 이렇게 하면 비즈니스 로직은 그 자체에만 집중할 수 있고, 다른 부가적인 로직은 AOP가 알아서 처리해 주죠. 


 
저는 트랜잭션 처리를 할 때 보통 DefaultTransactionDefinition을 사용해 트랜잭션을 수동으로 관리하는 일이 적지 않게 있었어요. 🙂
예를 들어, 새로운 트랜잭션을 시작하고, 작업을 수행한 후 정상적으로 끝나면 커밋하고, 예외가 발생하면 롤백하는 방식이죠. 일반적으로 다음과 같이 사용해요.

DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus status = transactionManager.getTransaction(def);

try {
    // 비즈니스 로직 실행
    transactionManager.commit(status);
} catch (Exception e) {
    transactionManager.rollback(status);
    throw e;
}

 
이 코드는 새로운 트랜잭션을 시작하고, 비즈니스 로직을 실행한 후, 성공적으로 완료되면 트랜잭션을 커밋하고, 예외가 발생하면 롤백하는 전형적인 패턴이에요. 이렇게 하면 트랜잭션이 안전하게 관리될 수 있지만, 코드의 중복이 발생하기 쉽고, 비즈니스 로직에 트랜잭션 관리 코드가 섞여 있어 가독성이 떨어질 수 있어요. 🤔


AOP(Aspect-Oriented Programming) 코드 예시

AOP를 통해 트랜잭션 관리 로직을 분리하면, 비즈니스 로직은 트랜잭션에 대한 걱정 없이 깔끔하게 유지할 수 있어요. 

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

@Aspect
@Component
public class TransactionAspect {

    private final PlatformTransactionManager transactionManager;

    public TransactionAspect(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    @Around("@annotation(com.example.TransactionalService)")
    public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            Object result = joinPoint.proceed();
            transactionManager.commit(status);
            return result;
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw e;
        }
    }
}

 
이 코드에서는 AOP를 활용해 트랜잭션 관리 로직을 깔끔하게 분리했어요. @Around 어노테이션을 사용해 트랜잭션을 관리할 메서드 호출 전후에 특정 로직을 실행하도록 했죠. @annotaion(com.example.TransactionalService)는 특정 어노테이션이 붙은 메서들만 이 로직이 적용되도록 설정하여, 비즈니스 로직에서는 트랜잭션 관리에 대한 걱정 없이 핵심 기능에만 집중할 수 있게 됩니다. 👍


AOP는 공통 기능을 깔끔하게 분리하고, 비즈니스 로직을 더 효율적으로 관리할 수 있는 멋진 도구인 것 같아요. AOP 덕분에 로깅이나 트랜잭션 관리가 한층 쉬워졌다는 걸 실감했고, 다음에 비슷한 트랜잭션 코드를 작성할 때는 AOP를 적극적으로 도입해 봐야겠어요 🤔


Aspect Oriented Programming with Spring :: Spring Framework

Aspect-oriented Programming (AOP) complements Object-oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enabl

docs.spring.io

1. AOP란?

Aspect-oriented Programming (AOP) complements Object-oriented Programming (OOP) by providing another way of thinking about program…

medium.com