Spring AOP와 관련된 개념들

다람·2024년 9월 6일
0

Spring

목록 보기
1/1

Spring AOP와 관련된 개념들

1. 동적 프록시 (Dynamic Proxy)

동적 프록시는 런타임에 특정 인터페이스를 구현한 프록시 객체를 동적으로 만들어서 사용하는 방식이다. Java의 Proxy 클래스를 사용하거나, Spring에서는 AOP(Aspect-Oriented Programming)를 구현할 때 동적 프록시를 사용한다.

  • 특징 : 어떤 메서드 호출 전후에 로깅을 추가하고 싶다면, 기존 코드를 변경하지 않고 프록시 객체를 통해 로깅 기능을 추가할 수 있다.
  • 한계 : 동적 프록시는 기본적으로 인터페이스가 있는 클래스만 지원한다.

2. CGLIB (Code Generation Library)

CGLIB는 인터페이스가 없는 클래스에도 프록시를 적용할 수 있게 도와주는 라이브러리이다. 동적 프록시가 인터페이스가 필요하다면, CGLIB는 클래스를 상속받아 프록시 객체를 생성한다.

  • 특징 : 메서드를 상속하여 프록시를 생성하기 때문에 인터페이스가 없는 클래스에도 사용할 수 있다.
  • 한계 : final 메서드에는 적용할 수 없어요. 왜냐하면 상속을 통해 메서드를 오버라이딩하기 때문이다.

3. AspectJ

AspectJ는 AOP의 한 종류로, 컴파일 시점에 프록시를 생성하는 방식이다. Spring AOP가 동적 프록시를 주로 사용하지만, AspectJ는 애스펙트(Aspect)를 코드에 직접 주입하는 방식이다.

  • 특징
    • 성능 면에서 빠르고, 더 강력한 AOP 기능을 제공하지만, Spring AOP보다 설정이 복잡할 수 있다.
    • Spring AOP는 런타임 시점에만 동작하지만, AspectJ는 컴파일 시점, 클래스 로딩 시점 등 더 다양한 시점에서 AOP 기능을 적용할 수 있다.

4. Spring AOP

Spring AOP는 동적 프록시와 CGLIB을 사용해 런타임에 Aspect(공통 기능)를 적용하는 방식이다. 인터페이스가 있는 경우는 동적 프록시를 사용하고, 없는 경우는 CGLIB을 사용해 프록시를 생성한다.

  • 주요 개념:
    - Aspect : 공통 관심사 (예. 로깅, 트랜잭션 관리)
    - Join Point : 애스펙트를 적용할 수 있는 지점 (예. 메서드 호출)
    - Pointcut : 애스펙트를 적용할 구체적인 지점 (예. 특정 메서드)
    - Advice : 실제로 실행되는 코드 (예. 로깅 코드)

정리

방식사용법적용대상프록시 생성 시점장단점
동적 프록시JDK 동적 프록시를 사용인터페이스런타임간단하지만 인터페이스가 필수, 구체 클래스에는 적용 불가능
CGLIB바이트코드 조작을 통한 클래스 상속구체 클래스런타임인터페이스가 필요 없음, final 메서드에 적용 불가능
AspectJ바이트코드 조작을 통한 AOP 적용클래스, 필드, 메서드 등컴파일, 로딩, 런타임강력한 기능 제공(메서드 호출 외에도 다양한 지점에 적용이 가능)
  • 동적프록시 : 인터페이스를 통한 간단한 AOP 구현
  • CGLIB : 인터페이스가 없는 구체 클래스에 적용할 때 사용
  • AspectJ : 더 다양한 지점에 AOP를 적용할 수 있는 강력한 기능 제공, 바이트코드를 컴파일 시점이나 클래스 로딩 시점에 조작 가능

내부 호출 문제

내부 호출 문제란 Spring AOP에서 발생하는 한계 중 하나로, 프록시 기반 AOP가 동작하는 방식에서 기인한다. 같은 클래스 내의 메서드 간 호출이 있을 때 AOP가 적용되지 않는 상황을 말한다.

왜 이런 문제가 발생할까?

  • 프록시 기반 AOP는 메서드 호출을 가로채기 위해서 실제 객체 대신 프록시 객체를 사용한다. 이 프록시 객체는 외부에서 해당 객체의 메서드를 호출할 때 AOP를 적용하게 만든다.
  • 하지만 클래스 내부에서 자기 자신의 메서드를 호출할 때는 프록시 객체를 거치지 않고, 바로 실제 객체의 메서드를 호출한다. 그래서 AOP로 적용한 기능이 작동하지 않는 것이다.

해결 방법

이러한 문제를 해결하려면 self-invocation(자기 호출)을 피해야 한다. 보통은 내부 호출을 외부에서 호출하도록 구조를 변경하거나, AOP 기능을 강제로 적용할 수 있는 방법을 사용한다고 한다.

1. 설계 변경

내부 호출을 피하고, 외부에서 해당 메서드를 호출하게 설계한다.
메서드를 각기 다른 Bean으로 분리한다.

2. AspectJ

프록시 방식이 아닌 바이트코드를 수정하는 방식의 AOP로, 내부 호출에서도 제대로 동작한다.

결론

동적 프록시는 간단하고 효과적이지만 인터페이스가 필요한 반면, CGLIB는 더 많은 유연성을 제공해 인터페이스가 없는 구체 클래스에도 적용할 수 있다.
AspectJ는 이러한 모든 한계를 넘어선 AOP 구현 방식으로, 컴파일 시점부터 AOP 기능을 적용할 수 있어 더 많은 제어와 성능 최적화를 가능하게 한다.

내부 호출 문제는 AOP 적용 시 자주 발생하는 문제점으로 프록시 객체를 통해 AOP가 적용되기 때문에 자기 자신을 호출하는 경우 AOP가 적용되지 않는 한계가 발생한다. 이를 해결하기 위해 설계 변경이나 AspectJ 같은 더 강력한 AOP 구현 방식을 고려해야 한다.

Spring AOP는 런타임 시점에 가벼운 로깅, 트랜잭션 관리 등과 같은 부가적인 기능을 쉽게 적용할 수 있도록 해주지만, 그만큼 사용 환경에 따라 적절한 프록시 방식(CGLIB 또는 동적 프록시)을 선택해야 한다.
AspectJ는 더 강력하고 다양한 기능을 제공하지만, 사용 설정이 복잡하다는 단점이 있다.

따라서 Spring AOP와 프록시를 사용할 때는 다음을 고려하는 것이 중요하다.

  1. 성능과 간결함이 중요한 상황에서는 동적 프록시나 CGLIB을 고려할 수 있다.
  2. 더 복잡한 AOP 기능이 필요하거나, 내부 호출 문제를 해결해야 한다면 AspectJ를 사용하는 것이 적합하다.
  3. AOP가 만능은 아니기 때문에 프로젝트의 요구 사항에 따라 어떤 AOP 방식을 사용할지 신중하게 선택해야한다.

AOP는 코드의 간결성을 유지하면서도 중복되는 부가 기능을 추상화하여 재사용할 수 있도록 해주지만, 요구 사항에 맞는 적절한 방식을 선택하는 것이 핵심이 될 것이다.

profile
개발하는 다람쥐

0개의 댓글