본문 바로가기

디자인패턴

프록시 패턴(Proxy Pattetn)

1. 프록시 패턴이란?

프록시(Proxy)를 번역하면 대리자, 대변인의 의미를 갖고 있다. 이 단어의 뜻과 마찬가지로 프로그램에도 똑같이 적용되는데, 프록시에게 어떤 일을 대신 시키는 것이다.

 

어떤 객체를 사용하고자 할 때, 객체를 직접적으로 참조하는 것이 아닌 해당 객체를 대항하는 객체를 통해 대상 객체에 접근하는 방식을 사용하면 해당 객체가 메모리에 존재하지 않아도 기본적인 정보를 참조하거나 설정할 수 있고, 실제 객체의 기능이 필요한 시점까지 객체의 생성을 미룰 수 있다.

 

2. 프록시 패턴의 장단점

프록시패턴 장점

  • 사이즈가 큰 객체가 로딩되기 전에도 프록시를 통해 참조를 할 수 있다.
  • 실제 객체의 public, protected 메소드를 숨기고 인터페이스를 통해 노출시킬 수 있다.
  • 로컬에 있지 않고 떨어져있는 객체를 사용할 수 있다.
  • 원래 객체에 접근에 대해 사전처리를 할 수 있다.

프록시패턴 단점

  • 객체를 생성할 때 한 단계를 거치게 되므로, 빈번한 객체 생성이 필요한 경우 성능이 저하될 수 있다.
  • 프록시 내부에서 객체 생성을 위해 스레드가 생성, 동기화가 구현되어야 하는 경우 성능이 저하될 수 있다.
  • 로직이 난해해져 가독성이 떨어질 수 있다.

 

3. 프록시 패턴의 종류

1) 가상프록시


꼭 필요로 하는 시점까지 객체의 생성을 연기하고, 해당 객체가 생성된 것 처럼 동작하도록 만들고 싶을 때 사용하는 패턴이다. 프록시 클래스에서 작은 단위의 작업을 처리하고 리소스가 많이 요구되는 작업들

package proxy_virtual;

public interface Image {
	public void display();
}

이 필요할 경우만 주체 클래스를 사용하도록 구현한다.

 

2) 원격프록시


원격 객체에 대한 접근을 제어 로컬 환경에 존재하며, 원격 객체에 대한 대변자 역할을 하는 객체 서로 다른 주소 공간에 있는 객체에 대해 마치 같은 주소 공간에 있는 것 처럼 동작하게 하는 패턴이다.(예: Google Docs)

 

3) 보호프록시


주체 클래스에 대한 접근을 제어하기 위한 경우에 객체에 대한 접근 권한을 제어하거나 객체마다 접근 권한을 달리하고 싶을 경우 사용하는 패턴으로 프록시 클래스에서 클라이언트가 주체 클래스에 대한 접근을 허용할지 말지 결정하도록 할 수 있다.

 

4. 프록시 패턴 예시

예를 들어 용량이 큰 이미지와 글이 같이 있는 문서를 화면에 띄운다고 가정했을 때, 텍스트는 용량이 작아서 빠르게 나타나지만 이미지는 용량이 크기 때문에 느리게 로딩되는 것을 확인할 수 있다. 만약 이렇게 처리되지 않고 텍스트와 이미지 로딩이 모두 끝난 후 화면이 나온다면 사용자는 로딩이 끝날 때까지 기다려야 한다.

 

따라서, 로딩이 먼저 끝난 텍스트를 먼저 나오게 하는 것이 좋다. 이와 같은 방식을 가지려면 텍스트 처리용 프로세스, 이미지 처리용 프로세스를 별도로 운영하면 될 것이다.

 

[Image.java]

package proxy_virtual;

public interface Image {
	public void display();
}

먼저 위와 같이, Image라는 인터페이스를 만든다.

 

[RealImage.java]

package proxy_virtual;

public class RealImage implements Image {
	private String filename;
	
	// 파일명을 받아서 로딩할 준비를 함
	public RealImage(String filename) {
		this.filename = filename;
		loadFromDisk(filename);
	}
	
	@Override
	public void display() {
		System.out.println("Displaying: " + this.filename);
	}
	
	private void loadFromDisk(String filename) {
		System.out.println("Loading: " + this.filename);
	}
}

 

바로 앞서 생성한 부모 인터페이스를 상속받기 위해 RealImage 클래스를 만들고, 그 안에 filename이라는 문자열 변수를 생성하였다. 그리고 당연히 오버라이딩을 통하여 부모 인터페이스에서 선언한 메소드를 재구현하도록 만들어 주었다.

 

[ProxyImage.java]

package proxy_virtual;

public class ProxyImage implements Image {
	private RealImage realImage;
	private String filename;
	
	public ProxyImage(String filename) {
		this.filename = filename;
	}
	
	@Override
	public void display() {
		// 객체 생성 타이밍을 조절함
		if (realImage == null) {
			realImage = new RealImage(filename);
		}
		realImage.display();
	}
}

 

이미지 인터페이스를 상속받는 ProxyImage 클래스를 하나 더 만든다. 오버라이딩 역시 당연히 해 줘야 하는데, 이때 realImage가 비어있으면 객체를 생성하게끔 한다.

 

[ProxyPatternDemo.java]

package proxy_virtual;

public class ProxyPatternDemo {

	public static void main(String[] args) {
		Image image = new ProxyImage("test_test_big.png");
		System.out.println("At this stage, the image is not created");
		
		image.display();
		System.out.println("=============================");
		
		image.display();
	}
}

 

[출력값]

'디자인패턴' 카테고리의 다른 글

데코레이터 패턴(Decorator Pattern)  (0) 2022.04.29