프로그래밍 이론/디자인 패턴

12. [Design Pattern] 프록시(Proxy) 패턴

NewtronVania 2024. 1. 6. 00:36

프록시 패턴

프록시 패턴은 다른 객체의 기능을 대리하거나 접근을 제어하는 디자인 패턴이다. 패턴은 클라이언트가 실제 객체(RealSubject) 직접 호출하지 않고, 대신 프록시(Proxy) 객체를 통해 요청을 중계한다. 이를 통해 RealSubject 대한 접근을 제어하거나 추가적인 기능을 제공할 있다.


구조

  • Subject - RealSubject Proxy 구현해야 메소드를 정의한다. 클라이언트는 인터페이스를 통해 RealSubject 또는 Proxy 상호 작용한다.
  • RealSubject - 실제로 서비스를 제공하는 클래스. Proxy 클래스의 인스턴스에 대한 요청을 중계한다.
  • Proxy -RealSubject 클래스의 기능을 대리하는 역할. Proxy RealSubject 인스턴스를 내부적으로 유지하며, 클라이언트로부터 요청을 받으면 이를 RealSubject 전달하기 전에 필요한 처리(: 접근 제어, 지연 로딩 ) 수행한다.
  • Client -Subject 인터페이스를 통해 RealSubject 또는 Proxy 상호 작용한다. 클라이언트는 실제로 어떤 객체(RealSubject 또는 Proxy) 상호 작용하는지 알지 못한다.

기본 인터페이스

// Subject 인터페이스 정의
public interface ISubject
{
    void Request();
}

// RealSubject 클래스 정의
public class RealSubject : ISubject
{
    public void Request()
    {
        Console.WriteLine("RealSubject: Handling Request.");
    }
}

// Proxy 클래스 정의
public class Proxy : ISubject
{
    private RealSubject _realSubject;

    public Proxy(RealSubject realSubject)
    {
        _realSubject = realSubject;
    }

    public void Request()
    {
        if (CheckAccess())
        {
            _realSubject.Request();
            LogAccess();
        }
    }

    private bool CheckAccess()
    {
        // 여기서는 단순히 접근 권한이 있다고 가정합니다.
        // 실제 애플리케이션에서는 접근 권한을 체크하는 로직이 들어갈 수 있습니다.
        Console.WriteLine("Proxy: Checking access prior to firing a real request.");
        return true;
    }

    private void LogAccess()
    {
        Console.WriteLine("Proxy: Logging the time of request.");
    }
}

// Main 클래스
class Client
{
    static void Main(string[] args)
    {
        Console.WriteLine("Client: Executing the client code with a real subject:");
        RealSubject realSubject = new RealSubject();
        realSubject.Request();

        Console.WriteLine();

        Console.WriteLine("Client: Executing the same client code with a proxy:");
        Proxy proxy = new Proxy(realSubject);
        proxy.Request();
    }
}

 

Client: Executing the client code with a real subject:
RealSubject: Handling Request.

Client: Executing the same client code with a proxy:
Proxy: Checking access prior to firing a real request.
RealSubject: Handling Request.
Proxy: Logging the time of request.

왜 사용해야 할까?

프록시(Proxy)란 단어는 대리자, 대리인, 대용물 등 무엇인가를 대체한다는 뜻이다. 프록시 패턴은 이름에 맞게 원본 객체를 그대로 사용하지 않고 대신할 대리자를 만들어 원본을 숨기는 걸 목표로 한다. 

또한 프록시 패턴은 민감한 정보를 다루는 객체에 대한 접근을 제한할 수 있다. 프록시를 사용하여 사용자의 권한을 확인하거나 객체에 대한 요청을 필터링하거나 검증할 수 있으며, 이는 잘못된 요청으로 인한 오류를 줄이는 데 도움이 된다.

public class SecurityProxy : ISubject
{
    private RealSubject _realSubject;

    public SecurityProxy(RealSubject realSubject)
    {
        _realSubject = realSubject;
    }

    public void Request()
    {
        //사용자의 제어 가능 여부를 체크
        if (CheckAccess())
        {
            _realSubject.Request();
        }
        else
        {
            Console.WriteLine("Access Denied");
        }
    }

    private bool CheckAccess()
    {
        // 권한 혹은 접근에 대한 제어
    }
}

 

또한 기능 추가를 통해 기존 객체를 수정하지 않고도 새로운 기능을 도입할 있다. 프록시 패턴은 원본 객체와 집합 관계를 가지며, 이를 통해 원본 객체의 기능을 사용하되, 추가 기능을 실행하고 클라이언트에게 제공할 수 있다. 예를 들어, 객체의 메서드 호출 전후에 로깅을 수행하는 프록시를 사용하여 디버깅과 모니터링을 간소화할 있다.

public class LoggingProxy : ISubject
{
    private RealSubject _realSubject;

    public LoggingProxy(RealSubject realSubject)
    {
        _realSubject = realSubject;
    }

    public void Request()
    {
        LogRequest();
        _realSubject.Request();
        LogResponse();
    }

    private void LogRequest()
    {
        // 요청 로깅
    }

    private void LogResponse()
    {
        // 응답 로깅
    }
}

장점

  1. 클라이언트는 Proxy 통해 RealSubject 사용하는 것이지만, 이를 인식하지 못하도록 숨긴다.
  2. 클라이언트가 RealSubject에 직접 접근하는 것을 방지하고, 접근 제어를 통해 보안을 강화할 수 있다.
  3. 기존 클래스를 수정하지 않고 추가 기능을 부여할 수 있다.
  4. RealSubject 생성을 지연시켜 리소스를 절약하고, 성능을 향상시킬 있다.

단점

  1. 프록시 클래스를 추가함으로써 코드의 복잡성이 증가할 있다.
  2. Proxy 거치는 과정에서 약간의 시간 지연이 발생할 있다.

 

참고자료

 

GoF의 디자인 패턴 - 예스24

이 책은 디자인 패턴을 다룬 이론서로 디자인 패턴의 기초적이고 전반적인 내용을 학습할 수 있다.

www.yes24.com