본문 바로가기

개발일지/Spring

[Spring] 스프링 입문 - AOP

AOP가 필요한 상황

모든 메소드의 호출 시간을 측정해야 한다면?

public Long join(Member member) {
        // 메서드 호출 시간 측정
        long start = System.currentTimeMillis();

        try {
            validateDuplicateMember(member);    // 중복 회원 검증
            memberRepository.save(member);
            return member.getId();
        } finally {
            long finish = System.currentTimeMillis();
            long timeMs = finish - start;
            System.out.println("join = "+ timeMs + "ms");
        }
    }

핵심 비즈니스 로직은 핵심 관심 사항 이고

이렇게 시간을 측정하는 로직은

모든 메소드의 공통 관심 사항 이다

 

핵심 관심 사항과 공통 관심 사항이 섞이면 유지보수에 어려움이 있다

 

AOP 적용

공통 관심 사항과 핵심 관심 사항을 분리하기

 

1. TimeTraceAop 클래스 생성

 

@Aspect 어노테이션 사용

 

 

@Aspect
public class TimeTraceAop {
    public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("START : " + joinPoint.toString());
        try {
            return joinPoint.proceed();
        } finally {
            long finish = System.currentTimeMillis();
            long timeMs = finish - start;
            System.out.println("END : " + joinPoint.toString() + " " + timeMs + "ms");
        }
    }
}

메서드 호출 시간을 출력하는 코드를 작성한다

 

 

2. 스프링 빈으로 등록하기

@Bean
    public TimeTraceAop timeTraceAop() {
        return new TimeTraceAop();
    }

SpringConfig 클래스에 직접 등록하는 게 좋다

→ AOP가 등록되어서 쓰이는 것을 확인하기 좋음

 

 

 

3. 타겟팅

@Aspect
@Component
public class TimeTraceAop {
    @Around("execution(* hello.hellospring..*(..)) && !target(hello.hellospring.SpringConfig)")
    public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {

🚨 순환 참조 문제가 발생했다

https://www.inflearn.com/questions/48156/aop-timetraceaop-%EB%A5%BC-component-%EB%A1%9C-%EC%84%A0%EC%96%B8-vs-springconfig%EC%97%90-bean%EC%9C%BC%EB%A1%9C-%EB%93%B1%EB%A1%9D

참고한 레퍼런스

 

 

스프링부트 버전 2.1 이상은 Bean이 중복 생성됨

따라서, @Component 어노테이션을 제거했다

 

 

서버를 띄우고

회원 목록을 들어가게 되면

다음과 같이 콘솔창에 메시지가 뜬다

 

 

시간 측정 로직을 공통 로직으로 만들어서

핵시 관심사항과 공통 관심사항을 분리했음을 확인

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

핵심 관심사항을 깔끔하게 유지할 수 있다

 

 

@Around("execution(* hello.hellospring..*(..)) && !target(hello.hellospring.SpringConfig)")

원하는 적용 대상을 선택할 수 있다

 

 

그래서 AOP가 어떻게 동작하는데!

스프링 컨테이너에 스프링 빈을 등록할 때

가짜 스프링 빈을 앞에 세워둔다

joinPoint.proceed() 가 호출 되면

그 때 진짜 스프링 빈을 호출

 

 

helloController가 호출하는 것은

프록시라고 하는 가짜 멤버 서비스

 

 

 

 

@참고자료

[인프런] 김영한 - 스프링 입문 강의 (무료)