프록시 패턴

객체지향과 디자인 패턴(최범균 저) 프록시 패턴 정리한 내용입니다.

상황

제품 목록을 보여주는 GUI 프로그램이다. 목록 중 일부를 화면에 보여주고, 스크롤을 할 때 나머지 목록을 화면에 표시할 수 있다. 제품 목록을 구성할 때 관련된 모든 이미지를 로딩하도록 구현할 수 있는데, 이 경우 불필요하게 메모리를 사용하는 문제가 발생할 수 있다.

문제

목록 하단에 위치한 이미지는 실제로 스크롤을 하기 전까지는 화면에 보이지 않음에도 불구하고 목록을 구성할 때 메모리에 이미지 정보를 로딩하게 된다. 특히 이미지를 로컬 파일 시스템이 아닌 웹에서 읽어 온다면 이미지 로딩으로 인해 제품 목록을 보여주기까지 대기 시간이 길어지게 된다. 이를 해결하기 위해 이미지가 실제로 화면에 보여질 때 이미지 데이터를 로딩해야 한다.

해결방법

프록시 패턴은 실제 객체를 대신하는 프록시 객체를 사용해서 실제 객체의 생성이나 접근 등을 제어할 수 있도록 해 주는 패턴으로서 구조는 다음과 같다.

  • ProxyImage 클래스를 생성한다.

  • ProxyImage 클래스의 draw() 메서드는 최초로 draw() 메서드를 실행할 때 RealImage 객체를 생성하고, 그 뒤에 생성된 RealImage 객체의 draw() 메서드를 호출한다.

// Image 인터페이스를 구현한 ProxyImage 클래스를 생성한다. 
public class ProxyImage implements Image {
    private String path;
    private RealImage image;
    
    public ProxyImage(String path) {
        this.path = path;
    }
    
    public void draw() {
        if (image == null){
            image = new RealImage(path); // 최초 접근 시 객체 생성
        }
        image.draw(); // RealImage 객체에 위임
    }
}

ProxyImage 객체는 최초에 draw() 메서드가 실행될 때 RealImage 객체를 생성하기 때문에, ProxyImage 객체의 draw() 메서드가 호출되기 전에는 RealImage 객체가 생성되지 않으므로 메모리에 이미지 데이터를 로딩하지 않는다. 따라서 화면에 표시되지 않는 이미지를 로딩하기 위해 불필요하게 메모리를 낭비하는 상황을 방지할 수 있게 된다.

public class ListUI {
		private List<Image> images;
		public ListUI(List<Image> images) {
				this.images = images;
		}

		public void onScroll(int start, int end) {
				// 스크롤 시, 화면에 표시되는 이미지를 표시
				for(int i = start; i <= end; i++) {
						Image image = images.get(i);
						image.draw();
				}
		}
}

ProxyImage처럼 필요한 순간에 실제 객체를 생성해 주는 프록시를 가상 프록시라고 부르는데, 프록시에는 가상 프록시 외에 보호 프록시나 원격 프록시 등이 존재한다. 보호 프록시는 실제 객체에 대한 접근을 제어하는 프록시로서, 접근 권한이 있는 경우에만 실제 객체의 메서드를 실행하는 방식으로 구현한다.

프록시 패턴을 적용할 때 고려할 점

프록시를 구현할 때 고려할 점은 실제 객체를 누가 생성할 것이냐에 대한 것이다. 위임 방식이 아닌 상속을 사용해서 프록시를 구현할 수도 있다. 예를 들어, 특정 기능은 관리자만 실행할 수 있어야 한다고 할 경우 보호 프록시를 사용할 수 있을 것이다. 상속 방식을 사용하면 위임 방식에 비해 구조가 단순해서 구현이 비교적 쉽다. 하지만, 상속 방식의 프록시는 객체를 생성하는 순간 실제 객체가 생성되기 때문에 가상 프록시를 구현하기에는 적합하지 않다.

위임 기반의 프록시 패턴 구현은 데코레이터 패턴의 구현과 매우 유사한데, 이 두 패턴은 의도에서 분명한 차이를 보인다. 프록시 패턴의 경우 실제 객체에 대한 접근을 제어하는데 초점이 맞춰져 있는 반면에 데코레이터 패턴은 기존 객체의 기능을 확장하는데 초점을 맞추고 있다. 따라서 클래스의 이름을 부여할 때에는 의도에 맞는 단어를 선택해야 한다.

Last updated