Spring 공부. Spring AOP란?
전에 해커톤에서 트랜잭션 처리가 잘 이루어지지가 않아 문제가 됐던 적이 있었다. 그냥 막연하게 @Transactional 쓰면 되지~ 라고 생각했었는데 이런 태도로는 발전이 없을 것 같고 또 같은 문제가 생길 것 같아 공부해두려고 한다.
Transactional의 동작 원리에 대해 공부하기 전에 알아두어야 할 개념이 있다. 바로 AOP(관점 지향 프로그래밍)이다.
AOP (Aspect Orient Programming, 관점 지향 프로그래밍)이란?
객체 지향 프로그래밍(OOP)에서는 주요 관심사에 따라 클래스를 분할한다. 이 클래스들은 보통 SRP(Single Responsibility Principle)에 따라 하나의 책임만을 갖게 설계된다. 하지만 클래스를 설계하다보면 로깅, 보안, 트랜잭션 등을 목적으로 여러 클래스에서 공통적으로 사용해야 할 부가 기능들이 생긴다.
이들은 주요 비즈니스 로직은 아니지만, 반복적으로 여러 곳에서 쓰이는 데 이를 흩어진 관심사(Cross Cutting Concerns)라고 한다.
AOP의 주요 개념
- Aspect: Advice + PointCut로 AOP의 기본 모듈 (묶어놓은 모듈)
- Advice: Target에 제공할 부가 기능을 담고 있는 모듈 (Aspect 안의 기능)
- Target: Advice이 부가 기능을 제공할 대상 (Advice가 적용될 비즈니스 로직)(적용이 되는 대상)
- JointPoint: Advice가 적용될 위치 (적용 시점) → 메서드 진입 지점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용 가능
- PointCut: Target을 지정하는 정규 표현식 (적용해야 할 위치)
Sprint AOP는 기본적으로 프록시 객체를 통해 동작한다. 객체를 직접적으로 참조하는 것이 아니라, 해당 객체를 대행하는 객체(proxy 객체)를 통해 대상 객체에 접근하는 방식을 말한다. Bean으로 등록된 클래스가 AOP 대상이면, Spring이 그 객체 대신 프록시 객체를 만들어서 빈으로 등록한다. 그래서 메서드 호출이 일어날 때 실제 객체가 아닌 프록시를 먼저 거쳐서 부가기능(Advice: 트랜잭션, 로깅 등)을 수행하고 원래 로직을 실행할 수 있게 된다.
좀 더 자세히 설명하자면, Spring AOP는 사용자의 특정 호출 시점에 IoC 컨테이너에 의해 AOP를 할 수 있는 Proxy Bean을 생성해준다. 동적으로 생성된 Proxy Bean은 타깃의 메서드가 호출되는 시점에 부가 기능을 추가할 메서드를 자체적으로 판단하고 가로채서 부가기능을 주입한다. 이처럼 호출 시점에 동적으로 위빙을 한다 하여 런타입 위빙(Runtime Weaving)이라고 한다.
따라서 Spring AOP는 런타입 위빙의 방식을 기반으로 하며, Spring에서 런타입 위빙을 할 수 있도록 상황에 따라 JDK Proxy와 CGLib Proxy를 통해 Proxy Bean을 생성한다.
JDK Dynamic Proxy vs CGLib Proxy
쉽게 말하자면 Spring은 AOP Proxy를 생성하는 과정에서 자체 검증 로직을 통해 Target의 인터페이스 유무를 판단한다.
이 때, 만약 타깃이 하나 이상의 인터페이스를 구현하고 있는 클래스라면 JDK Proxy의 방식으로 생성되고 인터페이스를 구현하지 않는 클래스라면 CGLib 방식으로 AOP 프록시를 생성한다.
어떤 방식을 기본으로 채택하는지는 Spring framework냐, Spring Boot냐에 따라 다르다.
- Spring Boot → CGLib Proxy를 기반. (인터페이스가 없어도 프록시가 생성되게 하기 위해) 인터페이스 존재 ⭕: JDK Proxy 사용.
- Spring Framework → JDK Proxy를 기반. (Spring Framework의 설계 철학: interface + impl 구조) 인터페이스 존재 ❌: CGLib 사용 명시해줘야 함. (@EnableAspectJAutoProxy(proxyTargetClass = true)
이에 대한 자세한 내용은 아래의 블로그 글을 보면 잘 정리되어있다!
https://gmoon92.github.io/spring/aop/2019/04/20/jdk-dynamic-proxy-and-cglib.html
참고:
- https://gmoon92.github.io/spring/aop/2019/04/20/jdk-dynamic-proxy-and-cglib.html