Spring/[강좌]스프링 입문-코드로 배우는 스프링 부트(김영한)
[강좌] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - chap 7. AOP - AOP가 필요한 상황
ysk223
2020. 8. 11. 18:45
[강좌] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - chap 7. AOP - AOP가 필요한 상황
AOP
AOP가 필요한 상황
- 만약 모든 메소드의 호출 시간을 측정하고 싶다면?
- 공통 관심 사항(cross-cutting concern) vs 핵심 관심 사항(core concern)
- 회원 가입 시간, 회원 조회 시간을 측정하고 싶다면?
ex )
@Transactional
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) { // Dependency Injection
this.memberRepository = memberRepository;
}
/**
* 회원 가입
* ex) 같은 이름이 있는 중복 회원은 안된다.
*/
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");
}
}
private void validateDuplicateMember(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(m -> { // 이미 존재하는 값이면
throw new IllegalStateException("이미 존재하는 회원입니다.");
});
}
/**
* 전체 회원 조회
*/
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");
}
}
public Optional<Member> findOne(Long memberId) {
return memberRepository.findById(memberId);
}
}
프로그램 중간중간마다 시간을 측정하는 코드를
끼워 넣어야
하는 문제가 생긴다.!!문제점!!
회원가입, 회원 조회에 시간을 측정하는 기능은 핵심 관심 사항이
아니다
.시간을 측정하는 로직
은공통 관심 사항
이다.시간을 측정하는 로직과 핵심 비즈니스의 로직이 섞여서 유지보수가 어렵다.
시간을 측정하는 로직을 별도의
공통 로직
으로 만들기매우 어렵다.
시간을 측정하는
로직을 변경할 때 모든 로직을 찾아가면서 변경
해야 한다.
이 때, 갑자기 스프링 고수가 나타나서 AOP를 끼워 넣으면 된다고 한다 ?!
AOP 적용
AOP: Aspect Oriented Programming
공통 관심 사항(cross-cutting concern) vs 핵심 관심 사항(core concern) 분리
시간 측정 로직을 담고있는 프로그램을 따로 작성하고, 원하는 곳
에 지정
하여 적용시킨다
AOP 클래스 작성
package com.ys.springboot.hellospring.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class TimeTraceAop {
@Around("execution(* com.ys.springboot..*(..))")
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" );
}
}
}
@Around("execution(* com.ys.springboot..*(..))")
- 실행할 클래스들의 패키지명을 작성
- com.ys.springboot.밑의 하위패키지를 모두 포함
- 문법이 따로 있으니 검색해보면 된다.
aop 클래스 작성시
@Aspect
어노테이션을 붙인다
Spring Bean 등록
@Configuration
public class SpringConfig {
...
...
@Bean
public TimeTraceAop timeTraceAop() {
return new TimeTraceAop();
}
}
실행하면 콘솔에 다음과같이 뜬다
- 지정한 메서드를 호출할때마다 인터셉트를 잡아 출력해주는것.
!!해결!!
회원가입, 회원 조회등 핵심 관심사항과 시간을 측정하는 공통 관심 사항을 분리한다.
시간을 측정하는 로직을
별도의 공통 로직으
로 만들었다.핵심 관심 사항
을깔끔하게 유지
할 수 있다.변경이 필요하면
이 로직만 변경
하면 된다.원하는 적용 대상을 선택할 수 있다