
Spring에는 Spring Triangle이라고 하는 AOP, IoC/DI, PSA가 있다. 이번 시간에는 AOP에 대해 알아보자.
AOP(Aspect Oriented Programming)
AOP(Aspect Oriented Programming)는 '관점 지향 프로그래밍'이라고 한다.
여기서 말하는 관점 지향이란?
→ 어떤 로직을 기준으로 핵심적인 관점과 부가적인 관점으로 나눠서 그 관점을 기준으로 모듈화 하겠다는 것이다.
그러면 모듈화란?
→ 공통된 로직이나 기능을 하나로 묶는 것이다.
핵심적인 관점과 부가적인 관점은 어떤 것을 의미하는가?
→ 핵심적인 관점은 우리가 적용하고자 하는 핵심적인 비즈니스 로직을 뜻한다. 반면에 부가적인 관점은 핵심적인 관점이 행해지기 위한 것으로 볼 수 있다.

위 그림을 보면 Class A , Class B , Class C에 중복 사용되는 코드를 색깔을 다르게 하여 구분해놓은 것을 볼 수 있다.
이렇게 반복 사용되는 코드를 흩어진 관심사(CrossCutting Concerns)라고 한다.
이처럼 흩어진 관심사를 Aspect로 모듈화하고 핵심적인 비즈니스 로직에서 분리하여 재사용하겠다는 것이 AOP의 목적이다.
AOP의 주요 개념
○ Aspect
→ 흩어진 관심사를 묶어서 모듈화 한 것이다. Advice와 Point Cut이 들어간다.
○ Target
→ Aspect가 가지고 있는 Advice의 적용 대상(Class , Method etc....)을 가리킨다.
○ Advice
→ 어떤 일을 해야 할지에 대한 정보를 가지고 있다.
○ Join Point
→ Advice가 적용될 위치를 말한다.
○ Point Cut
→ Join Point의 상세한 스펙을 정의한 것이다. 구체적으로 Advice가 실행될 지점을 정할 수 있다.
AOP 적용 시점
1. 컴파일 타임 적용
→ 컴파일 시점에 AOP가 적용된 바이트 코드를 생성하는 방법.
2. 로드 타임 적용
→ 컴파일 후 클래스를 로딩하는 과정에서 클래스 정보를 변경하는 방법.
3. 런타임 적용
→ 스프링 AOP가 자주 사용하는 방법. Bean을 만들 때 해당 타입의 Proxy Bean을 만들어 Proxy Bean이 Aspect 코드를 추가하여 동작하는 방법.
스프링 AOP의 특징
- 프록시 기반의 AOP 구현체 → 접근 제어와 부가 기능을 추가하기 위함.
- 스프링 AOP는 Bean에만 적용.
- 모든 AOP기능을 제공하기보단 중복 코드, 프록시 클래스 작성의 번거로움 등 흔한 문제를 해결하기 위함.
프록시 패턴

