개념
프록시는 '대리인'이라는 뜻을 가지며, 소프트웨어에서 클라이언트와 대상 객체 사이에서 중간 역할을 수행하는 객체를 의미합니다.
용도
- 보안(Security): 직접 접근을 막고 제어할 수 있음.
- 성능 향상(Caching, Lazy Loading): 객체 생성을 지연시키거나 캐시를 활용함.
- 로깅(Logging) 및 모니터링(Monitoring): 메서드 호출 전후로 로깅을 추가할 수 있음.
- 트랜잭션(Transaction) 관리: 데이터베이스 트랜잭션을 프록시 객체에서 처리할 수 있음.
AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)와 프록시 기반 동작 방식
Spring AOP는 프록시(Proxy) 기반으로 동작하며, 주로 Spring의 @Transactional, @Aspect, @Around 등의 기능이 프록시를 통해 구현된다.
프록시 기반 AOP 동작 방식
1. 프록시 객체 생성
- Spring은 대상 객체(Target Object) 대신 프록시 객체를 생성하여 클라이언트 요청을 처리한다.
- 클라이언트가 메서드를 호출하면 실제 객체 대신 프록시 객체가 먼저 호출된다.
2. 프록시 객체가 Advice 실행
- 프록시 객체는 등록된 Advice(부가 기능, 예: 로깅, 트랜잭션, 보안 검사 등) 를 실행한 후, 대상 객체의 메서드를 실행한다.
3. 대상 객체의 메서드 실행
- 프록시가 부가 기능을 적용한 후, 실제 대상 객체(Target Object)의 메서드를 실행한다.
4. 메서드 실행 후 후처리 수행
- 메서드 실행이 끝난 후, 프록시 객체는 필요에 따라 후처리 작업(예: 트랜잭션 커밋/롤백, 로깅)을 수행한다.
Spring AOP의 두 가지 프록시 방식
Spring에서는 JDK 동적 프록시(Dynamic Proxy)와 CGLIB(Code Generation Library) 프록시를 사용하여 AOP를 구현한다.
1. JDK 동적 프록시(Dynamic Proxy)
- java.lang.reflect.Proxy 를 사용
- 인터페이스 기반(Interface-based) 프록시를 생성
- 대상 객체가 인터페이스를 구현하고 있을 경우 적용됨
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Service {
void execute();
}
class RealService implements Service {
public void execute() {
System.out.println("실제 객체의 메서드 실행");
}
}
class ServiceProxyHandler implements InvocationHandler {
private final Object target;
public ServiceProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("메서드 실행 전 로깅");
Object result = method.invoke(target, args);
System.out.println("메서드 실행 후 로깅");
return result;
}
}
public class ProxyExample {
public static void main(String[] args) {
Service realService = new RealService();
Service proxy = (Service) Proxy.newProxyInstance(
realService.getClass().getClassLoader(),
new Class[]{Service.class},
new ServiceProxyHandler(realService)
);
proxy.execute();
}
}
2. CGLIB(Code Generation Library) 기반 프록시
클래스 기반 프록시(Class-based Proxy)를 생성.
대상 객체가 인터페이스를 구현하지 않은 경우에도 프록시를 만들 수 있음.
Spring Boot 2.x 이상에서는 기본적으로 CGLIB를 사용하지 않고, 인터페이스가 없을 경우 JDK 동적 프록시를 사용.
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
class RealService {
public void execute() {
System.out.println("실제 객체의 메서드 실행");
}
}
class CglibProxy implements MethodInterceptor {
private final Object target;
public CglibProxy(Object target) {
this.target = target;
}
public Object createProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("메서드 실행 전 로깅");
Object result = proxy.invoke(target, args);
System.out.println("메서드 실행 후 로깅");
return result;
}
}
public class CglibProxyExample {
public static void main(String[] args) {
RealService realService = new RealService();
RealService proxy = (RealService) new CglibProxy(realService).createProxy();
proxy.execute();
}
}
두 코드의 실행결과
메서드 실행 전 로깅
실제 객체의 메서드 실행
메서드 실행 후 로깅
Spring에서의 AOP 적용 예제
Spring에서는 @Aspect와 @Around를 사용하여 AOP를 적용할 수 있다.
AOP를 이용한 트랜잭션 관리 예제
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
@Around("execution(* com.example.service.*.*(..))") // 특정 패키지의 모든 메서드 실행 시 AOP 적용
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("메서드 실행 전 로깅: " + joinPoint.getSignature());
Object result = joinPoint.proceed();
System.out.println("메서드 실행 후 로깅: " + joinPoint.getSignature());
return result;
}
}
-> com.example.service 패키지의 모든 메서드는 실행 전후로 로깅이 수행됩니다.
정리
프록시란? 클라이언트와 실제 객체 사이에서 대리 역할을 하는 객체
AOP에서의 프록시 동작 방식
1. 클라이언트 요청 -> 프록시 객체 실행
2. 프록시에서 Advice 실행
3. 대상 객체 메서드 실행
4. 후처리 (예: 트랜잭션 커밋/롤백, 로깅 등)
Spring에서 AOP는 주로 JDK 동적 프록시 또는 CGLIB을 사용하여 구현됨.
- JDK 동적 프록시 : 인터페이스 기반 프록시
- CGLIB 프록시 : 클래스 기반 프록시
Spring AOP에서 @Aspect와 @Around를 사용하여 부가 기능(로깅, 트랜잭션 등)을 손쉽게 적용 가능
'Spring' 카테고리의 다른 글
개념
프록시는 '대리인'이라는 뜻을 가지며, 소프트웨어에서 클라이언트와 대상 객체 사이에서 중간 역할을 수행하는 객체를 의미합니다.
용도
- 보안(Security): 직접 접근을 막고 제어할 수 있음.
- 성능 향상(Caching, Lazy Loading): 객체 생성을 지연시키거나 캐시를 활용함.
- 로깅(Logging) 및 모니터링(Monitoring): 메서드 호출 전후로 로깅을 추가할 수 있음.
- 트랜잭션(Transaction) 관리: 데이터베이스 트랜잭션을 프록시 객체에서 처리할 수 있음.
AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)와 프록시 기반 동작 방식
Spring AOP는 프록시(Proxy) 기반으로 동작하며, 주로 Spring의 @Transactional, @Aspect, @Around 등의 기능이 프록시를 통해 구현된다.
프록시 기반 AOP 동작 방식
1. 프록시 객체 생성
- Spring은 대상 객체(Target Object) 대신 프록시 객체를 생성하여 클라이언트 요청을 처리한다.
- 클라이언트가 메서드를 호출하면 실제 객체 대신 프록시 객체가 먼저 호출된다.
2. 프록시 객체가 Advice 실행
- 프록시 객체는 등록된 Advice(부가 기능, 예: 로깅, 트랜잭션, 보안 검사 등) 를 실행한 후, 대상 객체의 메서드를 실행한다.
3. 대상 객체의 메서드 실행
- 프록시가 부가 기능을 적용한 후, 실제 대상 객체(Target Object)의 메서드를 실행한다.
4. 메서드 실행 후 후처리 수행
- 메서드 실행이 끝난 후, 프록시 객체는 필요에 따라 후처리 작업(예: 트랜잭션 커밋/롤백, 로깅)을 수행한다.
Spring AOP의 두 가지 프록시 방식
Spring에서는 JDK 동적 프록시(Dynamic Proxy)와 CGLIB(Code Generation Library) 프록시를 사용하여 AOP를 구현한다.
1. JDK 동적 프록시(Dynamic Proxy)
- java.lang.reflect.Proxy 를 사용
- 인터페이스 기반(Interface-based) 프록시를 생성
- 대상 객체가 인터페이스를 구현하고 있을 경우 적용됨
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Service {
void execute();
}
class RealService implements Service {
public void execute() {
System.out.println("실제 객체의 메서드 실행");
}
}
class ServiceProxyHandler implements InvocationHandler {
private final Object target;
public ServiceProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("메서드 실행 전 로깅");
Object result = method.invoke(target, args);
System.out.println("메서드 실행 후 로깅");
return result;
}
}
public class ProxyExample {
public static void main(String[] args) {
Service realService = new RealService();
Service proxy = (Service) Proxy.newProxyInstance(
realService.getClass().getClassLoader(),
new Class[]{Service.class},
new ServiceProxyHandler(realService)
);
proxy.execute();
}
}
2. CGLIB(Code Generation Library) 기반 프록시
클래스 기반 프록시(Class-based Proxy)를 생성.
대상 객체가 인터페이스를 구현하지 않은 경우에도 프록시를 만들 수 있음.
Spring Boot 2.x 이상에서는 기본적으로 CGLIB를 사용하지 않고, 인터페이스가 없을 경우 JDK 동적 프록시를 사용.
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
class RealService {
public void execute() {
System.out.println("실제 객체의 메서드 실행");
}
}
class CglibProxy implements MethodInterceptor {
private final Object target;
public CglibProxy(Object target) {
this.target = target;
}
public Object createProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("메서드 실행 전 로깅");
Object result = proxy.invoke(target, args);
System.out.println("메서드 실행 후 로깅");
return result;
}
}
public class CglibProxyExample {
public static void main(String[] args) {
RealService realService = new RealService();
RealService proxy = (RealService) new CglibProxy(realService).createProxy();
proxy.execute();
}
}
두 코드의 실행결과
메서드 실행 전 로깅
실제 객체의 메서드 실행
메서드 실행 후 로깅
Spring에서의 AOP 적용 예제
Spring에서는 @Aspect와 @Around를 사용하여 AOP를 적용할 수 있다.
AOP를 이용한 트랜잭션 관리 예제
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
@Around("execution(* com.example.service.*.*(..))") // 특정 패키지의 모든 메서드 실행 시 AOP 적용
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("메서드 실행 전 로깅: " + joinPoint.getSignature());
Object result = joinPoint.proceed();
System.out.println("메서드 실행 후 로깅: " + joinPoint.getSignature());
return result;
}
}
-> com.example.service 패키지의 모든 메서드는 실행 전후로 로깅이 수행됩니다.
정리
프록시란? 클라이언트와 실제 객체 사이에서 대리 역할을 하는 객체
AOP에서의 프록시 동작 방식
1. 클라이언트 요청 -> 프록시 객체 실행
2. 프록시에서 Advice 실행
3. 대상 객체 메서드 실행
4. 후처리 (예: 트랜잭션 커밋/롤백, 로깅 등)
Spring에서 AOP는 주로 JDK 동적 프록시 또는 CGLIB을 사용하여 구현됨.
- JDK 동적 프록시 : 인터페이스 기반 프록시
- CGLIB 프록시 : 클래스 기반 프록시
Spring AOP에서 @Aspect와 @Around를 사용하여 부가 기능(로깅, 트랜잭션 등)을 손쉽게 적용 가능