스프링과 프록시

Single Ko·2023년 5월 25일
0

Spring 강의 정리

목록 보기
30/31

Proxy

프로젝트는 - proxy

Client ----> Proxy ----> Server

간접적인 호출

Proxy의 주요기능


  • 접근 제어

    • 권한에 따른 접근 차단
    • 캐싱
    • 지연 로딩
  • 부가 기능 추가

    • 원래 서버가 제공하는 기능에 더해서 부가 기능 수행
    • 예) 요청 값이나, 응답 값을 중간에 변경
    • 예) 실행 식나을 측정해서 추가 로그를 남긴다
  • 프록시 체인

    • 프록시가 또 다른 프록시를 부를 수 있는 것.
    • 클라이언트는 프록시를 통해 요청했기 때문에, 그 이후 과정은 모른다

프록시 객체가 중간에 있으면 크게 '접근 제어'와 '부가 기능 추가'를 수행할 수 있다.

대체 가능


아무 객체나 프록시가 될 수 있는거 같다.
하지만 객체에서 프록시가 되려면 클라이언트는 서버에게 요청을 한 것인지,
프록시에게 요청을 한 것인지 조차 몰라야한다.
서버와 프록시는 같은 인터페이스를 사용해야 한다.
클라이언트가 사용하는 서버 객체를 프록시 객체로 변경해도
클라이언트 코드를 변경하지 않고 동작이 가능해야 한다.

'GOF 디자인 패턴'


  • 둘 다 프록시를 사용하는 방법 이지만 (접근 제어부가 기능 추가)
    둘의 의도에 따라서 프록시 패턴데코레이터 패턴으로 구분한다
  1. 프록시 패턴 : 접근 제어가 목적
  2. 데코레이터 패턴 : 새로운 기능 추가가 목적

프록시라는 개념은 클라이언트 서버 라는 큰 개념안에서 자연스럽게 발생할 수 있다.
객체안에서의 프록시 개념도 있고, 웹 서버에서의 프록시도 있다.
규모의 차이만 있을뿐 근본적인 역할은 같다.

Proxy 패턴

test.proxy


  • 접근 제어가 목적이다. 그 예에서 캐슁을 해볼 수 있다.
  • 처음 원본 객체에 진입해서 데이터를 가지고있으면, 그 데이터를 프록시가 가지고 있다가
    요청하면 바로 줄 수 있다.

Decorator 패턴

test.decorator


  • 체인을 통해서 데코레이트(꾸미기)를 더 할수 있다. 부가기능 더 넣을 수 있다.
  • 꾸며주는 역할을 하는 Decorator 들은 스스로 존재할 수 없다.
    항상 꾸며줄 대상이 있어야 한다.
  • 내부에 호출 대상인 component를 속성으로 가지고 있어야 한다.

디자인 패턴은 의도가 중요하다

  • 프록시 패턴과 데코레이터 패턴은 사실상 모양이 거의 비슷하다. 어쩔때는 그냥 똑같을 때도 있다.
    그렇다면 어떻게 디자인 패턴을 구분하는가?
  • 바로 의도로 그 차이를 구분한다. 프록시 패턴은 접근의 제어가 목적이다.
    데코레이터 패턴은 객체의 새로운 기능을 붙이기 위한 목적이다.
    이 목적으로 패턴을 차이가지고 있다.

인터페이스 기반 프록시 적용

interface_proxy


  • 실제 구현체를 빈으로 등록하는것이 아니다. 가짜 프록시 객체를 빈으로 등록하고,
    그 프록시를 통해 구현 객체를 가지고 온다.
  • 실제 객체는 프록시 객체를 통해 참조될 뿐이고, 스프링 컨테이너와는 상관이 없다.
  • 프록시 객체는 스프링 컨테이너가 관리하고 자바 힙 메모리에도 올라간다.
    반면 실제 객체는 자바 힙 메모리에는 올라가지만 스프링 컨테이너가 관리하지 않는다.

클래스 기반 프록시 적용

