카테고리 없음

AOP(Aspect Oriented Programming)

k-hamin 2024. 9. 2. 19:08

AOP(Aspect Oriented Programming)이란

직역하면 관점 지향 프로그래밍이다.

 

관점 지향이란

 

어떠한 로직을 핵심적인 관점 ,부가적인 관점으로 나누어보고,

그 관점을 기준으로 적용처를 정의하고 합쳐 모듈로 만든 것이다.

 

우리는 어플리케이션에서 공통적으로 사용되는 기능을 공통 관심 사항이라고 한다.

 

또한, 애플리케이션이 우리가 원하는 목적에 따라 작동할 수 있게 짠 코드로직을 비즈니스로직이라고 한다.

이러한 비즈니스 로직 같은 애플리케이션의 주목적을 달성하기 위한 핵심 관심 사항이라고 한다.

 

공통관심사항

애플리케이션을 개발하면서 공통적으로 사용되는 기능

 

핵심 관심 사항

비즈니스 로직 같은 애플리케이션의 목적을 달성하기 위한 핵심 비즈니스 로직


 

예를 들어

 

어떤 로직을 처리하는 doSomthing()이라는 메서드가 있다고 하자.

public void doSomething() {
    ...
}

 

그러다 doSomething()이 실행되는 실행시간을 찍어달라는 새로운 요구사항이 들어왔다. 

public void timeCheck() {
   Stopwatch stopwatch = Stopwatch.createStarted();
   doSomething();
   stopwatch.stop();
   log.info("time : " + stopwatch.elapsed(MILLISECONDS));
}
public void doSomething() {
    ...
}

위와 같이 시간을 측정하는 메서드 timeCheck()라는 메서드를 만들었다.

 

timeCheck의 흐름은 위와 같다.

 

실제 비즈니스에 필요한 로직은 doSomething의 실행뿐이고, 나머지는 부가적 기능이라는 것을 알 수 있다.

 

시간이 지나 doSomething2()가 생겨나고, 또다시 시간을 측정해 달라고 요청하였을 때

우리는 timeCheck2()를 구현해야 한다.

 

아주 귀찮겠지...?

 

위처럼 시간 측정 로그 찍기 등 공통되는 부가 관심의 코드를 분리하여

이후에도 재활용하여 사용할 수 있도록 만들어주는 방법이 바로 AOP 관점 지향 프로그래밍이다.

 

 

AOP가 적용된 부분을 봐보자!

 

@Transactional

@Override
@Transactional
public Long createMember(MemberCreateRequest request) {

    Member member = new Member(request.username(), request.age());

    Member save = memberRepository.save(member);

    return save.getId();
}

위 메서드는 DB에 새로운 멤버를 생성하여 저장하는 역할을 수행한다.

 

DB를 수정하는 메서드엔 @Transactional 어노테이션을 달아주어야 하는데

이 어노테이션이 우리가 가장 많이 사용하는 AOP 중 하나이다.


 

@Transactional의 트랜잭션이 무엇일까?

 

트랜잭션은 DB의 상태를 변화시키기 위해 수행하는 작업의 단위를 말한다.

 

작업의 단위란 하나의 트랜잭션이 모두 DB에 반영, 실패한다면 전혀 반영이 되지 않아야 하며 해당 작업을 진행 하며 중간에 다를 작업이 끼어들 수 없다는 것을 의미한다.

 

 

위와 같이 주문 결제 배송 등록으로 이어지는 플로우가 있다고 하자.

만약 배송 등록에 실패하였다면 결제가 취소되어야 할 것이다.

이런 상황에서 프로세스 중 하나가 실패하면 전부 실패하게 하여

DB에 아무것도 적용되지 않은 상태로 돌려주는 것이 바로 @Transactional이다.

 

 

@Transactional라는 AOP가 없다면,

@Service
public class MemberService {

    private final PlatformTransactionManager transactionManager;

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

    public Long createMember(MemberCreateRequest request) {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());

        try {
            // ---- 핵심 관심 영역 ------
            
            Member member = new Member(request.username(), request.age());
            
            Member savedMember = memberRepository.save(member);

            transactionManager.commit(status);
            
            return savedMember.getId();
            
            // ---- 핵심 관심 영역 ------
        } catch (Exception e) {
            // 트랜잭션 롤백
            transactionManager.rollback(status);
            throw e;
        }
    }
}

 

위와 같이 예외처리를 해주어야 할 것이다.

(불편하겠지...?)

 

이러한 트랜잭션은 DB 수정이 발생하는 모든 메서드에 달아주어야 한다.