본문 바로가기

Spring&IntelliJ

Spring AOP가 프록시 객체를 이용하여 구현하는 이유

참고: https://velog.io/@max9106/Spring-%ED%94%84%EB%A1%9D%EC%8B%9C-AOP-xwk5zy57ee

(복습. Spring AOP(관점지향 프로그래밍)란? 공통관심사와 핵심 관심사를 분리하여 프로그래밍하도록 공통 관심사만을 편리하게 구현하도록 어노테이션, 라이브러리등을 제공함)

 

프록시 객체란?

프록시 객체는 원본 객체를 대신해서 호출될 객체로, 원본 객체를 감싸서 클라이언트의 요청을 처리하는 중간 단계에 위치합니다. 프록시 객체는 원본 객체와 같은 인터페이스를 구현하고 있어서, 클라이언트는 프록시 객체를 호출하는 것으로 인해 원본 객체의 메소드를 호출하는 것과 같은 효과를 얻을 수 있습니다.

 

프록시 객체를 사용하는 이유는, 프록시 객체를 통해 원본 객체에 대한 접근을 제어하거나, 부가적인 기능을 제공하기 위해서입니다. 예를 들어, 보안을 위해 원본 객체의 메소드에 접근할 때 권한 검사를 수행하거나, 성능을 개선하기 위해 캐싱을 사용하는 등의 기능을 프록시 객체에서 추가할 수 있습니다. 이렇게 함으로써, 원본 객체의 코드를 변경하지 않고도 클라이언트의 요구사항에 맞추어 기능을 추가하거나 변경할 수 있습니다.

 

AOP를 구현하기 위해서 프록시 객체를 이용하는 이유?

원본 객체에 대한 코드 수정 없이, 런타임 시점에서 프록시 객체를 통해 부가적인 처리를 추가하기 위해서입니다.

프록시 객체는 원본 객체를 감싸고, 클라이언트가 프록시 객체를 호출할 때 원본 객체의 메소드를 호출하기 전에 추가적인 로직을 수행할 수 있도록 도와줍니다. 이렇게 하면 클라이언트는 원본 객체를 호출하는 것처럼 보이지만, 실제로는 프록시 객체가 원본 객체를 감싸고 부가적인 로직을 수행하게 됩니다.

Spring에서 프록시 객체를 생성하기 위해서는 프록시 객체를 생성할 인터페이스를 구현하는 클래스를 만들고, 이 클래스에 부가적인 처리를 수행하는 Advice를 정의한 뒤, Spring AOP가 프록시 객체를 생성하여 클라이언트에게 제공하게 됩니다. 이렇게 함으로써 원본 객체에 대한 수정 없이 부가적인 처리를 적용할 수 있으며, 코드의 재사용성과 유지보수성을 높일 수 있습니다.

----------------------------------------------------------------------------------------------------------------------------------------

스프링 AOP는 프록시 기반의 AOP 구현체이며, 스프링 Bean에만 AOP 적용 가능하다.

프록시 패턴이란?

프록시 객체는 원래 객체를 감싸고 있는 객체이다. 원래 객체와 타입은 동일하다. 프록시 객체가 원래 객체를 감싸서 client의 요청을 처리하게 하는 패턴이다.

프록시 패턴을 쓰는 이유는 접근을 제어하고 싶거나, 부가 기능을 추가하고 싶을 때 사용한다.

EventService라는 interface가 있고,

EventService를 implements 한 TestEventService 가 있다고 하면,

이 코드를 실행시키는 main이 client라고 볼 수 있고, TestEventService가 원래 객체로 볼 수 있다.

TestEventService의 createEvent 메서드와 publishEvent 메서드에 동일한 기능을 하는 코드를 넣고 싶을 때, 각각 메서드에 넣어주는 것은 효율성이 떨어진다.

이럴 때 프록시패턴을 사용한다.

프록시 객체는 원래 객체와 같은 interface를 구현해줘야한다. 원래 객체를 주입받아서, interface의 메서드들을 위임받아 사용하고, 원하는 추가 코드를 넣어주면 된다.

스프링 AOP

이렇게 해주면, 원래 코드에 손을 쓰지 않고도 기능을 추가 할 수는 있지만, 프록시 객체에 중복코드가 발생할 수 있고, 다른 클래스에서도 동일한 기능을 사용하고자 할 때, 매번 코딩을 해줘야하는 부분에서 효율적이지 못하다.

이런 문제를 해결해주는 게, 런타임시, 동적으로 프록시객체를 만들어주는 것인데, 그것이 스프링 AOP이다.

위의 예제로 살펴보면,
1) TestEventService가 Bean으로 등록이되면,
2) 스프링이 AbstractAutoProxyCreator라는 BeanPostProcessor(어떤 Bean이 등록되면, 그 Bean을 가공할 수 있는 life-cycle interface)로 TestEventService라는 Bean을 감싸는 프록시 Bean을 만들어,
3) 그 프록시 Bean을 TestEventService 대신에 등록해준다.

스프링 AOP를 사용해주기 위해서는 pom.xml에 의존성을 추가해주어야한다.

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.0.9.RELEASE</version>
        </dependency>
        
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.6</version>
        </dependency>
        
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.6</version>
        </dependency>

Aspect 클래스를 만들어준다.

Aspect에는 2가지 정보가 필요하다. pointcut(어디에 적용할 것인지)과 advice(해야할 일, 기능)이다.

public Object logPerf 자체가 하나의 advice이며, 이 advice가 어디에 적용될 것 인지(pointcut) @Around 어노테이션으로 표시해준다.
@Around("execution(* 패키지.interface.메서드)")

execution말고 어노테이션을 만들어서 사용할 수도 있다.

어노테이션 파일을 만들고, @Retention 어노테이션을 CLASS타입으로 설정해준다.