[Spring] Spring AOP(Aspect Oriented Programming)
스프링은 AOP, 즉 관점 지향 프로그래밍을 지원합니다.
사실 말 자체는 어렵게 들립니다. 쉽게 말하자면, 핵심 코드와 공통으로 진행되는 코드들을 분리하고, 핵심 코드상에서 공통 코드를 사용할 수 있게 하는 것입니다.
간단한 예시를 들어보도록 하겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A {
method a() {
AAA
오늘은 미국 독립기념일이다.
BBB
}
method b(){
AAA
아침에 운동했어요.
BBB
}
}
class B {
method c() {
AAA
점심은요?
BBB
}
}
위에서 모든 메소드 상에 공통 부분은 앞, 뒤로 있는 AAA 부분과 BBB 부분입니다.
이렇게 공통으로 진행해야 하는 코드가 각각의 코드마다 일일이 다 존재하는데, 하나라도 바뀌면 다른 곳에서도 똑같도록 다 바꿔주어야 합니다.
이런 공통 코드를 핵심 코드에서 분리시켜서 모아주는 것을 AOP 라고 합니다.
위의 공통부를 모아 주면 아래와 같은 형태가 되겠죠?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class A {
method a () {
오늘은 7월 4일 미국 독립기념일이다.
}
method b (){
저는 아침에 운동했어요.
}
}
class B {
method c() {
점심은요?
}
}
class AAABBB {
method aaabbb(JoinPoint point){
AAA
point.execute()
BBB
}
}
이제 스프링에서 AOP를 구현하는 다양한 방법에 대해 살펴보겠습니다.
AOP 구현 방법들
- (1) 컴파일을 이용하는 방법
중간 컴파일 단계에 무언가 AOP를 주입하면, 컴파일한 코드에는 등록되는 것처럼 보이는 것. AspectJ를 활용한다.
A.java ——-> — (AOP) — ——-> A.class
- (2) 바이트코드 조작 방법
A.java -> A.class 로 컴파일할 때 classLoader가 .class 파일을 읽어온 뒤 메모리에 올립니다. 그 때 조작하는 방식을 말합니다. 원래는 .class 파일에 공통 부분 등이 없었는데, 클래스 파일을 로딩하는 그 시점에 메모리상에는 공통 부분 코드가 들어가 있는 것입니다. AspectJ를 활용합니다.
- (3) 프록시 패턴 - 스프링에서 사용하는 방법
디자인 패턴 중의 하나를 사용하여 AOP와 같은 효과를 내는 것으로, 아래 링크를 참조하면 좋습니다.
일반적으로는 스프링에서 AOP는 자동으로 해 준다고 생각하면 됩니다. 즉, Bean이 등록될 때 자동으로 Proxy가 만들어지면서 AOP가 구현됩니다.
그러면 스프링에서는 어떻게 사용자가 직접 AOP를 구현하고, 적용할까요?
예를 들어, 각 코드의 실행 시간을 측정하는 공통 코드를 작성해야 한다고 가정해 보겠습니다.
그 실행 시간을 측정하는 것을 @LogExecutionTime 이라는 새로 정의한 어노테이션으로 만들어 보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Component
@Aspect
public class LogAspect {
Logger logger = LoggerFactory.getLogger(LogAspect.class);
// JoinPoint는 실행할 타겟을 말함. 즉 메소드를 받아오고, 그것을 실행하겠다는 의미
// 그 앞뒤에 시간을 재서 출력하는 일들을 정의한 것.
// @Around는, LogExecutionTime 어노테이션을 적용한 주변에다가 알려주겠다는 의미이다.
@Around("@annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable{
///앞 공통///
StopWatch stopWatch = new StopWatch();
stopWatch.start();
///////
Object proceed = joinPoint.proceed();
///뒤 공통///
stopWatch.stop();
logger.info(stopWatch.prettyPrint());
////////
return proceed;
}
}
...
public @interface LogExecutionTime {
}
위의 코드를 보시면, Aspect로 정의한 부분이 공통 기능을 작성한 부분입니다.
@Component는 이전 포스트에서 말했지만 Bean으로 등록해 주는 어노테이션이고, @Aspect 어노테이션은 스프링에서 AOP를 구현하기 위해 붙입니다.
그리고 joinPoint는 그 공통 기능을 적용할 부분(메소드)을 말합니다. 위에서는, joinPoint 앞, 뒤로 수행 시간을 체크하는 공통 코드를 넣은 것입니다.
@Around는 적용 시점을 의미하며 그 시점은 이 메소드 실행 전, 후에 공통 코드를 적용하고 싶을 때 사용합니다.
@Before 는 이 메소드 실행 전에, @After 는 이 메소드 실행 후에 공통 코드를 수행하고 싶을 때 사용합니다.
@Around(“@annotation(LogExecutionTime)”) 이라 선언하면, @LogExecutionTime 어노테이션이 붙은 곳에 이 AOP를 적용한다 라는 의미가 됩니다.
이후 이렇게 만든 @LogExecutionTime 어노테이션을, 실행 시간을 측정하고 싶은 메소드 위에 어노테이션으로 붙이면, 수행 시간을 출력해 주게 됩니다.
사실 위처럼 스프링에서 AOP를 직접 구현해 보면서 그 의미를 직접 느끼는 것이 더 중요하다고 생각됩니다.
AOP라는 용어 자체는 생소하지만, 사실 알고 보면 공통 코드를 정의하고 필요한 시점에 어디서든 동일하게 쓸 수 있게 해 주는 프로그래밍 방법이라는 것이죠!
이 포스팅은 예제로 배우는 스프링 입문(백기선 님) 내용에 기반하여 학습한 사항을 정리한 것입니다.