concreteProxy package


  • 인터페이스가 없어도 상속으로 클래스 기반 프록시를 생성할 수 있다.
  • 클래스 기반 프록시는 부모 클래스에만 적용가능. 인터페이스는 그 하위 구현체들 모두에 적용 가능
  • 클래스 기반 프록시는 상속을 하기 때문에 몇가지 제약이 있다.
    1. 부모 클래스의 생서자를 호출해야 된다(super())
    2. 클래스에 final 키워드가 붙으면 상속이 불가능하다.
    3. method에 final 키워드가 붙으면 해당 메서드를 override 할 수 없다.

결론 : 인터페이스 vs 클래스 기반 프록시

  • 인터페이스 기반 프록시가 더 좋음.
    프로그래밍 관점에서도 인터페이스를 사용하는 것이 역할과 구현을 더 명확하게 나눈다
    단점은 인터페이스가 필요하다는것. 인터페이스가 없으면 인터페이스 기반 프록시를 만들 수 없다.

  • 하지만 실제로는 구현을 변경할 일이 거의 없는 클래스도 많다.
    이런곳에는 실용적인 관점에서 인터페이스를 사용하지 않고 구체 클래스를 바로 사용하는 것이 좋음

  • 실무에서는 프록시를 적용할때는 인터페이스와 구체 클래스가 섞여 있는 경우가 있다.
    따라서 2가지 상황을 모두 다 대응할 수 있어야 한다.

::문제점::

  • 프록시를 사용해서 기존 코드를 변경하지 않고, 로그 추적기라는 부가기능을 적용 함.
  • 하지만 프록시 클래스가 너무 많이 만들어야 한다는 점이 문제다.
  • 적용해야 하는 대상 클래스가 100개라면 프록시 클래스도 100개 ...
    이런 문제를 해결 할 수 없을까?
  • 동적 프록시 기술 사용으로 해결 가능!

리플렉션

test.jdkdynamic.RelfectionTest


  1. 자바가 기본으로 제공하는 JDK 동적 프록시 기술이나 CGLIB 같은 프록시 생성 오픈기술을 활용
    하면 프록시 객체를 동적으로 만들어 낼 수 있다.
    프록시 클래스를 지금처럼 계속 만들어내지 않아도 된다는 것이다.
  1. JDK 동적 프록시를 이해하기 위해서는 먼저 자바의 리플렉션에 대한 이해가 필요하다
    리플렉션 기술을 사용하면 클래스나 메서드의 메타정보를 동적으로 획득하고, 코드도
    동적으로 호출할 수 있다.

주의점 : 리플렉션을 사용하면 애플리케이션을 동적으로 유연하게 만들 수 있다. 하지만
리플렉션 기술은 런타임에 동작하기 때문에, 컴파일 시점에 오류를 잡을 수 없다.
리플렉션은 일반적으로 사용하면 안된다.

지금까지 프로그래밍 언어가 발달하면서 타입 정보를 기반으로 컴파일 시점에 오류를 잡아준
덕에 개발자가 오류를 바로 잡았는데, 리플렉션은 그것에 역행하는 방식이다

리플렉션은 프레임워크 개발이나 또는 매우 일반적인 공통 처리가 필요한 때 부분적으로
주의해서 사용해야한다.

JDK 동적 프록시

test.jdkdynamic.code


동적 프록시 기술을 활용하면 개발자가 직접 프록시 클래스를 만들지 않아도 된다. 런타임에
개발자 대신 만들어 준다. 그리고 동적 프록시에 원하는 실행 로직을 지정할 수 있다.

"주의"
JDK 동적 프록시는 인터페이스를 기반으로 프록시를 동적으로 만들어준다. 인터페이스가 필수!!

JDK 동적 프록시에 적용할 로직은 InvocationHandler 인터페이스를 구현해서 작성하면 된다.

구현해야할 메서드 : public Object invoke(Object proxy, Method method, Object[] args)
1. object proxy - 프록시 자신
2. Method method - 호출한 메서드
3. Object[] args - 메서드를 호출할 때 전달한 인수

