프록시 패턴에 대해 공부하면서, AOP에 대해 자세히 정리해봐야겠다고 느꼈다! 오늘은 AOP에 대해서 알아보도록 하자.
서비스에서 필요한 내용은 비지니스 로직이라고 불리는 핵심 기능만 수행할 수 있으면 된다.
그 외 권한, 사용 시간 체크, 트랜잭션을 거는 것 등은 인프라 로직이라고 한다.
인프라 로직
- 애플리케이션의 전 영역에서 나타날 수 있음
- 중복코드를 만들어낼 가능성 때문에 유지보수가 힘들어짐
- 비지니스 로직과 함께 있으면 비지니스 로직을 이해하기 어려워짐
예를들어 위 기능 들에서 공통적으로 나타나는 색깔 블럭들은 중복되는 메소드, 필드, 또는 코드 등이다.
만약 로그인 기능에서 성능 검사에 대한 코드를 수정해야한다면, 글 작성 기능과 삭제 기능의 성능 검사도 전부 찾아 수정해야한다. 이는 SOLID원칙을 위배하며, 유지보수를 어렵게 만든다.
SOLID 원칙
소프트웨어 작업에서 프로그래머가 소스 코드가 읽기 쉽고 확장하기 쉽게 될 때까지 소프트웨어 소스 코드를 리팩터링하여 코드 냄새를 제거하기 위해 적용할 수 있는 지침 이다.
AOP
AOP는 Aspect Oriented Programming의 약자로 관점 지향 프로그래밍이다. 즉, 어떤 로직을 기준으로 핵심적인 관점(비지니스 로직), 부가적인 관점(데이터베이스 연결, 로깅 등)으로 나누어 보고 그 관점을 기준으로 모듈(Aspect)화 하겠다는 것이다. 위에 언급한 횡단 관심에 따라 프로그래밍을 한다고 생각하면 이해하기가 더 쉽다.
즉, 각 관점을 기준으로 로직을 모듈화 한다는 것은, 흩어져있는 관심사를 모듈화 하겠다는 의미이다.
위의 예시를 생각해보자. 성능 검사, 로깅, 권한 체크 등을 모듈화를 시켜놓고, 어디에만 적용시킬 지 정의해주면 된다.
AOP(관점지향)는 OOP(객체지향)를 더욱 더 OOP 답게 사용할 수 있도록 보완해준다.
AOP 용어
- Target : 어떤 대상에 부가 기능을 부여할 것인가
- Advice : 어떤 부가 기능을 추가할 것인가 (Before, AfterReturning, AfterThrowing, After, Around)
- Join point : 어디에 적용할 것인가 (메소드, 필드, 객체, 생성자 등)
- Point cut : 실제 Advice 가 적용될 지점 (Spring AOP에서는 Advice가 적용될 메소드를 선정한다)
AOP 구현 방법
- 컴파일 : 컴파일하는 시점에 해당하는 aspect들을 끼워넣어 AOP를 적용
- 클래스 로드 : 순수하게 컴파일한 뒤, 클래스 로더가 메모리 상에 올릴 때(클래스를 로딩하는 시점) AOP를 적용
- 프록시 패턴 : Spring AOP에서 쓰이는 방식으로, A 클래스(타겟 클래스)의 Bean 을 만들 때, A 타입의 Proxy Bean(부가 기능을 제공하는 프록시)을 만들어 Proxy Bean이 Aspect 코드를 추가하여 동작하는 방법이다. 이는 Spring 이 IoC(Inversion of Control)와 DI(Dependency Injection)를 기반으로 하기 때문에 가능한 방법이다.
Spring AOP
- Spring에서 제공하는 Sping AOP는 프록시 기반의 AOP 구현체이다.
- 프록시 객체를 사용하는 것은 접근 제어 및 부가 기능을 추가하기 위해서이다.
- Spring AOP는 Spring Bean에만 적용할 수 있다.
- 모든 AOP 기능을 제공하는 것이 목적이 아닌, 중복 코드, 프록시 클래스 작성의 번거로움 등 흔한 문제를 해결하기 위한 솔루션을 제공하는 것이 목적이다.
- Spring AOP는 순수 자바로 구현되었기 때문에 특별한 컴파일 과정이 필요하지 않다.
Spring AOP 구현
타겟 메소드의 실행 시간을 측정하는 로직을 작성해 Sping AOP 를 같이 구현해보자.
1) 의존성 주입
// Maven
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.6.6</version>
</dependency>
// Gradle
implementation 'org.springframework.boot:spring-boot-starter-aop'
2) Spring Bean 으로 등록 = @Component (@Aspect 은 해당 클래스가 Aspect 이라는 것을 명시한다.)
@Component
@Aspect
public class PerfAspect {
@Around("execution(* com.example..*.EventService.*(..))")
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object reVal = pjp.proceed();
System.out.println(System.currentTimeMillis() - begin);
return reVal;
}
}
- Execution : logPerf() 메소드 @Around 어노테이션의 execution을 통해 Advice를 적용할 범위를 지정 가능.
- 위의 코드를 예시로 들자면 com.example 밑의 모든 클래스에 적용하고, EventService 밑의 모든 메소드에 적용하라는 것이다.
@Component
@Aspect
public class PerfAspect {
@Around("@annotation(PerfLogging)")
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object retVal = pjp.proceed();
System.out.println(System.currentTimeMillis() - begin);
return retVal;
}
}
- Annotation : 아래와 같이 @Around 어노테이션에 @annotation(PerfLogging)처럼 적용될 어노테이션을 명시할 수 있다.
- 이 경우 해당 메서드를 적용시킬 특정 메서드에 @PerfLogging 어노테이션을 붙이면 logPerf() 기능이 동작한다.
@Component
@Aspect
public class PerfAspect {
@Around("bean(simpleServiceEvent)")
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object retVal = pjp.proceed();
System.out.println(System.currentTimeMillis() - begin);
return retVal;
}
}
- Bean 명시 : 아래와 같이 @Around 어노테이션에 bean(simpleServcieEvent)를 붙인 것 처럼, 적용될 Bean을 명시할 수 있다.
- 그럼 해당 빈이 가지고 있는 모든 public 메소드에 해당 기능이 적용된다.
@Around (Advice가 타겟 메소드를 감싸 타겟 메서드 호출 전, 후에 Advice 기능 수행) 외에 타겟 메소드의 Aspect 실행 시점을 지정할 수 있는 어노테이션은 다음과 같은 것들이 있다.
- @Before : Advice 타겟 메소드 호출되기 전에 Advice 기능 수행
- @After : 타겟 메서드의 결과에 관계없이 타겟 메소드 완료되면 Advice 기능 수행
- @AfterRunning : 타겟 메소드 성공적으로 결과값을 반환 한 후에 Advice 기능 수행
- @AfterThrowing : 타겟 메소드 수행 중 예외를 던지면 Advice 기능 수행
- @Around : Advice가 타겟 메소드를 감싸 타겟 메서드 호출 전, 후에 Advice 기능 수행
[참고]
https://www.youtube.com/watch?v=Hm0w_9ngDpM
https://www.inflearn.com/course/spring-framework_core
https://code-lab1.tistory.com/193
'𝑾𝑬𝑩 > 𝑆𝑃𝑅𝐼𝑁𝐺' 카테고리의 다른 글
[프로젝트] 빌드 관리 도구 선택 - Maven VS Gradle (0) | 2022.12.28 |
---|---|
JPA 와 MyBatis 의 차이 (ORM, SQL Mapper) (0) | 2022.11.17 |
[Spring] REST API, REST, RESTful 이란? (0) | 2022.11.05 |
[Spring] JAR or WAR 차이 (0) | 2022.11.03 |
[Spring] DI, 의존성을 주입하는 여러가지 방법 (0) | 2022.04.05 |