클라이언트와 서버개념에서 일반적으로 클라이언트가 서버를 직접 호출하고 처리 결과를 받는다.
클라이언트가 요청한 결과를 서버에 직접 요청하는게 아니라 대리자를 통해서 간접적으로 서버에 요청할 수 있다. 여기서 대리자를 프록시(Proxy)라고 한다.
프록시는 체인을 걸어 여러개를 적용할 수도 있다.
객체에서 프록시가 되기위해선 클라이언트가 서버에게 요청한 것인지 프록시에게 요청한 것인지 몰라야 된다. 그러므로 서버와 프록시는 같은 인터페이스를 사용해야 한다.
그러면 클라이언트가 사용하는 서버객체를 프록시 객체로 변경해도 클라이언트 코드를 변경하지 않고 동작할 수 있게 된다.
서버와 프록시가 같은 인터페이스를 사용하므로 DI를 이용해 대체 가능하다.
프록시를 통해서 할 수 있는 일은 2가지가 있다.
프록시 객체가 중간에 있으면 크게 접근제어와 부가 기능 추가를 수행할 수 있다.
같은 프록시를 사용하는 방법이지만 GOF 디자인 패턴에서는 의도에 따라서 프록시 패턴과 데코레이터 패턴으로 구분한다.
Subject 인터페이스
public interface Subject {
String operation();
}
RealSubject
@Slf4j
public class RealSubject implements Subject{
@Override
public String operation() {
log.info("실제 객체 호출");
sleep(1000);
return "data";
}
private void sleep(int i) {
try {
Thread.sleep(i);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
ProxyPatternClient
public class ProxyPatternClient {
private Subject subject;
public ProxyPatternClient(Subject subject) {
this.subject = subject;
}
public void execute(){
subject.operation();
}
}
ProxyPatternTest
public class ProxyPatternTest {
@Test
void noProxyTest(){
RealSubject realSubject=new RealSubject();
ProxyPatternClient client = new ProxyPatternClient(realSubject);
client.execute();
client.execute();
client.execute();
}
}
RealSubject - 실제 객체 호출
RealSubject - 실제 객체 호출
RealSubject - 실제 객체 호출
이 데이터가 한번 조회하고 변하지 않는 데이터라면 어딘가에 저장하고 이미 조회한 데이터를 사용하는것이 성능상 좋다. 이런것을 캐시라고 한다. 프록시 패턴의 주용기능은 접근제어이다. 캐시도 접근 자체를 제어하는 기능 중 하나이다.
@Slf4j
public class CacheProxy implements Subject{
private Subject target;
private String cacheValue;
public CacheProxy(Subject target) {
this.target = target;
}
@Override
public String operation() {
log.info("프록시 호출");
if(cacheValue==null){
cacheValue=target.operation();
}
return cacheValue;
}
}
public class ProxyPatternTest {
@Test
void noProxyTest(){
RealSubject realSubject=new RealSubject();
ProxyPatternClient client = new ProxyPatternClient(realSubject);
client.execute();
client.execute();
client.execute();
}
@Test
void cacheProxy(){
Subject subject=new RealSubject();
Subject cacheProxy=new CacheProxy(subject);
ProxyPatternClient client = new ProxyPatternClient(cacheProxy);
client.execute();
client.execute();
client.execute();
}
}
cacheProxyTest()
realSubject와 cacheProxy를 생성하고 둘을 연결한다. 결과적으로 cacheProxy가 realSubject를 참조하는 런타임 객체 의존 관계가 완성된다.
그리고 client에 realSubject가 아닌 cacheProxy를 주입한다. 이 과정을 통해 client -> cacheProxy -> realSubject런타임 의존 관계가 완성된다.
실행 결과
CacheProxy - 프록시 호출
RealSubject - 실제 객체 호출
CacheProxy - 프록시 호출
CacheProxy - 프록시 호출
결과 정리 프록시 패턴의 핵심은 RealSubject코드와 클라이언트 코드를 변경하지 않고 프록시를 도입해서 접근 제어를 했다는 점이다.
그리고 클라이언트 코드의 변경 없이 자유롭게 프록시를 넣고 뺄수 있다. 실제 클라이언트 입장에서는 프록시 객체가 주입되었는지, 실제 객체가 주입됬는지 알 수 없다.