실행 순서
1. 클라이언트는 JDK 동적 프록시의 call() 실행
2. JDK 동적 프록시는 InvocationHandler.invoce() 호출.
-> TimeInvocationHandler 구현체가 있으므로 TimeInvocationHandler.invoke() 호출
3. TimeInvocationHandler가 내부로직을 수행하고, method.invoke(target, args)를 호출해서
target인 실제 객체를 호출한다
4. 실제 객체의 call()이 실행
5. 실제 객체의 call()의 실행이 끝나면 TimeInvocationHandler로 응답이 돌아온다.
시간 로그를 출력하고 결과를 반환함.

결론
적용 대상만큼 프록시 객체를 만들지 않아도 된다. 부가 기능 로직을 한번만 개발해서
공통으로 적용 가능. 적용 클래스가 100개여도 동적 프록시를 통해 생성하고,
필요한 InvocationHandler만 만들어서 넣어주면 된다.

결과적으로 프록시 클래스를 수없이 만들어야 하는 문제도 해결하고, 부가 기능 로직도 하나의
클래스에 모아서 단일 책임 원칙(SRP)도 지킬 수 있게 되었다.

실제 예제에 적용
일차적으로 메서드의 실행시간을 제는 LogTrace기능을 JDK 동적 프록시를 통해 적용했는데,
문제가 하나 생겼다. 바로 no-log 메서드에도 log가 남는것이다. 이 메서드에는 로그가 남으면 안된다

이때 해결할 수 있는 방법 - JDK 동적 프록시에는 Method 이름 필터 기능이 있다.
같이 연계해서 Spring의 PatternMathUtils를 사용하면 패턴의 매칭을 맞추어,
그 문자열이 들어가는 메서드만 로그를 남길 수 있다.

CGLIB

CGLIB : Code Generator Library

test.cglib


  • 바이트 코드를 조작해서 동적으로 클래스를 생성하는 기술을 제공하는 라이브러리

  • 인터페이스가 없어도 구체 클래스만 가지고 동적 프록시를 만들어 낼 수 있다.

  • 원래 CGLIB은 외부 라이브러리인데, 스프링 프레임워크가 스프링 내부 소스코드에 포함했다.
    따라서 스프링을 사용하면 별도의 추가 없이 사용 가능

    참고 : 우리가 CGLIB을 직접 사용할 경우는 거의없다. 이후 배울 스프링의 ProxyFactory라는
    것이 이 기술을 편리하게 사용하도록 도와줌.

    실제 구현해야 될것은 JDK 동적 프록시의 InvocationHandler와 비슷하다

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)

invoke Method를 구현해야되는 jdk와 intercept라는 것을 받는 CGLIB. 실제로 메서드는 거의 비슷함.

CGLIB 제약

  • 클래스 기반 프록시는 상속을 사용하기 때문에 몇가지 제약이 있다.
  • 부모 클래스의 생성자를 체크해야됨 -> 자식 클래스를 동적으로 생성함. 기본 생성자가 필요하다
  • 클래스에 final이 붙어있으면 상속 불가능 -> CGLIB에서는 예외 발생
  • 메서드에 fianl이 붙으면 메서드 오버라이딩 불가능 -> CGLIB에서는 프록시 로직 동작 x

CGLIB과 JDK 동적 프록시 정리


  • 인터페이스가 있는 경우엔 JDK 동적 프록시를 적용하고 그렇지 않으면 CGLIB을 적용해야되나?
  • 두 기술을 함께 사용할때 부가 기능을 제공하기 위해 중복으로 만들어서 관리해야되나?
  • 특정 조건에 맞을 때 프록시 로직을 적용하는 기능도 공통으로 제공되면?

Spring의 프록시 팩토리

test.proxy.proxyfactory


