Design pattern/구조 패턴

[디자인 패턴] 13장 프록시 패턴

미스터로즈 2021. 5. 17. 21:56

공부하기 위해서 요약정리해놓은 것입니다..

 

정확하고 꼼꼼한 자료는 쉽게 배워 바로 써먹는 디자인 패턴을 확인하시고

코드는 github.com/infohojin/patterns 을 참고해 주세요.

 

프록시 패턴은 객체 접근을 제어하기 위해 중간 단계에 대리자를 위치시키는 패턴입니다.

 

프록시는 무슨 일을 직접 처리하지 않고 대리자를 내세워 처리를 위임합니다.

 

프록시의 특징은 하나의 객체를 두 개로 나눠 재구성한다는 것입니다. 분리하는 이유는 직접적인 접근을 막고 대리할 객체를 구현하기 위해서입니다.

 

범위에 따라 다르게 불리는 파생 프록시들도 많습니다.

 

- 원격 프록시

- 가상 프록시

- 보호 프록시

- 스마트 프록시

이 외에도 방화벽 프록시, 래퍼런스 프록시, 동기화 프록시 등의 다양한 프록시가 있습니다.

객체 분리

객체를 정교하게 제어해야 하거나 객체 참조가 필요한 경우 프록시 패턴을 사용합니다.

 

프록시는 대리인을 의미하며, 실제 객체에 직접 접근하지 않고 똑같이 동작하는 대리자를 생성합니다. 프록시는 대리자 객체를 통해 실체 객체를 가로챈 후 대리자 객체로 우회한 접근을 허용합니다.

프록시 생성

인터페이스를 이용해 프록시 객체를 생성합니다. 프록시 객체를 생성할 때 기존의 실체 객체 정보도 같이 필요합니다.

<?php
/**
 * 프록시 객체
 */
class Proxy implements Subject
{

    public function action1()
    {
        echo __METHOD__."를 호출합니다.\n";
        echo "프록시 action1 작업을 처리합니다.\n";
    }

    public function action2()
    {
        echo __METHOD__."를 호출합니다.\n";
        echo "프록시 action2 작업을 처리합니다.\n";
    }
}

인터페이스에 정의된 규약에 맞게 프록시를 설계합니다.

<?php

class Proxy implements Subject
{
    private $_object;

    public function __construct($real)
    {
        echo __CLASS__." 객체가 생성이 되었습니다.\n\n";
        $this->_object = $real;
    }

    public function action1()
    {
        echo __METHOD__."를 호출합니다.\n";
        $this->_object->action1();
    }

    public function action2()
    {
        echo __METHOD__."를 호출합니다.\n";
        $this->_object->action2();
    }
}

프록시 객체의 생성자를 통해 원본 객체 정보를 전달 받은 후, 이 객체 정보를 내부 프로퍼티에 저장합니다.

 

프록시의 객체는 실체 객체와 동일한 동작을 수행합니다. 프록시는 요청된 동작을 실체 객체로 위임함으로써 동일한 결과를 반환하게 됩니다.

 

실체 객체와 동일한 동작을 그대로 대신하는 것을 투과적 특성이라고 합니다.

 

핸들러는 투과적 특성을 이용하여 요청된 행위를 처리하는 프록시 동작을 말합니다.

간접 통로는 프록시의 투과적 틍성을 이용하여 객체의 행위를 위임하고 처리를 요청합니다.

 

두 객체의 동일성을 유지하기 위해 같은 인터페이스를 유지하는 것이 중요합니다. 인터페이스에 맞게 프록시 객체에도 변경된 메서드를 생성해야 합니다. 이처럼 프록시 객체의 내부 설계를 동일하게 유지하는 작업은 매우 번거롭습니다.

 

<?php

class Proxy implements Subject
{
    private $_object;

    public function __construct($real)
    {
        echo __CLASS__." 객체가 생성이 되었습니다.\n\n";
        $this->_object = $real;
    }

