본문 바로가기

컴퓨터/Spring Boot

[TIL] Spring Boot(intellij) - AOP

1. AOP란?

- AOP(Aspect-Oriented Programming)는 부가 기능을 모듈화하여 재사용할 수 있도록 지원함으로써 핵심 로직과 부가기능을 분리할 수 있게 하는 기능이다.

- 공통 관심 사항(cross-cutting concern) vs 핵심 관심 사항(core concern)

- 공통 기능이 하나의 장소에서 관리되기 때문에 유지 보수에 용이하다.

 

ex) 모니터링, 로깅, 동기화, 오류 처리, 성능 최적화 등

 

 

 

2. memberService에서 회원 조회 시간 측정 로직 추가

[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");
     }
 }

memberService에 다음과 같이 회원 join 로직이 얼마나 걸리는지 시간을 측정하는 로직을 추가하려고 한다.

만약 위와 같은 로직을 memberService 안의 여러 메서드들에 추가하고자 한다면

추가할 때와 마찬가지로 나중에 수정할 때 역시 여러 코드들을 다 수정해야하기 때문에 

유지보수하기가 굉장히 어려워진다.

 

이 때, AOP 를 사용하여 처리한다면 이런 문제점을 해결할 수 있다.

여기서 각 메서드들은 핵심 로직이고(core concern)

공통 로직으로 처리할 부분은 시간을 측정하는 로직이다.(cross-cutting concern)

 

 

 

3. AOP 적용

src/main/java/myStudyspring.myStudyspring 하위에 aop 디렉토리를 생성하고

시간을 측정하는 공통로직을 생성하기 위해

TimeTraceAop.class를 생성한다.

 

[TimeTraceAop]

package myStudyspring.myStudyspring.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(* myStudyspring.myStudyspring..*(..))")
    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");
        }

    }
}

우선, bean으로 등록해야 하는데

springConfig에 등록하는 방법과 @Component를 사용하는 방법이 있다.

여기서는 @Component를 사용하여 빈을 등록하였다.

 

@Aspect를 사용하여 해당 클래스가 Aspect임을 명시한다.

Aspect는 부가기능(Advice)와 이를 어디에 적용시킬 것인지 결정하는 Pointcut 기능을 담고있는

AOP의 기본 모듈이다.

 

@Around Advice와 관련된 애노테이션 중 하나

@Around를 사용하여 타겟 메서드를 지정해주었다.

 

pointcut 표현식은 execution으로 위 예제에서는 전체 메서드들에 적용되도록 지정하였다.

 

 

*참고

Advice?
 - 실질적으로 프록시에서 수행하게 되는 로직을 정의하게 되는 곳
 - 스프링에서는 관련된 5가지 애노테이션을 제공
 - 각 애노테이션은 메서드에 붙여지며 해당 메서드는 advice의 로직을 정의
 - 애노테이션의 종류에 따라 pointcut에 지정된 대상 메서드에서 advice의 실행 시점을 정할 수 있음
 - 속성값으로 pointcut을 지정하여 타겟 메서드 지정 가능


@Around
 - 나머지 4개의 애노테이션을 모두 포함
 - joinpoint 실행 여부 선택 가능, 여러번 실행 가능

@Before
 - 조인 포인트 실행 이전에 실행

@AfterReturning
 - 조인 포인트 완료 후 실행
 
@AfterThrowing
 - 메서드가 예외를 던지는 경우 실행

@After
 - joinpoint에 상관 없이 무조건 실행

 

joinpoint는 advice가 적용될 수 있는 모든 위치를 말하는 추상적인 개념이다.

스프링 AOP는 프록시 방식을 사용하므로 joinpoint는 항상 메서드 실행 지점이다.

 

[memberController -> 프록시(memberService) -> memberService]

proxy?
- 대리, 대신이라는 뜻으로 중계의 기능을 하는 서버

 

AOP 작성 후 서버 실행 결과

콘솔로 확인 가능하다.

728x90