유사한 기술들 (JDK 동적 프록시, CGLIB)를 통합해서 일관성 있게 접근할 수 있고,
편리하게 사용할 수 있는 추상화된 기술을 Spring이 제공한다

  1. Adivce 도입

    스프링은 Advice를 통해서 InvocationHandler나 MethodInterceptor를 신경쓰지 않고
    Advice만 만들면 됨. 이 프록시 팩토리를 사용하면 Advice
    호출하는 전용 InvocationHandler, MethodInterceptor를 호출함.

  1. 특정 조건에 맞을 때 프록시 로직을 적용하는 기능도 공통으로 제공된다

    앞서 특정 메서드 이름의 조건에 맞을때만(Filter) 프록시 부가 기능이 적용되는 코드를
    직접 만들었다. Spring에선 Pointcut이라는 개념을 도입해 이 문제를 해결하였다.

Advice 만들기

Advice는 프록시에 적용하는 부가기능 로직. 기존의 동적프록시와 CGLIB을 개념적으로 추상화
한 것이다. Advice를 만드는 방법은 여러가지가 있지만 기본적으로 인터페이스를 구현한다

MethodInterceptor -> CGLIB과 메서드 이름이 같지만, 패키지가 다르다.(Interceptor를 상속
하고 Interceptor는 Advice 인터페이스를 상속한다.)

@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
    return null;
}

기존에 보던 메서드들과 달리 파라미터가 적다. 그 파라미터들의 기능은 전부 MethodInvocation
이 들고 있다.

// 프록시팩토리를 생성할때 타겟 객체를 넣음. advice는 만들어둔 advice를 넣어주면된다.
ProxyFactory proxyFactory = new ProxyFactory(target);
proxyFactory.addAdvice(new TimeAdivce());
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();

// 프록시펙토리를 사용시 AopUtils를 통해 proxy를 사용하고있나 아닌가 확인가능
AopUtils.isAopProxy(proxy);
AopUtils.isJDKDynamicProxy(proxy);
AopUtils.isCglibProxy(proxy);

어떤 것을 선택하나?

  • 인터페이스 기반 프록시를 만들 시 JDK 동적 프록시로 프록시 팩토리가 만듬
  • 구체클래스 기반 프록시를 만들땐 CGLIB을 사용해 만듬.
  • 상관없이 CGLIB만 사용하고 싶을땐, prxoyTargetClass(boolean) 사용하면 됨

정리

  • 프록시 팩토리의 추상화 덕분에 CGLIB, JDK 동적 프록시 기술에 의존하지 않고,
    매우 편리하게 동적 프록시를 생성할 수있다.
  • 부가기능 로직도 Advice 하나로 편리하게 사용 가능.
  1. 포인트컷(Pointcut): 어디에 부가기능을 적용할지, 어디에 부가기능을 적용하지 않을지
    판단하는 필터링 로직. 클래스와 메서드 이름으로 필터링.
  1. 어드바이스(Advice): 프록시가 호출하는 부가기능. 단순하게 프록시 로직이라 생각하면 됨
  1. 어드바이져(Advisor): 단순하게 하나의 포인트컷과 하나의 어드바이스를 가지고 있는 것.

조언(Advice)를 어디(Pointcut)에 할것인가?
조언자(Advisor)는 어디(Pointcut)에 조언(Advice)을 해야할지 알고 있다.

이는 역할과 책임을 명확하게 분리한 것이다.
포인트컷은 대상 여부를 확인하는 필터 역할만 담당. 어드바이스는 부가기능 로직만 담당
둘을 합치면 어드바이저. 스프링의 어드바이저는 하나의 포인트컷 + 하나의 어드바이스로 구성됨

프록시 펙토리는 Advisor가 필수다.
프록시를 호출할때 필수적으로 어드바이저를 넣어줘야됨. 그 전에 만들었을 때는
어드바이스만 넣었는데 이미 그 어드바이스에 편의메서드로 포인트컷이 함께 제공된
어드바이저가 들어가있던것임.

Pointcut

  • Pointcut 관련 인터페이스를 스프링이 제공한다
