📖/spring(김영한)
스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 5일차
모팔구
2023. 8. 3. 15:02
728x90
반응형
섹션7 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를 사용하는 것이 수월하다
- 이에 따른 문제가 있다
AOP 적용
- AOP: Aspect Oriented Programming
- 공통 관심 사항(cross-cutting concern) vs 핵심 관심 사항(core concern) 분리
- 시간 측정용 클래스를 만든다
@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"); } } }
- @Aspect: AOP 클래스로 사용할 클래스에 해당 어노테이션을 추가한다.
- @Component: 스프링 빈으로 등록해야 AOP를 사용할 수 있다. 해당 어노테이션으로 등록하는 방법이 있고 SpringConfig 클래스에 @Bean 어노테이션을 이용해 등록하는 방법도 있다
- @Around("${pattern}"): (https://pamyferret.tistory.com/51 참고)지정된 패턴에 해당하는 메서드가 실행되는 동안 동작한다. 이 어노테이션이 붙은 메서드의 반환 값은 Object여야 한다. pattern 자리에는 특정 패턴을 지정한 문자열이 들어간다. 이를 pointcunt 표현식이라고 한다
- pointcut: 작성 방법은 여러가지가 있지만 와일드카드를 이용해서 작성하는 것으로 *(모두), ..(0개 이상)을 사용할 수 있다. "" 상에 작성할 속성은 아래를 참고한다.
- execution: 제일 기본적인 방법으로 특정 메서드를 지정하는 패턴을 작성할 수 있다. 위 예시에서는 접근제어자는 작성하지 않고 모든 반환타입을 지정, hellospring의 0개 이상의 클래스와 모든 메서드, 0개 이상의 인자를 선택했다 그러니까 모든 메서드의 동작 시간을 체크한다고 볼 수 있다.
@Around("execution([접근제어자] 반환타입 패키지.패키지.패키지.클래스.메서드(인자))")
- within: 특정 클래스 내 모든 메서드를 지정하는 패턴을 작성할 수 있다.
@Around("within(패키지.패키지.패키지.클래스)")
- @within: 특정 어노테이션 타입을 갖는 객체에 대해 aop를 지정하기 위해 사용한다. 다른 속성처럼 경로를 작성핮 않고 aop를 적용할 메서드가 있는 클래스 타입을 지정한다.
@Around("@within(org.springframework.stereotype.Controller)")
- this, target: 특정 클래스를 상속 받는 하위 객체를 지정하는 표현식이다. this는 CGLB 기반 프록시로 생성되는 객체를 지정할 때 사용한다. target은 JDK 기반 프록시를 사용하는 것에 붙인다.
예를 들어 아래와 같이 TestImpl이라는 인터페이스가 있고 TestImpl을 상속받은 하위 객체들에 대해 aop를 적용하고 싶으면 아래처럼 사용한다
@Around("this(com.pamyferret.impl.TestImpl)") @Around("target(com.pamyferret.impl.TestImpl)")
- @target: 특정 타입의 어노테이션이 붙어있는 객체를 지정할 수 있는 점에서 @within과 똑같이 동작한다. 다만 @target은 런타임 때 객체가 일치하는지 확인하므로 해당 어노테이션의 retention이 Runtime이어야 한다.
@Around("@target(org.springframework.stereotype.Repository)")
- @annotation: 특정 어노테이션이 붙은 객체에 aop를 적용시킨다. @target과 @within보다 더 넓은 범위로 사용할 수 있는 듯하다. 커스텀 어노테이션에 대해 aop를 적용할 때 @annotation을 많이 사용한다.
@Around("@annotation(com.pemyferret.test.CustomAnnotation)")
- execution: 제일 기본적인 방법으로 특정 메서드를 지정하는 패턴을 작성할 수 있다. 위 예시에서는 접근제어자는 작성하지 않고 모든 반환타입을 지정, hellospring의 0개 이상의 클래스와 모든 메서드, 0개 이상의 인자를 선택했다 그러니까 모든 메서드의 동작 시간을 체크한다고 볼 수 있다.
- pointcut: 작성 방법은 여러가지가 있지만 와일드카드를 이용해서 작성하는 것으로 *(모두), ..(0개 이상)을 사용할 수 있다. "" 상에 작성할 속성은 아래를 참고한다.
- AOP 적용 전
- AOP 적용 후 의존 관계
섹션8 다음으로
다음으로
-
728x90
반응형