김영한님의 '스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술'을 듣고 적은 글입니다.
AOP가 필요한 상황 ➡️ 모든 메소드의 호출 시간을 측정하고 싶다면 ?
원래였다면 메소드마다 일일히 start, finish 타임을 측정해야했다.
package hello.hellospring.service;
@Transactional
public class MemberService {
/**
* 회원가입
*/
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");
}
}
/**
* 전체 회원 조회
*/
public List<Member> findMembers() {
long start = System.currentTimeMillis();
try {
return memberRepository.findAll();
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("findMembers " + timeMs + "ms");
}
}
}
이렇게 말이다. 하지만 코드가 길어지게 되면 너무나도 오래 걸리고, 시간을 측정하는 기능은 핵심 관심 사항이 아니다. 그리고 시간을 측정하는 로직과 비즈니스 로직이 섞여 유지보수가 어려워진다. 그래서 우리는 AOP 를 적용하려고 한다. (원하는 곳에 공통 관심 사항을 적용하겠다는 것 !)
AOP :
Aspect Oriented Programming의 약자로 관점 지향 프로그래밍이다. 어떤 로직을 기준으로 핵심적 관점, 부가적 관점으로 나누어 보고 그 관점을 기준으로 각각 모듈화한 것이다.
시간 측정 AOP 등록을 위해 aop라는 패키지를 만들고 그 안에 TimeTraceAop.java 파일을 만들어 준다.
package hello.hellospring.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class TimeTraceAop {
@Around("execution(* hello.hellospring..*(..))") //이 패키지 밑으로 찍겠다는 의미
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");
}
}
}
이 코드를 통해 시간을 측정하는 공통 관심 사항을 분리하고 시간을 측정하는 로직을 별도의 공통 로직으로 만들었다. 그리고 시간 측정 로직을 수정하려면 이 로직만 변경하면 된다.
🔹AOP 적용 전 의존관계

🔹AOP 적용 후 의존관계

스프링 컨테이너는 AOP가 적용되면 프록시를 통해 AOP를 실행한다. 프록시는 간단히 말해 가짜 멤버 메소드를 의미한다. 스프링 컨테이너는 프록시를 먼저 앞으로 내세운다. 그리고 joinPoint.proceed() 를 통해 진짜 멤버 메소드를 호출한다.
AOP를 적용한 전체 그림을 살펴보면

각각 프록시를 통해 AOP를 실행하고난 후에야 실제 메서드들을 호출한 것을 볼 수 있다.