소프트웨어 개발/Design Pattern

⑨ 디자인 패턴(Design Pattern) - proxy

늘근이 2015. 9. 21. 21:53

프록시란 참 여러군데서 많이 쓰인다. 인터넷에서는 우회하는데 쓰이기도 하고, 빠르게 내용을 가져올때 쓰기도 한다.

일단 디자인패턴에서 쓰이는 프록시는 빠르게 내용을 가져오고 정말 중요한게 있으면 본 서버에서 데이터를 가져오는 인터넷에서의 프록시와 개념이 같다.

일단, 어떠한 작업이 있다고 하자. 가벼운일과 무거운일. 가벼운일을 처리하는데는 프록시를 쓰고 무거운일을 처리하는데는 본 기능을 부른다. 이렇게 해서 괜히 헤비한 객체의 생성을 피할수 있다.

마치 회사에서의 사원의 모습같다. 온갖 짬처리를 하다가 제대로 큰건이 있으면 보고를 통해서 과장이 처리하게 되는것이다.

위에서 프록시는 지가 처리할수 있으면 처리하며(짬처리) 진짜 본체는 중요한 일만 골라서 하는 놈이다.

이 포스트에서는 단 세개의 직관적인  public 메서드를 사용한다.

getName() <- 이름을 얻는 메서드 (Proxy)

setName() <- 이름을 설정하는 메서드 (Proxy)

work()      <- 진짜 헤비한 일 (Real)


일단 위의 메서드를 구현하기 위해서 다음과 같은 인터페이스를 만든다. IF라고 만들어놓으니 생각해보니 조건문하고 예약어가 겹쳐서 조금 나도 어이가 없다.


public interface IF {
	public void   setName(String name);
	public String getName();
	public void   work(String string); //heavy한 성격의 일
}


이제, 이를 상속받아 구현하는 Proxy 를 하나 만들어준다. 얘는 내부적으로 Real 객체를 가지고 있으면서 진짜로 work()메서드가 호출될때 Real 클래스를 인스턴스화 시킨다. 그리고 일은 Real을 호출해서 한다. 얘는 사실 단지 하는일이 setName() 과 getName()의 짬처리성 일만 한다.


public class Proxy implements IF {
	private String name;
	private Real real;

	@Override
	public synchronized void setName(String name) {
		if(real != null) real.setName(name);
		this.name = name;
	}

	@Override
	public String getName() {
		return name;
	}

	@Override
	public void work(String string) {
		realize();
		real.work(name);

	}
	
	private synchronized void realize() {
		if (real == null) real = new Real();
	}

}// end proxy


Real에서는 work 에서 진짜 루프문을 도는 빡센(?)작업을 한다. 아 생각해보니 이게 더 짬처리같기도 하다.


public class Real implements IF{

	private String name;
	
	@Override
	public void setName(String name) {
		this.name = name;		
	}

	@Override
	public String getName() {
		return name;
	}

	@Override
	public void work(String name) {
		for (int i = 0; i < 10000 ; i++) System.out.println(name + " 이것은 빡센 일입니다.");
	}

}


마지막으로 이를 사용하는 Main 메서드다.

public class Main {
	public static void main(String[] args) {
		
		IF worker = new Proxy();
		worker.setName("고락가락");      //여기까지는 프록시
		worker.work("진짜 Real이 구동됨");  //여기는 진짜 Real
		
		
	}
}


위의 코드에서 이상한것이 하나 있다. 바로 Synchronized 키워드를 쓴것이다. 어차피 멀티쓰레딩 프로그래밍을 하지않으면 상관이 없지만 이 Proxy놈은 여기저기서 많이 쓰이는 놈이라고 할수있다. 여기에서는 약간의 설계상 미스가 있을수도 있는데 name이라는 변수는 Proxy에서도 쓰이고 Real 에서도 쓰이는 정신없는 놈이다.  만약 쓰레드가 여러개가 접근하여 name을 바꾸고 이상한짓을 한다면 쓰레드가 이것저것을 주고받을때 name이 일관성이 깨져버릴수 있다. 따라서 동기화를 방지하기 위해서는 위와같이 써주어서 필드를 보호하면 되겠다.


 

그래서 이 프록시를 도대체 어떻게 응용해서 써먹나.

JAVA RMI (Remote Method Invocation) 은 네트워크 원격지의 메서드를 쉽게 가져와서 써먹는다고 한다. 별로 프록시의 장점같지는 않은데.. 뭐 그렇다. 이것은 됐고, 예를들어 게임에서 로딩화면을 보자. 예전 게임 로딩화면은 뭔가 까맣고 지루한 화면이지만 요즘 로딩화면은 한편의 영화를 보여주는 듯한것들이 있다. 한편의 동영상 로딩하는데는 별로 시간이 걸리지 않기 때문에 금방 Proxy를 이용해 메서드를 호출해줄수 있는데, 게임에 관련된 모든 3D객체들을 로딩하는데는 상당한 시간이 든다. 따라서 Proxy를 이용해 동영상을 틀어주는 기능만 작동하게 하고 Real을 이용해 게임에 관련된 여러 기능을 가진 객체를 구성토록 할수있다.