Spring AOP 프록시 패턴
서버개발

Spring AOP 프록시 패턴

AOP란

Aspect Oriented Programming의 약자로 관점 지향 프로그래밍이다. OOP를 보완하는 것으로 여러 곳에서 자주 사용되는 공통 기능을 모듈화하여 쓰이는 곳에 필요할 때 연결해 유지보수와 재사용성을 용이하도록 프로그래밍 하는 것

 

AOP를 적용하는 방법

  1. 컴파일 타임 적용 : 컴파일 시점 바이트 코드 조작
  2. 로드 타임 적용 : 클래스 로딩 시점
  3. 런타임 적용 : 스프링 AOP 채택, Bean을 만들 때 Proxy Bean을 만들어 Proxy Bean이 Aspect 코드를 추가하여 동작하는 방법

 

 

 

스프링 빈이란?

Spring IoC 컨테이너가 관리하는 자바 객체를 빈(Bean)이라고 부른다. 제어의 역전으로 인해 프레임워크가 객체를 주입을 맡게 되면서, 주입할 객체를 관리해야하는데 이를 빈이라 부르며 이러한 객체들을 등록하는 방법은 여러가지 있지만 이 중 어노테이션을 사용하는 @Bean @Component가 존재한다.

 

 

 

@Bean과 @Component

@Bean은 스프링 컨텍스트에 빈으로 등록될 수 있는 메서드에 적용한다. 이 메서드는 스프링 컨테이너에 의해 실행되어 빈 인스턴스를 반환하고 대체로 구성 클래스(Configuration, @@Config)와 같은 곳에 Bean을 정의한다.

@Component는 스프링 컨텍스트에 빈으로 등록될 수 있는 모든 클래스에 적용될 수 있다. 스프링 어플리케이션에서 컴포넌트를 정의할 때 사용한다.

 

크게 보면 Component는 클래스 수준에서, Bean은 메서드 수준에서 사용한다. @Component는 클래스 자체가 빈으로 등록되지만, @Bean은 메서드가 반환하는 객체를 빈으로 등록하는데 사용된다.

@Component는 컴포넌트 스캔으로 등록이 되지만 @Bean은 명시적으로 빈을 등록해야 한다.

 

또한, @Component는 사용자가 정의한 코드에 대해서만 사용할 수 있는 반면, @Bean은 직접 작성한 코드에서 뿐만 아니라, 외부 라이브러리나 다른 소스에서 가져온 클래스나 객체에 대해서도 사용할 수 있다.

 

간단한 예를 들자면, 모든 List<String>에 대해서 생성자에서 ArrayList<String>()으로 반환해주고 싶다면 그것에 대한 Bean을 생성해주고 생성자 주입을 시켜주면 전역 List 변수를 사용자가 초기화해주지 않아도 사용할 수 있다.

 

 

 

프록시 패턴이란?

프록시 패턴은 어떠한 객체를 사용하고자 할 때, 객체를 직접적으로 참조하는 것이 아닌 해당 객체를 대변하는 객체를 통해 대상 객체에 접근하는 방식을 사용한다. 이를 통해 해당 객체가 메모리에 존재하지 않아도 기본적인 정보를 참조하거나 설정할 수 있고, 실제 객체의 기능이 필요한 시점까지 객체의 생성을 미룰 수 있다.

 

장점

  • 사이즈가 큰 객체가 로딩되기 전 프록시를 통해 참조 가능(LAZY 로딩)
  • 실제 객체의 public, protected 메소드를 숨기고 인터페이스를 통해 노출
  • 로컬에 없는 객체 사용 가능
  • 원래 객체의 접근에 대해 사전 처리 가능(AOP)

 

 

 

스프링 빈과 AOP 프록시 패턴

Bean은 프록시 패턴을 이용한 프록시 객체와 밀접한 관계이다. 프록시 객체는 Bean의 AOP 기능을 구현하기 위해 사용하여, 위 장점에서 언급했듯 Bean의 생성과 초기화 등의 과정에서 AOP가 적용될 수 있다. 이때, 프록시 객체는 실제 Bean 객체와 같은 인터페이스를 구현하면서 메서드 호출 시에 AOP 기능을 수행한다.

 

즉, Spring에서 Bean을 주입할 때는 프록시 객체를 사용하여 AOP 기능을 수행하며 이를 통해 공통적으로 사용되는 로직을 중복 작성하지 않고 적용한다.

 

AOP가 메서드 호출 전,후에 추가적인 로직을 수행할 때 기존 객체를 변경하지 않고 프록시 객체를 사용하여 구현한다. 또한, 프록시 객체는 Lazy Loading과도 관련이 있는 개념이다.

 

 

 

 

AOP 프록시 패턴이란?

