관리 메뉴

개발자의 스터디 노트

자바 디자인패턴 - 프록시 패턴(Proxy Pattern) 본문

자바/인스턴트코드

자바 디자인패턴 - 프록시 패턴(Proxy Pattern)

박개발씨 2022. 4. 13. 23:53

이 포스팅은

https://coding-factory.tistory.com/711

 

[Design Pattern] 프록시 패턴(Proxy Pattern)에 대하여

프록시 패턴이란? 프록시는 대리인이라는 뜻으로, 무엇인가를 대신 처리하는 의미입니다. 일종의 비서라고 생각하시면 됩니다. 사장님한테 사소한 질문을 하기보다는 비서한테 먼저 물어보는

coding-factory.tistory.com

위의 포스팅을 보고 작성하였습니다.

 

 

 

 

 

 

프록시 패턴이란?

 - 프록시는 대리인이라는 뜻으로, 무엇인가를 대신 처리하는 의미입니다. 

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

예를 들어 용량이 큰 이미지와 글이 같이 있는 문서를 모니터 화면에 띄운다고 가정하였을때 이미지 파일은 용량이 크고 텍스트는 용량이 적어서 텍스트는 빠르게 나타나지만 그림은 조금 느리게 로딩되는 것을 보신 적이 있으실 겁니다. 만약 이렇게 처리가 안되고 이미지와 텍스트가 모두 로딩이 된 후에야 화면이 나온다면 사용자는 페이지가 로딩될 때까지 의미 없이 기다려야 합니다. 그러므로 먼저 로딩이 되는 텍스트라도 먼저 나오는 게 좋습니다. 이런 방식을 취하려면 텍스트 처리용 프로세스, 그림 처리용 프로세스를 별도로 운영하면 됩니다. 이런 구조를 갖도록 설계하는 것이 바로 프록시 패턴입니다. 일반적으로 프록시는 다른 무언가와 이어지는 인터페이스의 역할을 하는 클래스를 의미합니다.

 

 

프록시가 사용되는 대표적인 3가지

 

1. 가상프록시

꼭 필요로 하는 시점까지 객체의 생성을 연기하고, 해당 객체가 생성된 것처럼 동작하도록 만들고 싶을 때 사용하는 패턴입니다. 프록시 클래스에서 자잘한 작업들을 처리하고 리소스가 많이 요구되는 작업들이 필요할 때에만 주체 클래스를 사용하도록 구현하며 위에서 예로 들었다시피 해상도가 아주 높은 이미지를 처리해야 하는 경우 작업을 분산하는 것을 예로 들 수 있겠습니다.

 

2. 원격프록시

원격 객체에 대한 접근을 제어 로컬 환경에 존재하며, 원격객체에 대한 대변자 역할을 하는 객체 서로 다른 주소 공간에 있는 객체에 대해 마치 같은 주소 공간에 있는 것처럼 동작하게 만드는 패턴입니다. 예시로 Google Docs를 들 수 있겠습니다. 브라우저는 브라우저대로 필요한 자원을 로컬에 가지고 있고 또 다른 자원은 Google 서버에 있는 형태입니다.

 

3. 보호프록시

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

 

 

프록시 패턴의 장단점

 

프록시 패턴 장점

1. 사이즈가 큰 객체(ex: 이미지)가 로딩되기 전에도 프록시를 통해 참조를 할 수 있다.

2. 실제 객체의 public, protected 메서드들을 숨기고 인터페이스를 통해 노출시킬 수 있다.

3. 로컬에 있지 않고 떨어져 있는 객체를 사용할 수 있다.

4. 원래 객체의 접근에 대해서 사전처리를 할 수 있다.

 

프록시 패턴 단점

1. 객체를 생성할 때 한 단계를 거치게 되므로, 빈번한 객체 생성이 필요한 경우 성능이 저하될 수 있다.

2. 프록시 내부에서 객체 생성을 위해 스레드가 생성, 동기화가 구현되야 하는 경우 성능이 저하될 수 있다.

3. 로직이 난해해져 가독성이 떨어질 수 있다.

 

 

프록시 패턴 예제

Image.java (interface)

public interface Image {
   void displayImage();
}

 

Real_Image.java

public class Real_Image implements Image {

    private String fileName;
    
    public Real_Image(String fileName) {
        this.fileName = fileName;
        loadFromDisk(fileName);
    }
    
    private void loadFromDisk(String fileName) {
        System.out.println("Loading " + fileName);
    }
    
    @Override
    public void displayImage() {
        System.out.println("Displaying " + fileName);
    }
}

 

Proxy_Image.java

public class Proxy_Image implements Image {
    private Real_Image realImage;
    private String fileName;
    
    public Proxy_Image(String fileName) {
        this.fileName = fileName;
    }
    
    @Override
    public void displayImage() {
        if (realImage == null) {
            realImage = new Real_Image(fileName);
        }
        realImage.displayImage();
    }
}

 

Proxy_Main.java

public class Proxy_Main {
    public static void main(String[] args) {
        Image image1 = new Proxy_Image("test1.png");
        Image image2 = new Proxy_Image("test2.png");
        
        image1.displayImage();
        System.out.println();
        image2.displayImage();
    }
}

위의 예제를 보시면 Proxy_Main에서 Real_Image에 직접 접근하지 않고 Proxy_Image 객체를 생성하여 대신시킵니다. Proxy는 displayImage() 메서드를 호출하고 그 반환 값을 Main에 반환합니다.