[Spring] AOP

kdkdhoho·2022년 2월 15일
0

Spring

목록 보기
6/26

이 글은 인프런 - 김영한 님의 스프링 입문 강의를 보고 공부한 것을 정리한 글입니다.

AOP가 필요한 상황

  1. 메소드의 호출 시간을 측정하고 싶을 때
  2. 공통 관심 사항(cross-cutting concern) vs 핵심 관심 사항(core concern)
  3. 회원 가입 시간, 회원 조회 시간을 측정하고 싶을 때

메소드의 호출 시간을 측정하고 싶을 때

메소드 개수가 몇 천 개라면 모든 메소드에 하나씩

long start = System.currentTimeMillis();

~~~

long finish = System.curretTimeMillis();
long timeMs = finish - start;
System.out.println("method = " + timeMs + "ms"):

이런 식으로 모두 넣어줘야 하는데, 이는 매우 비효율적인 일.

또한, 시간 측정하는 기능은 여태 진행한 메소드에서 핵심 관심 사항이 아니다. 단순히 공통 관심 사항이다.
그리고 시간 측정하는 코드와 핵심 비즈니스 코드가 공존해 유지보수도 힘들고,
시간을 측정하는 코드를 별도의 공통 로직으로 만들기도 매우 어렵다.
시간을 측정하는 로직을 변경할 때 모든 로직을 찾아가면서 변경해야 하는 문제점들이 있다.

AOP 적용

AOP(Aspect Oriented Programming): 관점 지향 프로그래밍
공통 관심사항과 핵심 관심사항을 분리

위의 예시에서 AOP를 적용하게 되면, 시간측정로직을 따로 빼내어서 메소드들을 호출할 때 적용시키는 식이다.

실제로 아래 코드를 hello.hellospring/aop/TimeTraceAop.java 로 만들고 서버를 실행시키면
실행되는 메소드 별로 수행시간이 출력된다.

package hello.hellospring.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class TimeTraceAop {

    @Around("execution(* hello.hellospring..*(..))") // 타겟팅
    public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("START: " + joinPoint.toString());

        try {
            // 다음 메소드로 진행
            return joinPoint.proceed();
        } finally {
            long finish = System.currentTimeMillis();
            long timsMs = finish - start;
            System.out.println("END: " + joinPoint.toString() + " " + timsMs + "ms");
        }
    }
}

함수 호출이 될 때마다 joinPoint에 다양한 파라미터를 전달하여, joinPoint의 함수로 여러 값들 이용 가능
호출 될 때 중간에 인터셉트, 인터럽트가 걸려서 joinPoint에 값 전달
@Around("execution(* hello.hellospring..*(..))"): hellospring 패키지 안에 모든 패키지와 함수

이로써, 핵심 관심 사항과 공통 관심 사항을 분리할 수 있다. 따라서 이 둘을 깔끔하게 유지보수 할 수 있다.
또 시간을 측정하는 로직을 별도의 공통 로직으로 만들었고, AOP를 통해 원하는 적용대상을 선택할 수도 있다.

@Around("execution(* hello.hellospring.service..*(..))") 로 작성하면 service 패키지 안에서만 적용

AOP 동작 방식

스프링 컨테이너에 올라간 Controller - Service - Repository 가 있다면,

AOP 적용 전엔 직접적인 의존관계가 있어 바로 실행된다.
하지만 AOP를 적용하면 실제 Controller, Service, Repository가 실행되기 전, 프록시 Controller, Service, Repository가 만들어지고.
그 프록시 CSR이 실행되면서 joinPoint.proceed()를 통해 다음으로 진행되면, 그제서야 실제 CSR이 실행된다.

이는 모든 AOP를 모든 클래스에 적용시켰을 경우이고, 만약 Service에만 AOP를 적용했다면, Controller에서 Service를 호출하기 전에
프록시 Service가 생성되고 먼저 실행되고, 프록시 Service가 실제 Service를 호출하는 식으로 동작한다.

여기서 프록시는 가짜? 가상? 복제?의 느낌이다.

profile
newBlog == https://kdkdhoho.github.io

0개의 댓글