    public function action1()
    {
        echo __METHOD__."를 호출합니다.\n";
        $this->_object->action1();
    }

    public function action2()
    {
        echo __METHOD__."를 호출합니다.\n";
        $this->_object->action2();
    }

    public function __call($method, $args)
    {
        if(method_exists($this->_object, $method)) {
            $this->_object->$method($args);
        } else {
            print $method."는 실제 존재 하는 매소드가 아닙니다.\n";
            var_dump($args);
        } 
    }


}

__call() 메서드는 객체 내부에 존재하지 않는 메서드가 호출될 때 자동 실행되는 매직 메서드입니다.

 

동적 프록시

프록시에는 실체 객체를 숨기는 효과가 있습니다. 객체를 호출하는 쪽에서는 실행되는 객체가 실체 객체인지 프록시인지 몰라야 합니다.

 

프록시 객체를 생성할지 실체 객체를 생성할지 판단하지 않고 객체를 생성해서 사용할 수 있어야 합니다.

팩토리 패턴을 같이 사용해 객체를 동적으로 생성하는 것입니다. 이처럼 패턴은 하나만 사용하지 않으며, 다른 패턴과 결합해 문제를 보다 유연하게 해결합니다.

 

<?php
/**
 * 프록시 팩토리
 */
class ProxyFactory
{
    public function getObject()
    {
        $real = new RealSubject;
        return new Proxy($real);
    }
}

원격 프록시

원격 프록시는 프록시 패턴을 가장 많이 응용하는 적용 사례이며 주로 데이터 전달을 목적으로 사용합니다.

 

프록시와 어댑터는 두 개의 객체를 이어준다는 역활적인 측면에서 두 패턴은 서로 유사합니다.

어댑터 패턴은 서로 다른 인터페이스를 맞춰주는 반면, 프록시는 투과적 특성으로 동일한 인터페이스를 유지합니다.

 

웹 HTTP에서 프록시라는 단어를 많이 들어봤을 것입니다. 프록시는 HTTP에서 웹페이지를 캐시 처리함으로써 속도를 개선하고 우회 접속을 시도합니다.

<?php

class Proxy implements Subject
{
    private $_object;

    public function __construct($real)
    {
        echo __CLASS__." 객체가 생성이 되었습니다.\n\n";
        $this->_object = $real;
    }

    public function action1()
    {
        echo __METHOD__."를 호출합니다.\n";
        return "action1 은 기능이 변경되었습니다.\n";
    }

    public function action2()
    {
        echo __METHOD__."를 호출합니다.\n";
        if($msg = $this->_object->action2()) {
            return $msg;
        } else {
            return "실제 객체에서 문자열을 반환 받지 못하였습니다.";
        }
    }

    public function __call($method, $args)
    {
        if(method_exists($this->_object, $method)) {
            if($msg = $this->_object->$method($args)) {
                return $msg;
            } else {
                return "실제 객체에서 문자열을 반환 받지 못하였습니다.";
            }

        } else {
            print $method."는 실제 존재 하는 매소드가 아닙니다.\n";
            var_dump($args);
        } 
    }

}

 

가상 프록시

가상 프록시는 프로그램의 실행 속도를 개선하기 위한 패턴입니다.

 

프로그램은 수많은 객체를 생성하고, 생성된 객체에 접근합니다. 프로그램은 내부에서 필요한 객체를 미리 생성하는 작업을 하기도 합니다. 이를 부트스트래핑 과정이라고 합니다.

 

가상 프록시 패턴은 실제로 동작하는 객체를 생성하지 않고, 필요 시점에서 객체를 동적으로 생성하도록 프록시 객체로 대신 연결합니다. 게으른 초기화와 유사합니다.

 

사용 빈도가 낮은 객체 생성까지 초기화 과정에서 실행됨으로써 불필요한 지연시간이 발생하기도 합니다. 게으른 초기화는 이러한 단점을 보완하기 위해 가상 프록시를 사용합니다.