public interface Pointcut{
    ClassFilter getClassFilter();
    MethodMathcer getMethodMatcher();
}
  • 이름 그대로 클래스가가 맞는지, 메서드가 맞는지 확인할 때 사용. 둘다 true로 반환해야
    어드바이스를 적용할 수 있다. 일반적으로 스프링이 이미 만들어둔 구현체를 사용함
  • 스프링은 무수히 많은 포인트컷을 제공한다. 대표적인 몇가지만 알아보자

    • NameMatchMethodPointcut: 메서드 이름을 기반으로 매칭.(PatternMatchUtils 사용)
    • JdkRegexMethodPointcut: JDK 정규 표현식을 기반으로 포인트컷을 매칭한다.
    • TruePointcut: 항상 참을 반환한다.
    • AnnotationMatchingPointcut: 애노테이션으로 매칭한다.
    • AspectJExpressionPointcut: aspectJ 표현식으로 매칭한다

    가장 중요한 것은 aspectJ 표현식
    사실 다른것은 중요하지 않다. 실무에서는 사용성과 편리성을 고려해 aspectJ 표현식을
    기반으로 사용하는 AspectJExpressionPoincut을 사용하게됨.

여러 Advisor 사용

  • 프록시를 여러개 생성해서, 프록시 호출 ( 문제점: 프록시 개수를 적용 수만큼 만들어야됨.)

  • 프록시 하나에 여러 Advisor 사용 가능 ( 결과는 같고, 성능은 더 좋아진다)

    정리

  • 스프링 AOP를 처음 공부하거나 사용시, AOP 적용 수 만큼 프록시가 생성된다고 착각함.

  • 스프링은 AOP를 적용할때, 최적화를 진행해서 지금처럼 프록시는 하나만 만들고,
    하나의 프록시에 여러 어드바이저를 적용한다.

  • target에 여러 AOP가 동시에 적용되어도, 스프링의 AOP는 target마다 하나의 프록시만
    생성한다.

정리

프록시 팩토리 덕분에 매우 편리하게 프록시를 생성할 수 있게 되었다.
추가로 어드바이저, 어드바이스, 포인트컷 이라는 개념덕분에 어떤 부가기능을 어디에 적용해야할지
명확하게 이해할 수 있었다.

남은 문제

  1. 너무 많은 설정
    • 설정 파일이 지나치게 많다. 스프링 빈이 100개가 있다면 프록시를 통한 부가기능을 적용하려면
      100개의 동적 프록시 생성 코드를 만들어야 한다.
    • 최근에는 빈 등록도 귀찮아서 @ComponentScan까지 사용하는데, 이렇게 직접 등록하는 것도
      모자라서, 프록시 적용 코드까지 빈 생성 코드에 넣어야한다.
  1. 컴포넌트 스캔
    • 컴포넌트 스캔을 사용하는 경우, 지금까지의 방법으로는 프록시 적용이 불가능하다.
      실제 객체를 컴포넌트 스캔으로 스프링 컨테이너에 스프링 빈으로 등록을 다해버린 상태임..
    • 실제 객체를 스프링 컨테이너에 빈으로 등록하는 것이 아니라, Config에서 부가 기능이
      있는 프록시를 실제 객체 대신 스프링 컨테이너에 빈으로 등록해야 함.

이 두가지 문제를 한번에 해결하는 방법이 바로 빈 후처리기 이다.

빈 후처리기 - BeanPostProcessor


  • 스프링이 빈 저장소에 등록할 목적으로 생성한 객체를 빈 저장소에 등록하기 직전에 조작
  • BeanPostProcessor는 빈 후처리기인데, 이름 그대로 빈을 생성한 후에 무언가를 처리하는 용도
  • 매우 막강한 기능을 가지고 있다. 객체를 조작할 수도 있고, 완전히 다른 객체로 바꿔치기 하는 것도 가능.

빈 등록 과정
1. 생성 - 스프링 빈이 대상 객체를 생성(@Bean, @Component)
2. 전달 : 생성된 객체를 빈 저장소에 등록(이때 빈 후처리기를 사용하면 빈 후처리기에 먼저 전달됨)
3. 후 처리 작업: 전달된 스프링 빈 객체를 조작하거나 다른 객체로 바꿔치기 가능.

