프록시 패턴이란?
- 사전적 의미로 대리 혹은 대리인과 같은 의미가 있다.
- 사용자가 접근하고 싶은 오브젝트에 접근하기 전에 프록시 오브젝트라 불리는 오브젝트에 접근을 먼저 하도록 강제하는 것이다.
- 오브젝트 접근 요청 시 해당 요청은 먼저 프록시 오브젝트에게 간다.
- 실생활에 비유하자면 연예인을 섭외하기 위해서 연예인에게 직접 전화를 거는 것이 아니라 매니저에게 전화를 걸어 섭외하는 것이다.
다이어그램으로 보기
Proxy
는 데코레이터 패턴처럼 필드 내부에서RealSubject
를 참조하고 있다.
활용 예제: 메서드의 소요시간 측정과 게으른 초기화 (Lazy initialization)
가정
- 자동차를 만드는 메서드가 있다.
- 자동차를 만드는데 걸리는 시간은 그때그때 다르다.
- 평균적으로 1초 ~ 1.5초가 걸린다고 가정한다.
목표
- 핵심 비즈니스 로직을 건들지 않고 아래와 같은 기능을 추가하고 싶다.
- 자동차를 만드는데 소요된 시간을 정확히 출력하고 싶다.
- 자동차를 만들지 않는다면 비싼 비용의 오브젝트를 초기화하고 싶지 않다.
인터페이스
public interface CarMaker {
void makeCar();
}
- 자동차를 만드는 메서드가 정의되어 있다.
- 위의 프록시 다이어그램에서
Subject
의 역할을 하게 될 것이다.
구현체
public class MyCarMaker implements CarMaker{
Random random = new Random();
@Override
public void makeCar() {
randomDelay();
System.out.println("드디어 차를 완성했습니다.");
}
private void randomDelay() {
try {
int randomDelay = random.nextInt(501) + 1000;
Thread.sleep(randomDelay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 랜덤 딜레이 후에 차가 완성되었다는 메세지를 표출한다.
- 랜덤 딜레이는 1초~1.5초가 걸린다.
- 위의 프록시 다이어그램에서
RealSubject
로 핵심 비즈니스가 들어있는 곳이다.
프록시
public class MyCarMakerProxy implements CarMaker {
CarMaker carMaker;
@Override
public void makeCar() {
if (this.carMaker == null) {
System.out.println("최초로 CarMaker 를 호출하여 CarMaker 가 생성됩니다.");
this.carMaker = new MyCarMaker();
}
long beforeMs = System.currentTimeMillis();
carMaker.makeCar();
long afterMs = System.currentTimeMillis();
System.out.println("걸린 시간 :" + (afterMs - beforeMs));
}
}
CarMaker
타입의 멤버를 가지고 있다.makeCar()
메서드가 한번도 호출되지 않는다면,null
로 남아있을 것이다.- 단, 한번이라도 호출되면 초기화 과정이 이뤄진다.
carMaker.makeCar()
를 호출하기 전에 앞 뒤로 시스템의 현재 시간을 밀리 세컨드로 받아낸다.- 그 결과를 토대로 걸린 시간을 측정한다.
- 위의 다이어그램에서
Proxy
의 역할이다.- 핵심 비즈니스와는 관련 없는 로직을 수행하고 있다.
클라이언트
public class Client {
public static void main(String[] args) {
CarMaker carMaker = new MyCarMakerProxy();
carMaker.makeCar();
carMaker.makeCar();
}
}
MyCarMakerProxy
객체를 이용해makeCar()
메서드를 두번 호출하고 있다.
결과
최초로 CarMaker 를 호출하여 초기화됩니다.
드디어 차를 완성했습니다.
걸린 시간 :1064
드디어 차를 완성했습니다.
걸린 시간 :1494
목표를 잘 달성했다.
- 초기화는
makeCar()
를 최초로 호출한 시점에 단 한번만 이뤄진다. - 메서드에 걸린 시간을 잘 표현해주고 있다.
프록시 패턴의 장점과 단점
장점
- 기존 코드를 변경하지 않고 새 기능 추가 가능.
- 객체지향 원칙 중 OCP (Open Closed Principle) 가 적용된다.
- 기존 코드가 해야하는 일을 안 건드려서 핵심 로직이 그대로 유지된다.
- SRP (Single Responsibility Principle) 를 지키는데도 도움을 준다.
- 로직의 시작 지점이나 끝 지점에 부가기능을 넣는 형식으로 이용 가능하다.
- 시간 재기, 초기화 지연 등 다양하게 활용 가능
단점
- 코드의 복잡도가 증가
- 단순한 클래스로 구현되어 있다면 인터페이스, 프록시 객체 등을 좀 추가해줘야 한다.
- 상속으로 구현이 가능하긴 한데 상속은 항상 제약사항을 같이 가져가서 조금 불편하다.
final
클래스라면 또 상속이 불가능하기도 하다.
- 상속으로 구현이 가능하긴 한데 상속은 항상 제약사항을 같이 가져가서 조금 불편하다.
- 단순한 클래스로 구현되어 있다면 인터페이스, 프록시 객체 등을 좀 추가해줘야 한다.
자바와 스프링의 프록시 패턴
다이나믹 프록시
- 자바에서는 리플렉션을 이용해 다이나믹 프록시라는 것을 제공한다.
- 런타임에 프록시를 적용하는 기법을 말한다.
- 타입이 다른 여러 메서드에 프록시를 적용할 때 필수적으로 이 방식이 사용된다.
- 이전의 프록시 예제는 인터페이스 하나로 적용하는 거여서 범용성이 없었다.
Proxy.newProxyInstance
를 통해 가능하다.
public class ProxyInJava {
public static void main(String[] args) {
ProxyInJava proxyInJava = new ProxyInJava();
proxyInJava.dynamicProxy();
}
private void dynamicProxy() {
CarMaker carMaker = getCarMakerProxy(new MyCarMaker());
carMaker.makeCar();
}
private CarMaker getCarMakerProxy(CarMaker target) {
return (CarMaker) Proxy.newProxyInstance(this.getClass().getClassLoader(),
new Class[]{CarMaker.class}, (proxy, method, args) -> {
System.out.println("다이나믹 프록시 시작");
method.invoke(target, args);
System.out.println("다이나믹 프록시 끝");
return null;
});
}
}
스프링 AOP
- 스프링이 관리하는 빈에 공통 관심사 로직을 적용해 프록시 패턴을 이용할 수 있다.
- 스프링 AOP 는 기본적으로 빈에 대해서만 동작한다.
- 아래 코드에서
@Aspect
와@Around
애노테이션을 이용해 AOP 를 이용한다. - 스프링 AOP 는 스프링 모든 기능들의 기반이 된다.
- 애노테이션을 이용해 트랜잭션 처리(
@Transactional
), 캐시(@Cacheable
) 처리를 하는 등의 기능이 있다.
- 애노테이션을 이용해 트랜잭션 처리(
@Aspect
@Component
public class PerfAspect {
@Around("bean(gameService)")
public void timestamp(ProceedingJoinPoint point) throws Throwable {
long before = System.currentTimeMillis();
point.proceed();
System.out.println(System.currentTimeMillis() - before);
}
}
CGLIB 프록시
- 인터페이스 등을 만들지 않고 프록시 패턴을 이용하고 싶을 때 유용하다.
레퍼런스
반응형
'Java > 자바 디자인 패턴' 카테고리의 다른 글
커맨드 패턴 (Command Pattern) 이란? (0) | 2023.04.28 |
---|---|
책임 연쇄 패턴 (Chain Of Responsibility) 이란? (2) | 2023.04.26 |
플라이웨이트 패턴 (Flyweight Pattern) 이란? (0) | 2023.04.22 |
퍼사드 패턴 (Facade Pattern) 이란? (0) | 2023.04.18 |
데코레이터 패턴 (Decorator Pattern) 이란? (0) | 2023.02.25 |