스프링 AOP는 스프링 Bean에만 적용할 수 있으며 모든 AOP 기능을 제공하는 것이 목적이 아닌 중복 코드, 프록시 클래스 작성의 번거로움 등의 문제를 해결하기 위한 솔루션을 제공하는 것이다.

 

AOP를 적용하기 위해 대상 객체의 메서드를 호출하기 전, 후, 또는 예외가 발생했을 때 실행될 공통 기능을 작성해야한다. 즉, 프록시 객체는 기존의 객체의 메서드를 감싸 추가 기능이 더해진 형태인 것이고 이 Bean을 참조할 경우 AOP로 인한 기능이 더해진 프록시 객체를 거쳐 대상 객체의 메서드를 호출하게 된다. 마찬가지로 이 반환 값을 돌려주기 전에도 추가 기능을 수행할 수 있다.

 

클라이언트가 어떤 객체의 Bean을 호출하면, 그 객체의 동일한 인터페이스를 가진 프록시 객체가 생성되어 특정 AOP 기능을 프록시 객체 메서드에서 실행합니다. 이후에 실제 객체를 호출하여 기능을 수행한 다음, 다시 프록시 객체 메서드에서 이후 AOP 기능이 있다면 실행하고 호출부에 반환합니다.

 

 

 

 

데코레이터 패턴과의 차이

프록시 패턴과 데코레이터 패턴

위 그림을 보면 하나는 프록시, 하나는 데코레이터 패턴이다. 공부를 하다보니 실제 객체의 메서드에 추가 기능을 준다는 면에서 데코레이터 패턴과 유사하다고 생각이 들어 차이점을 찾아봤다.

 

데코레이터 패턴은 실제 객체를 내부에 가지고 있어 호출된 메서드를 먼저 실제 객체에 전달한 다음 그 결과를 수정하거나 추가 작업을 수행한 뒤 반환하기 때문에 객체에 기능을 추가하거나 수정할 수 있다. 그러나, AOP는 객체의 메서드를 호출할 때 미리 정의된 공통 기능을 일괄적으로 적용하는 기술이다. 그렇기 때문에 데코레이터 패턴은 객체의 기능을 추가하거나 수정하는 것에 초점을 두고 있다면, AOP는 여러 객체에 적용될 수 있는 공통 기능을 일괄적으로 적용하는 것에 초점을 두고 있다.

 

AOP 프록시 패턴은 내부에 실제 객체를 가지고 있지 않은 대신 실제 객체를 참조하는 참조 변수를 가지고 있다. 그래서 프록시 객체의 메서드를 호출하고 나면, 프록시 객체는 실제 객체를 참조하는 참조 변수를 통해 실제 객체의 메서드를 호출합니다.

 

추가되는 기능의 종류와 추가되는 방식에도 차이가 있다. AOP 프록시 패턴은 공통된 기능을 일괄적으로 추가하거나 수정하는 방식으로 동작하며, 데코레이터 패턴은 개별적으로 기능을 추가하거나 수정하는 방식으로 동작한다.

 

마지막으로 AOP 프록시 패턴은 여러 객체에 대해 공통된 부가 기능을 추가하는 것이 가능하다. 이것이 AOP가 관점 지향적인 기능을 제공하기 때문이다. 반면 데코레이터 패턴은 한 인터페이스에 대해서만 동작하며 객체의 구조를 변경하지 않고 객체의 기능을 동적으로 추가 및 변경할 수 있는 방법을 제공한다.

 

 

 

 

스프링 AOP 프록시 패턴 용어

관련 기능

  • @Aspect : AOP를 정의하는 클래스를 지정
  • @Pointcut : AOP기능을 메서드, 어노테이션 등 어디에 적용시킬지 설정, 설정하기 위한 수식들 존재
  • Advice : 언제 공통 기능을 핵심 로직에 적용할 지 정의
    • 종류
      • @Before : 메서드를 실행하기 이전
      • @After : 메서드를 실행한 후(예외가 발생하더라도)
      • @AfterReturning : 메서드가 정상적으로 종료될 때
      • @AfterThrowing : 메서드가 예외가 발생할 때
      • @Around : Before + After 모두 제어(예외가 발생하더라도)
  • JointPoint : Advice를 적용 가능한 지점
  • Weaving : Adivce를 핵심 로직 코드에 적용하는 것

'서버개발' 카테고리의 다른 글

Redis와 캐시  (0) 2023.03.17
Netty와 Spring Webflux  (0) 2023.03.17
TCP/IP Socket vs RESTful API vs gRPC vs WebSocket  (0) 2023.03.01
벌크 연산과 @Modifying 어노테이션  (0) 2023.02.28
비관적 락, 낙관적 락  (3) 2023.02.28