public interface BeanPostProcessor {

	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
	
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
}
  • 빈 후처리기 인터페이스를 제공한다. 여기서 default 메서드를 사용했다.
  • 이는 오버라이드를 꼭 하지 않아도됨. 필요한 기능만 가져가서 하면 된다.
  • 이름에서 알수있듯이 Before와 After가 존제함. 상황에 따라 선택해서 내가 원하는
    기능을 구현하면 됨

빈을 조작한다는 것은 해당 객체의 특정 메서드를 호출하는 것을 뜻한다.
일반적으로 스프링컨테이너가 등록하는, 특히 컴포넌트 스캔의 대상이 되는 빈들은
중간에 조작할 방법이 없는데, 빈 후처리기를 사용하면 개발자가 등록하는 모든 빈을
중간에 조작할 수 있다. 이 말은 '빈 객체를 프록시로 교체'하는 것도 가능하다는 뜻.

해결

  • 빈 후처리기를 사용한 덕분에 프록시를 생성하는 부분을 하나로 집중 할 수 있었다.
  • @Bean 뿐만 아니라 컴포넌트 스캔으로 등록하는 빈 등록 과정을 가로채서 원본 대신에 프록시를
    스프링 빈으로 등록할 수 있었다.
  • 여기서 한가지 더 좋은점은 방금은 빈 후처리기를 직접 만들어 사용했다. 하지만.. 스프링은
    프록시 생성을 위한 빈 후처리기를 이미 만들어서 제공한다.
  • 이번 예제에서는 basePackage를 설정해서 프록시 적용대상 여부를 설정했다. 하지만 포인터컷을 사용하면 더 깔끔
    하게 가능하다고 한다. 클래스, 메서드 단위의 필터를 이미 가지고 있기 때문에 더 정밀하게 설정가능.
    결과적으로 포인트컷은 다음 두곳에서 사용됨
    1. 프록시 적용 대상 여부를 체크해서 꼭 필요한 곳에서만 프록시 적용(빈 후처리기)
    2. 프록시의 어떤 메서드가 호출 되었을 때 어드바이스를 적용할 지 판단한다.(프록시 내부)

Spring의 빈 후처리기


library 등록 해야됨 - 'org.springframework.boot:spring-boot-starter-aop'

자동 프록시 생성기 - AutoProxyCreator

  • 스프링 부트 자동 설정으로 빈 후처리기가 스프링 빈에 등록된다.

  • 스프링 빈으로 등록된 Advisor들을 찾아서 프록시가 필요한 곳에 자동으로 프록시를 적용해준다.

    생성 과정

  1. 생성 - 스프링 빈 대상이 되는 객체 생성
  2. 전달 - 생성된 객체를 빈 저장소에 등록하기 진적엔 빈 후처리기에 전달
  3. 모든 Advisor 빈 조회 - 자동 프록시 생성기 - 빈 후처리기는 스프링 컨터이너에서 모든 Advisor를 조회
  4. 프록시 적용 대상 체크 - 앞서 조회한 Advisor에 포함되어 있는 포인트컷을 사용해서 해당 객체가 프록시 적용 대상인지
    아닌지 판단한다. 객체의 클래스 정보는 물론이고, 모든 메서드까지 포인트컷에 하나하나 매칭해봄. 조건이 만족하면, 프록시
    적용 대상이 된다. (10개중에 하나만 만족해도 프록시 적용 대상이됨)
  5. 프록시 생성 - 프록시 적용 대상이면 프록시를 생성하고 반환해서 프록시를 스프링 빈으로 등록한다. 만약 적용 대상이
    아니라면 원본 객체를 반환해서 스프링 빈으로 등록한다
  6. 빈 등록 - 반환된 객체는 스프링 빈으로 등록됨.

