[Spring] Spring AOP

사과알러지·2024년 3월 7일
0

Spring

목록 보기
2/5

AOP란?

AOP는 Aspect-Oriented Programming의 약자로

비즈니스 로직과 공통 관심사를 분리하는 프로그래밍 패러다임이다.

왜 필요한가?

AOP를 설명하는 가장 유명한 예시를 가져와봤다.

IT 회사에 근무중인 형준에게 다음과 같은 지시가 내려왔다.

"회원가입에 걸리는 시간을 측정해 로그로 남겨주세요"

public Long join(Member member) {
        validateDuplicateMember(member); //중복 회원 검증
        memberRepository.save(member);
        return member.getId();
    }

형준은 이렇게 작성되었던 코드를

public Long join(Member member) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        try {
        	validateDuplicateMember(member); //중복 회원 검증
        	memberRepository.save(member);
        } finally {
            stopWatch.stop();
            log.info("join spent {} ms", stopWatch.getLastTaskTimeMillis());
        }
        return member.getId();
    }

다음과 같이 수정했다.

한 건 해냈다는 마음으로 보고를 하니 다음과 같은 추가지시가 내려온다.

"이런 로그를 모든 비즈니스 로직에 적용해주면 좋겠어요"

현재 애플리케이션에 존재하는 로직이 백만개가 넘어갔기 때문에

눈앞이 아득해진 형준은 다음날 사직서를 제출한다...


위와 같은 상황의 문제는 무엇일까

  1. 일일이 모든 로직에 코드로 작성해야한다. (사실상 불가능)
  2. 서비스에 비즈니스 로직과 부가기능이 결합된다. (유지보수 및 가독성 낮아짐)

이와 같은 상황을 AOP를 적용하므로써 해결해보자.

횡단 관심사

각 기능에 중복되는 부가 기능을 횡단 관심사라고 한다.

우리의 목표는 횡단 관심사를 비즈니스 로직과 분리하면서

부가기능이 작동하도록 하는 것이다.

적용 예시

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExeTimer {
}

이제 메소드에 @ExeTimer만 붙이면 실행시간을 측정해서 로그로 남기도록 할 것이다.

@Slf4j
@Aspect
@Component
public class ExecutionTimer {

    @Pointcut("@annotation(jpabook.jpashop.service.aop.ExeTimer)")
    private void Timer(){}

    @Around("Timer()")
    public Object traceTime(ProceedingJoinPoint joinPoint) throws Throwable{
        StopWatch stopWatch = new StopWatch();

        try {
            stopWatch.start();
            return joinPoint.proceed();
        } finally {
            stopWatch.stop();
            log.info("{} - Total execution time = {}s",
                    joinPoint.getSignature().toShortString(), stopWatch.getTotalTimeSeconds());
        }
    }
}

타켓은 ExeTimer 어노테이션을 가지는 메소드이며

Advice는 실제 타켓 호출 전후에 실행되어야 하므로 Around 어노테이션을 설정했다.

실제 타켓이 호출되기 전 시간을 측정해 종료되면 시간측정을 종료하고 info 로그로 기록한다.

public Long join(Member member) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        try {
        	validateDuplicateMember(member); //중복 회원 검증
        	memberRepository.save(member);
        } finally {
            stopWatch.stop();
            log.info("join spent {} ms", stopWatch.getLastTaskTimeMillis());
        }
        return member.getId();
    }

이랬던 코드가

@Transactional
@ExeTimer
public Long join(Member member) {
        validateDuplicateMember(member); //중복 회원 검증
        memberRepository.save(member);
        return member.getId();
    }

어노테이션만 붙여도 같은 동작을 하도록 수정되었다.

정상적으로 로그가 출력된다!

profile
노는 건 쉽고 코딩은 어려워

0개의 댓글