AOP는 Aspect Oriented Programming의 약자로 관점 지향 프로그래밍을 의미한다.
관점 지향은 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어 보고 관점을 기준으로 모듈화를 한다는 것을 의미한다.
객체지향 프로그래밍은 클래스가 단일 책임을 갖도록 분리하여 모듈들의 결합도는 낮고 응집도는 높아지게 설계를 한다. 객체지향 설계를 하면 하나의 모듈을 변경하였을 때 다른 모듈, 즉 시스템 전체에 주는 영향이 적어 유지보수가 쉽다는 장점이 있습니다. 하지만 이러한 설계를 하더라도 클래스의 로깅, 보안, 트렌잭션 처리 등 공통된 기능들이 흩어져서 존재한다는 아쉬움이 남는다.
이렇게 소스코드상에서 여러 부분에 반복해서 쓰이는 코드를 흩어진 관심사 (Crosscutting Concerns)라고 하며 흩어진 관심사를 관점에 따라 모듈화하고 핵심적인 비즈니스 로직에서 분리하여 재사용하도록 하는 것이 AOP의 취지이다.
즉, OOP로 독립적으로 분리하기 어려운 부가기능들을 관점에 따라 모듈화하는 방식이다.
spring boot에서 AOP를 사용하기 위해서는 dependency를 추가해줘야한다.
우선 build.gradle의 dependencies부분에 아래의 코드를 추가해보자
implementation 'org.springframework.boot:spring-boot-starter-aop'
@RestController
@RequestMapping("/api")
public class RestApiController {
@GetMapping("/get/{id}")
public void get(@PathVariable Long id, @RequestParam String name){
System.out.println("Get method");
System.out.println("Get method : "+id);
System.out.println("Get method : "+name);
}
@PostMapping("/post")
public void post(@RequestBody User user){
System.out.println("Post method : "+user);
}
}
위의 코드를 보면 2개의 method가 존재하지만 실제 서비스를 운영하면 많은 method들이 존재할 것이다. 이러한 method의 출력을 log로 한곳에 모을 수 있다.
ParameterAop.java
package com.example.aop.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class ParameterAop {
@Pointcut("execution(* com.example.aop.controller..*.*(..))")
private void cut() {
}
@Before("cut()")
public void before(JoinPoint joinPoint) {
// Joinpoint는 들어가는 지점에 대한 정보를 갖을 수 있는 객체이다.
// Method 이름 출력
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
System.out.println(method.getName());
Object[] args = joinPoint.getArgs();
for(Object obj : args){
System.out.println("type : "+ obj.getClass().getSimpleName());
System.out.println("type : "+ obj);
}
}
// returning에는 내가 받고싶은 객체의 이름을 넣어준다. (이는 method의 parameter명과 동일해야한다.)
@AfterReturning(value = "cut()", returning = "returnObj")
public void afterReturn(JoinPoint joinPoint, Object returnObj) {
System.out.println("Return obj");
System.out.println(returnObj);
}
}
AOP를 생성하는 방법은 다음과 같다. 먼저 해당 class를 AOP로 동작하게 하기 위해 class위에 @Aspect를 추가해줘야한다. 또한 해당 class를 Spring Bean에 등록하여 Spring이 관리하게 하기 위해 @Component도 붙여줘야한다.
class정의에서 내려가 @Pointcut부분을 보도록 하자, 앞서 말했듯이 PointCut은 JointPoint에 Aspect로 인해 적용될 Advice를 매칭시켜주는 일을 수행한다. 나는 com.example.aop.controller하위에 있는 모든 것에 AOP를 동작하게 하기위해 아래와 같이 작성하였다.
@Pointcut("execution(* com.example.aop.controller..*.*(..))")
다음으로 @Before부분을 보도록 하자.
@Before는 특정 JointPoint에서 aspect로 인해 실행 될 액션을 담고 있는 Advice라는 것을 알리는 annotation이며 @Before이외에도 @Around, @AfterReturning, @AfterThrowing, @After가 존재한다.
@Before("cut()")의 경우는 cut method가 실행되기 전에 해당 method를 실행시키겠다는 의미를 내포한다.
그 외에도 하단에 위치한 @AfterReturning(value = "cut()", returning = "returnObj")은 Cut이후 Return을 할 때 returnObj를 return하겠다는 의미를 가진다.