중요 : 포인트컷은 2가지에 사용된다.

  1. 프록시 적용 여부 판단 - "생성 단계"

    • 포인트컷을 이용해 자동 프록시 생성기가 프록시 필요 여부 판단
    • 만약 조건이 맞는것이 하나라도 있으면 프록시 생성, 없으면 프록시 생성을 하지 않는다.
  2. 어드바이스 적용 여부 판단 - "사용 단계"

    • 프록시가 호출되었을 때 부가 기능인 어디바이스 적용 판단 여부하는 포인트컷

    프록시를 모든 곳에 생성하는 것은 비용 낭비이다. 꼭 필요한 곳에 최소한의 프록시를 적용해야 한다.
    자동 프록시 생성기는 모든 스프링 빈에 프록시를 적용하는 것이 아니라 포인트컷으로 한번 필터링,
    어드바이스가 사용될 가능성이 있는 곳에만 프록시를 생성.

문제점 : 서버를 실행만 해보면, 스프링이 초기화 되면서 기대하지 않은 로그가 올라온다.
그 이유는 단순하게 지금은 어떤 단어만 포함되어있으면 매칭 된다고 판단하기 때문.
결국 스프링 내부에서 사용하는 빈, 메서드 이름에 그 단어만 있으면 프록시가 만들어지고
어드바이스도 적용됨.

결론적으로 매우 정밀한 포인트컷이 필요하다. -> AspectJ를 이용한다. 또한 실무에서는 이 AspectJ
를 대부분 사용한다.

@Aspect 프록시


스프링 애플리케이션에 프록시를 적용하려면 포인트컷과 어드바이스로 구성되어 있는 어드바이저를
만들어서 스프링 빈으로 등록하면 된다. 그럼 나머지는 자동 프록시 생성기가 자동으로 처리해줌.

스프링은 @Aspect 애노테이션으로 매우 편리하게 포인트컷과 어드바이스로 구성되어있는
어드바이저 생성 기능을 지원한다. 직접 만들었던 어드바이저에 @Aspect 애노테이션을 사용해
만들면쉬워 진다.

@Aspect는 관점 지향 프로그래밍(AOP)을 가능하게 하는 AspectJ 프로젝트에서 제공하는
애노테이션이다. 스프링은 이것을 차용해서 프록시를 통한 AOP를 가능하게 한다.
AOP와 AspectJ 관련된 내용은 다음에 설명.. 지금은 이 애노테이션을 사용해서
스프링이 편리하게 프록시를 만들어주는것을 생각하면 됨.

사실 자동 프록시 생성기는 2가지 일을 한다
1. @Aspect를 보고 어드바이저로 변환해서 저장한다.
2. 어드바이저를 기반으로 프록시를 생성한다.

"@Asepect를 어드바이저로 변환해서 저장하는 과정"

  1. 실행 : 스프링 애플리케이션 로딩 시점에 자동 프록시 생성기를 호출
  2. 모든 @Aspect빈 조회: 자동 프록시 생성기는 스프링 컨테이너에서 @Aspect애노테이션이 붙은
    빈을 모두 조회한다.
  3. 어드바이저 생성: @Aspect 어드바이저 빌더를 통해 @Aspect의 정보를 기반으로 어드바이저 생성.
  4. @Aspect기반 어드바이저 저장: 생성한 어드바이저를 @Aspect 어드바이저 빌더 내부에 저장한다.

정리

  • 지금까지 우리가 진행한 애플리케이션 전반에 로그를 남기는 기능은 특정 기능 하나에 관심이
    있는 기능이 아니다. 애플리케이션의 여러 기능들 사이에 걸쳐서 들어가는 관심사이다.
  • 이것을 바로 '횡단 관심사(cross-cutting concerns)`라고 한다. 우리가 지금까지 진행한
    방법이 이렇게 여러곳에 걸쳐 있는 횡단 관심사의 문제를 해결하는 방법이었다.

참고 : 본 글은 김영한님의 스프링 강의를 정리한 것이다.

profile
공부 정리 블로그

0개의 댓글