객체 지향 프로그래밍과 완전하게 다른 프로그래밍이 아니라 객체 지향 프로그래밍을 돕는 보조 기법이다. 로직을 핵심적인 관점, 부가적인 관점으로 나누어 보고 그 관점을 기준으로 모듈화하는 것이다. (흩어진 관심사(Crosscutting Concerns)를 모듈화)
*모듈화: 공통된 기능이나 로직을 하나의 단위로 묶는 것
우리가 프로그래밍을 할 때 불가피하게 중복 코드가 자주 발생하는 경우가 있다. (로깅, 트랜잭션 등등) 예를 들어 모든 비즈니스 로직이 실행될 때마다 로그를 남긴다고 해보자. 일일이 로그를 남기는 코드를 작성한다면 가독성도 떨어지고 추후 유지보수 시 다시 일일이 수정해주어야 하는 단점이 있다. 이러한 문제를 해결하기 위해 등장한 것이 관점 지향 프로그래밍이다.
Aspect: 흩어진 관심사를 모듈화 한 것
Target: Aspect(모듈)를 적용하는 곳. 클래스, 메서드 등
Advice: 실질적으로 어떤 일을 해야 할 지에 대한 것, 실질적인 부가기능을 담은 구현체
Join Point: Advide가 적용될 위치 혹은 끼어들 시점. 스프링에서 Join Point는 언제나 메서드 실행 시점을 의미
Point Cut: Join Point를 구체화. “A라는 메서드의 진입 시점에 호출할 것”처럼 구체적으로 Advice가 실행될 시점을 의미
지정된 패턴에 해당하는 메서드 실행 전, 후 모두에서 동작한다. Around 어노테이션이 붙은 메서드의 반환 값은 Object여야 한다.
지정된 패턴에 해당하는 메서드 실행 전 동작한다. Before 어노테이션이 붙은 메서드의 반환 값은 void여야 한다. (지정 메서드 실행 전에 동작해 반환값이 없기 때문)
지정된 패턴에 해당하는 메서드가 실행된 후 동작한다. After 어노테이션이 붙은 메서드의 반환 값은 void여야 한다. (지정 메서드 실행 후에 동작해 반환값이 없기 때문)
지정된 패턴에 해당하는 메서드가 성공적으로 결과값을 반환한 후 동작한다.
지정된 패턴에 해당하는 메서드가 수행 중 예외를 던지면 동작한다.
AspectJ는 CGlib이라는 바이트 조작 라이브러리를 사용해 타깃 오브젝트의 바이트를 고쳐 부가기능을 직접 넣어주는 바이트 조작을 사용한다. 그래서 우리가 만든 코드에서는 부가기능이 분리되어 있지만 바이트 코드에서는 핵심 기능과 부가 기능이 섞여있는 구조이다. 장점으로는
build.gradle
// AOP 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-aop'
TimeLogAspect.java
@Component
@Slf4j
@Aspect
public class TimeLogAspect {
@Around("execution(* com.ssafy.greenEarth.controller.*.*(..))")
public Object logAsp(ProceedingJoinPoint pjp) throws Throwable {
log.info("Start Aspect");
Object reVal = pjp.proceed();
log.info("End Aspect");
return reVal;
}
}
관점지향 프로그래밍을 공부하며 볼륨이 큰 프로젝트 및 다수의 중복이 발생하는 클래스 구현 시 유용할 것이라는 생각이 들긴 했지만 볼륨이 작은 프로젝트나 중복이 발생하되 내부 로직이 조금씩 다른 경우 관점지향 프로그래밍을 사용하면 배보다 배꼽이 더 큰 상황이 발생할 수 있을 것 같다는 생각이 든다. 역시 무작정 사용하기보다 상황에 따른 유연한 사용이 필요해보인다.