Intro
AOP는 Aspect Oriented Programming의 약자로, 관점 지향 프로그래밍이라는 뜻을 갖고 있다.
여러 모듈들에서 공통 관심 사항을 묶어 핵심 관심 사항과 따로 분리시켜버리는 것이다.
예를 들어 모든 함수의 실행시간을 측정하고 싶다고 할 때, 원래는 함수 하나하나 들어가서 아래와 같이 시간 측정 코드를 넣어줘야 한다.
private void function() {
long start = System.currentTimeMillis();
try {
//기존 코드
}
finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("Total time: " + timeMs + "ms");
}
}
물론 함수의 양이 적으면 금방 하겠지만, 함수가 1000개, 10000개가 되면 그 함수마다 저런 짓을 해줘야 되는데,
이렇게 하면 효율성도 떨어질뿐더러 시간 측정 방식이 바뀌면 또 1000개, 10000개의 함수를 일일이 다 수정해야 한다.
그런데 여기서 AOP를 사용하면 기존 함수를 수정하지 않고 시간 측정 로직을 추가할 수 있다.
AOP 적용하기
프로젝트에 aop 패키지를 추가하고 TimeTraceAop 클래스를 만든다.
그리고 아래의 함수를 추가해주자.
TimeTraceAop.java
package com.devjaewoo.hellospring.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
public class TimeTraceAop {
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
}
}
그 다음 함수 시간 처리 로직을 넣는데, 원래 기능이 있어야 할 자리에 joinPoint.proceed() 함수를 넣는다.
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 + " " + timeMs + "ms");
}
}
그다음 TimeTraceAop에 @Aspect 어노테이션을 달아주고, execute 함수 위에 아래의 코드를 추가하자.
패키지 명은 자신의 코드에 맞게 변경해야 한다.
@Around("execution(* com.devjaewoo..*(..)) && !target(com.devjaewoo.hellospring.SpringConfig)")
프로젝트 내의 모든 클래스와 모든 함수에 적용하되, SpringConfig 클래스는 제외하라는 뜻이다.
최종 코드는 다음과 같다.
TimeTraceAop.java
package com.devjaewoo.hellospring.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class TimeTraceAop {
@Around("execution(* com.devjaewoo..*(..)) && !target(com.devjaewoo.hellospring.SpringConfig)")
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 + " " + timeMs + "ms");
}
}
}
이제 이 AOP를 적용시키기 위해선 Bean에 등록해야 하는데, Bean에 등록하는 방법은 2가지가 있다.
하나는 @Configuration 파일에 넣는 것이고, 나머지 하나는 클래스에 직접 @Component 어노테이션을 추가해주는 것이다.
AOP의 경우 직관성을 위해 @Component 보단 @Configuration 파일에 직접 등록해 주는 방식을 권장한다고 한다.
이전에 만들었던 SpringConfig에 아래의 코드를 추가해주자.
@Configuration
public class SpringConfig {
...
@Bean
public TimeTraceAop timeTraceAop() {
return new TimeTraceAop();
}
}
서버를 돌려보면 아래의 사진과 같이 각 함수가 실행을 마칠 때마다 실행 시간이 출력되는 것을 볼 수 있다. 이제 TimeTraceAop 클래스의 함수만 수정해주면 모든 시간 측정 로직을 한번에 변경할 수 있다.
AOP의 원리는 기존의 의존관계에 프록시를 씌워 프록시에서 전/후처리를 하고, 중간에 실제 코드를 호출하는 방식이다.
그림으로 나타내면 다음과 같다.
memberController가 호출되기 전에 프록시에서 시간 측정을 시작하고, joinPoint.proceed() 함수를 통해 실제 memberController를 호출한다.
memberController 호출이 끝나면 joinPoint.proceed() 함수가 종료되고 그 다음인 시간 출력 로직으로 넘어가서 콘솔 창에 뜨는 것이다.
AOP를 잘 사용해서 반복적인 로직을 하나로 묶어 효율적으로 관리해보도록 하자.
출처
'Study > Spring Boot' 카테고리의 다른 글
[Spring Boot] Spring과 Android에서 RabbitMQ 사용해보기 (0) | 2022.09.07 |
---|---|
[Spring Boot] Spring Data Redis 사용해보기 (0) | 2022.09.04 |
[Spring Boot] Spring Data JPA 써보기 (0) | 2022.02.15 |
[Spring Boot] JPA로 JdbcTemplate 대체하기 (0) | 2022.02.15 |
[Spring Boot] 스프링 통합 테스트 케이스 만들기 (0) | 2022.02.15 |