AOP
목차
1. AOP란?
2. OOP와 차이
3. 어떻게 구현할까?
1. AOP란?
- Aspect Oriented Programming
- 관점 지향 프로그램
- 공통 사항과 핵심 비즈니스 로직을 분리하여 공통 사항을 모듈화하는 프로그래밍 방식
코드를 작성하다 보면 로깅이나 권한 인증같이 중복되는 코드가 발생할 수 있다. 이런 중복되는 코드들을 따로 분리하여 관리하면 전체적인 코드의 양이 줄어드니 가독성이 높아지고 유지보수가 좀 더 편해질 것이다.
1) 개념
▶ 관점(Aspect): 공통의 관심사를 캡슐화하는 모듈입니다.
ex) 로깅, 보안, 트랜잭션 관리 등
▶ 조인 포인트(Join Point): 코드에서 관점이 적용될 수 있는 지점
▶ 포인트컷(Pointcut): 어드바이스가 적용될 조인 포인트를 정의하는 표현식
▶ 어드바이스(Advice): 조인 포인트에서 실행되는 코드로, 특정 조인 포인트에서 관점의 동작
조인 포인터와 포인트 컷이 비슷해보이지만 어드바이스가 실행되는 시점을 포인트 컷이라고 하며 포인트 컷이 될 수 있는 모든 지점을 조인 포인트라고 한다.
2) 장점
▶ 모듈화: 공통 관심사를 별도의 모듈로 분리하여 코드의 재사용성과 유지보수성을 높인다.
▶ 코드 중복 제거: 여러 모듈에서 반복되는 코드를 제거하고, 공통 로직을 한 곳에서 관리할 수 있다.
▶ 코드 가독성 향상: 비즈니스 로직과 공통 관심사를 분리하여 코드의 가독성을 높일 수 있다.
2. OOP와 차이
개발자라면 객체지향적으로 프로그래밍하라는 이야기를 들어봤을 것이다. 그런데 갑자기 관점 지향 프로그래밍이라니...프로그래밍 방식을 완전히 바꿔야할까?
그 두 프로그래밍 방식의 차이를 확인해봐야할 것 같다.
OOP | AOP |
객체와 클래스 중심으로 설계하면 객체간의 상호작용 중심 프로그래밍 | 공통의 관심사와 처리를 중심으로 관심사항을 분리하여 모듈화 |
객체의 상태와 행동으로 모듈화하여 유지보수개선 | 모듈화된 관심사항으로 코드의 중복을 줄여 유지보수 개선 |
두 방식은 조금의 차이가 있을 뿐 베타적인 프로그래밍 방식이 아니며 오히려 함께 사용했을 때 단점을 개선할 수 있다.
3. 어떻게 구현할까?
1) 스프링에서 구현
@Aspect
@Component
public class TestClass{
// 언제 호출할건지
@Around("execution(반환타입 패키지.클래스.메서드(파라미터)")
public void test(){
// 작업할 코드 내용
}
}
스프링에서는 Aspect라는 어노테이션을 사용하여 관점사항임을 나타내고 호출 시점을 지정해준다.
호출 시점은 다음과 같다.
@Before | 메서드가 호출되기 전 실행 |
@After | 메서드가 호출된 수 실행 |
@Around | 메서드가 호출되기 전과 후 모두 실행 |
반환 타입이나 모든 메서드 호출은 "*"를 사용과 같이 사용하면된다.
* com.example.TestClass.*(..)
2) NestJS에서 구현
nestjs에는 AOP를 위한 별도의 데코레이터가 없다. 그렇다면 어떻게 구현을 해야할까?
nestjs에서는 intercepter를 통해 구현이 가능하다. intercepter에서 반환타입은 Observable로 해당 타입을 가져오기 위해서는 rxjs 라이브러리가 필요하다.
npm i rxjs
그 후 인터셉터를 작성한다.
import { CallHandler, ExecutionContext, NestInterceptor } from "@nestjs/common";
import { Observable, pipe, tap } from 'rxjs';
export class TestIntercepter implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
// 특정 컨트롤러 및 메서드 호출 전 실행할 로직
return next.handle().pipe(tap(() => {
// 호출 후 실행할 로직
}))
}
}
특정 시점에만 실행하려면 해당 시점의 로직만 작성해주면 된다.
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { AppService } from './app.service';
import { TestIntercepter } from './intercepters/Test.intercepter';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) { }
@Get()
@UseInterceptors(TestIntercepter)
getHello(): string {
return this.appService.getHello();
}
}
전체 요청에 실행하려면 컨트롤러 위에, 특정 메서드에만 적용하려면 해당 메서드 위에 UseIntercepter로 지정해주면 된다.