Posts [Spring] Spring AOP(Aspect Oriented Programming)
Post
Cancel

[Spring] Spring AOP(Aspect Oriented Programming)


[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와 같은 효과를 내는 것으로, 아래 링크를 참조하면 좋습니다.

https://refactoring.guru/design-patterns/proxy

일반적으로는 스프링에서 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라는 용어 자체는 생소하지만, 사실 알고 보면 공통 코드를 정의하고 필요한 시점에 어디서든 동일하게 쓸 수 있게 해 주는 프로그래밍 방법이라는 것이죠!


이 포스팅은 예제로 배우는 스프링 입문(백기선 님) 내용에 기반하여 학습한 사항을 정리한 것입니다.