프록시 패턴에는 interface가 존재하고 Cilent는 이 interface 타입으로 프록시 객체를 사용한다.
프록시 객체와 기존 타겟 객체(Real Subject)는 같은 타입이고 Proxy 객체는 원래 해야 할 일을 가지고 있는 타겟 객체를 감싸서 Cilent의 요청을 처리한다.
스프링 AOP 구현
Spring AOP를 구현하기 위하여 아래 의존성을 추가해주었다.
implementation 'org.springframework.boot:spring-boot-starter-aop'
AspectEx.java
@Component // 스프링 AOP를 위해 Bean 등록
@Aspect // 해당 클래스가 Aspect라는 것을 명시
public class AspectEx {
@Around("execution(* com.example..*.EventService.*(..))")
// com.example 아래 모든 클래스와 EventService 밑의 모든 메서드에 적용.
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable{
long begin = System.currentTimeMillis();
Object proceed = pjp.proceed();
//ProceedingJoinPoint는 Advice가 적용되는 대상. 즉, EventService의 메서드들
System.out.println(System.currentTimeMillis() - begin);
return proceed;
}
}
EventService.java
public interface EventService {
void createEvent();
void publishEvent();
void deleteEvent();
}
EventServiceImpl.java
@Service
public class EventServiceImpl implements EventService {
@Override
public void createEvent() {
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("Created Event!");
}
@Override
public void publishEvent() {
try{
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("Published Event!");
}
@Override
public void deleteEvent() {
System.out.println("Deleted Event!");
}
}
AppRunner.java
@Component
public class AppRunner implements ApplicationRunner {
private EventService eventService;
public AppRunner(EventService eventService){
this.eventService = eventService;
}
@Override
public void run(ApplicationArguments args) throws Exception {
eventService.createEvent();
eventService.publishEvent();
eventService.deleteEvent();
}
}
실행 결과

이번에는 위와 같이 경로를 지정하는 방식이 아닌 어노테이션이 붙은 메서드에만 적용하는 방법을 보자.
AspectEx.java
@Component
@Aspect
public class AspectEx {
@Around("@annotation(PerfLogging)") // @PerfLogging 어노테이션이 붙은 메서드만 적용
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable{
long begin = System.currentTimeMillis();
Object proceed = pjp.proceed();
System.out.println(System.currentTimeMillis() - begin);
return proceed;
}
}
PerfLogging.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface PerfLogging {
}
EventServiceImpl.java
@Service
public class EventServiceImpl implements EventService {
@PerfLogging
@Override
public void createEvent() {
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("Created Event!");
}
@Override
public void publishEvent() {
try{
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("Published Event!");
}
@Override
public void deleteEvent() {
System.out.println("Deleted Event!");
}
}
실행 결과

createEvent()에만 @PerfLogging을 적용해주었더니 이전 결과와 달리 PubilshEvent()에는 Aspect가 적용되지 않은 결과를 보여준다.
또한 특정 Bean 전체에 Aspect를 적용시킬 수 있다.
@Component
@Aspect
public class AspectEx {
@Around("bean(EventServiceImpl)")
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable{
long begin = System.currentTimeMillis();
Object proceed = pjp.proceed();
System.out.println(System.currentTimeMillis() - begin);
return proceed;
}
}
위처럼 @Around에 적용할 Bean을 명시해준다. 그럼 해당 Bean이 가지고 있는 모든 Public 메서드에 Aspect가 적용된다.
@Around 어노테이션 외에 Aspect 적용 시점을 지정할 수 있는 어노테이션이 다음과 같이 있다.
- @Before(이전) : 타겟 메서드가 호출되기 전에 Advice 기능 수행
- @After(이후) : 타겟 메서드의 결과와 상관없이 타겟 메서드 완료 후 Advice 기능 수행
- @AfterRunning(정상적 반환 이후) : 타겟 메서드가 성공적인 결과를 반환한 후 Advice 기능 수행
- @AfterThrowing(예외 발생 이후) : 타겟 메서드가 수행 중 예외 처리를 던지면 Advice 기능 수행
- @Around(메서드 실행 전후) : Advice가 타겟 메서드를 감싸서 타겟 메서드 호출 전/후에 Advice 기능 수행
▽ 도움을 주신 분들
[Spring] AOP(Aspect Oriented Programming)란? 스프링 AOP란?
AOP (Aspect Oriented Programming)란? AOP는 Aspect Oriented Programming의 약자로 관점 지향 프로그래밍이라고 불린다. 관점 지향은 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그..
code-lab1.tistory.com
스프링 AOP 총정리 : 개념, 프록시 기반 AOP, @AOP :: 개발자 한선우
스프링 프레임워크 핵심기술을 공부하고 정리하는 포스트입니다.
yadon079.github.io
[Spring] 스프링 AOP (Spring AOP) 총정리 : 개념, 프록시 기반 AOP, @AOP
| 스프링 AOP ( Aspect Oriented Programming ) AOP는 Aspect Oriented Programming의 약자로 관점 지향 프로그래밍이라고 불린다. 관점 지향은 쉽게 말해 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으..
engkimbs.tistory.com

'Spring > Spring' 카테고리의 다른 글
[Spring] Spring Triangle? 그게 뭔데 - PSA (0) | 2022.07.05 |
---|---|
[Spring] Spring Triangle? 그게 뭔데 - IoC/DI (0) | 2022.06.30 |
[Spring] 인터셉터(Interceptor)에 대해 알아보자! (0) | 2022.06.28 |
[Spring] Filter에 대해 알아보자 ! (0) | 2022.06.24 |
[spring] Spring MVC 구조 (0) | 2022.06.02 |