AOP가 뭔지 가볍게 살펴볼 예정
1. Aspect Oriented Programming
0. 개요
어느 한 함수 처리에 걸리는 시간 측정하고 싶다
- 실제 서비스의 흐름과는 별개
ex ) 로그 남기기 / 서비스 제공을 위한 기능
- 서로 직접적인 연관은 없음
ex ) 게시글 읽기, 게시판 목록 불러오기 -> 연관성 없지만 같은 기능의 일부분 구현해줘야됨.
-> 동일한 코드 반복해서 들어가게됨-> 좋은 코드 아님
1. AOC
: 서로 다른 비즈니스 로직이 공통적으로 가지는 관심에 대해 고민하는 개발 지향
- log를 남기는 기능 -> 어떤 비즈니스 로직에 포함되어야함
- but 반복되면 소스코드 복잡, 지저분해짐
3가지 시나리오 살펴보기
- 실행할 때 걸리는 시간
- 실행할 때 들어오는 함수의 인자
- 돌아나가게 되는 함수의 반환값
을 동일하게 logging 하는 것이 이번강의 목표
2. AOP의 기본 개념
0. 개요
카페 case
- 카페 생성, 게시판 생성, 게시글 생성 각각 buisness logic
- 어떤 user가 사용할지, 걸리는 과정, 시간, db 넣는 과정 필요
- 횡단 관심 해소하기 위해 등장한 것이 AOP
횡단 관심(Cross Cutting Concerns) : 여러가지의 핵심 기능 사이를 횡단
1. AOP 기본개념
일반적인 프로그램의 flow
실행 -> class 객체화되어 생성 -> (함수,예외갖음)-> 서로 기능가진 객체들이 상호작용하며 프로그램 기능 읽어냄 -> 종료시 까지 진행
Logging Aspect : logging 하고 싶은 관점
Join Points : 관점이 적용될 수 있는 지점
- 함수 실행 이전
- 함수 반환 이후
- 예외 발생 시
Pointcut : Join Point 정의 하기 위해 지정 -> 규칙을 가진 문자열로 표현됨
Advice : logging aspect 관점에서 실제로 실행 될 함수
logging aspect와 advice, pointcut 을 코드로 적용 할 것
실습 1 - Around PointCut 생성
aspect 패키지 작성
annotation의 형태로 aspect 작성
1. annotation 만들기
@Target(ElementType.METHOD)
- 어떠한 것에 붙을 수 있는 annotation인지 알려줌
- elementType. Method -> 함수에 붙을 수 있음
@Retention(RetentionPolicy.RUNTIME)
annotation 이 실제 기능에 연관될지, 컴파일 과정에 필요할 지, 그냥 표기용도로 사용 될 수 있음
- annotation이 어느 시점까지 컴퓨터상에 존재 할 것인지
- RetentionPolicy - SOURCE : 소스코드 상에 작성 위해
- CLASS : 컴파일러에선 읽어들임, 실행중엔 VM에 들어가지 않음( 실행 할 땐 상관없는 annotation)
- RUNTIME : annotation이 정상적으로 실행 할 때 작동됨
2. annotation에 따라 작동할 aspect만들기
1) build.gradle
spring AOP는 dependency를 추가해야됨
implementation 'org.springframework.boot:spring-boot-starter-aop'
2) LoggintAspect
@Component
@Aspect
- @Component : bean의 일종으로 선언해야됨.
- @Aspect : springboot에선 aspectj를 사용해서 AOP구현
- log 작성
3. LogExecutionTime에 대한 advice
OOP의 관점에서 하나의 객체에서는 하나의 역할을 하는게 좋으니 하나의 advice를 한 객체 안에서 정의
public Object logExecutionTime() throws Throwable{
long startTime = System.currentTimeMillis();
long execTime = System.currentTimeMillis() - startTime;
logger.trace("method executed in {}", execTime);
return null;
}
실행시간 측정 long startTime, long execTime
4. advice pointcut
advice를 실제로 프로그램 상에 적용하기 위해 어떤 join point에 들어갈 지 필요한 pointcut
@Around(value ="@annotation(LogExecurtionTime)")
1) @Around()
: 특정 pointcut을 기준으로 주위에서 작동하게 되는 함수일 때 사용
- LogExecutionTime이라는 annotation 주변에서 일어나는 일이 아래 함수임을 선언.
- aspectj에서 요구하는 정규 표현식으로 작성 해야됨
2) 함수 안에 인자 넣기
ProceedingJoinPoint
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable{
long startTime = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long execTime = System.currentTimeMillis() - startTime;
logger.trace("method executed in {}", execTime);
return proceed;
}
일반적인 joinpoint : 일어났을 때, 그 시점
joinPoint.proceed()
- proceed 함수가 추가된 확장형 : 다음에 실행될 advice, 함수 자체로 진행을 한다는 뜻
- 결과값을 받고 그 사이에서 얼마나 시간이 흘렀는지 log로 작성, 돌아온 proceed 반환하게 함.
실습2 - Before PointCut 생성
LogArguments 생성
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
1. aspect 에서 @Before 사용
@Before(value="@annotation(LogParameters)")
public void logParameters(JoinPoint joinPoint){
MethodSignature methodSignature =(MethodSignature) joinPoint.getSignature();
//어떤 함수 사용했는지
logger.trace("method description : [{}]", methodSignature.getMethod());
//함수의 실제이름
logger.trace("method name :[{}]", methodSignature.getName());
//함수가 실행된 class에 대한 값
logger.trace("declaring class:[{}]",methodSignature.getDeclaringType());
Object[] arguments = joinPoint.getArge();
if(arguments.length==0){
logger.trace("no arguments"); } //인자가 없는경우
for(Object argument : arguments){
logger.trace("argument :[{}]", argument);}
}
1)
@Before(value="@annotation(LogArguments)")
- 실행되기 전에만 잠시 들렸다 돌아가는 함수
- argument를 log하겠다 라고 함
void인 이유
- around pointcut : 실행되기전, 실행된 후 둘다 접근-> 반환했어야 하는 값을 반환해야됨.
- before pointcut : 특정 시점 이전에 무슨일 있었는지 확인하는것 -> 반환할 것이 없음
2)
MethodSignature methodSignature =(MethodSignature) joinPoint.getSignature();
- Signature : 함수가 어떤 모양 갖고 있는지
- joinPoint에서 signature 가져옴
- methodSignature가 joinPoint 인터페이스자체에 안들어가 있어서 형변환 해야됨
3)
Object[] arguments = joinPoint.getArgs();
- joinpoint가 담고 있는 것중 일부가 우리가 찾고 싶음: argument
- 함수에서 들어온 인자들이 argument 변수 안에 들어옴
2. RestController에 추가
실습3 - After PointCut 생성
LogReturn 생성
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
1. @AfterReturning 추가
@AfterReturning(value = "@annotation(LogReturn)", returning ="returnValue")
public void logResults(JoinPoint joinPoint, Object returnValue){
MethodSignature methodSignature =(MethodSignature) joinPoint.getSignature();
logger.trace("method name :[{}]", methodSignature.getName());
logger.trace("return type:[{}]",methodSignature.getReturnType());
logger.trace("return value:[{}]", returnValue);
1) @AfterReturning
- 반환값이 존재하는 함수에 대해 pointcut 지정해 줄 수 있음. (void여도 상관은 없음)
- 반환값 받아오기 위해 사용
2) returning ="returnValue"
- 반환하는 값을 함수에 autowired시켜야됨 -> 어떤 인자에 autowired 시켜줄지를 위해 returning 들어감
- aspectj가 returnValue이름을 가진 인자에 param값을 대입해줌
2. PostController
return 값이 존재하는 함수 찾아 @LogReturn 붙이기
마무리
정확하게 쓰기 위해선 알아야 할 게 많음
영 이해가 안되면 반드시 이해할 필요는 없음.
'SpringBoot' 카테고리의 다른 글
Interceptors & Filters (0) | 2022.03.24 |
---|---|
Exception Handling - 강의 정리 (0) | 2022.03.24 |
Validation - 강의정리 (0) | 2022.03.22 |
Logging -강의 (0) | 2022.03.22 |
Spring Boot Properties - 강의 정리 (0) | 2022.03.22 |