Computer >> 컴퓨터 >  >> 프로그래밍 >> C#

프록시(Proxy) 디자인 패턴이란? 개념부터 C# 구현 방법까지


프록시(Proxy) 패턴은 실제 객체에 대한 접근을 제어하기 위해 대리자(surrogate) 또는 플레이스홀더 역할을 하는 객체를 제공하는 구조적 디자인 패턴입니다. 클라이언트는 프록시 객체를 원본 객체와 동일한 방식으로 사용할 수 있기 때문에, 내부 구현을 몰라도 투명하게 객체를 다룰 수 있습니다.

패턴의 구성 요소(Participants)

1. Subject (주체)

RealSubject와 Proxy가 공통으로 따르는 인터페이스를 정의합니다. 이 덕분에 RealSubject가 필요한 모든 위치에서 Proxy를 자유롭게 대체하여 사용할 수 있습니다.

2. RealSubject (실제 주체)

프록시가 대표하는 실제 구체 객체입니다. 실질적인 비즈니스 로직을 수행하는 핵심 클래스라고 볼 수 있습니다.

3. Proxy (프록시)

RealSubject에 대한 참조를 유지하고 그 접근을 제어합니다. 두 객체를 상호 교환 가능하게 만들기 위해 RealSubject와 동일한 인터페이스를 반드시 구현해야 합니다.

프록시 패턴은 언제 사용할까?

기존 객체의 정의를 변경하지 않으면서 객체의 동작을 확장하거나 수정해야 할 때 프록시 패턴이 빛을 발합니다. 특히 테스트 환경에서 유용한데, 클래스를 완전히 구현하지 않고도 해당 클래스의 동작만 재현해야 하는 경우에 활용할 수 있습니다. 이 외에도 지연 로딩(Lazy Loading), 접근 권한 검사, 로깅, 캐싱 등 다양한 목적으로 널리 사용됩니다.

C# 구현 예제

다음 예제는 레스토랑의 서버(ServerP)와 이를 감싸는 프록시(NewServerProxy)를 통해 프록시 패턴을 보여줍니다. 클라이언트(Program)는 공통 인터페이스(IServerP)에만 의존하기 때문에 실제 서버든 프록시든 동일하게 호출할 수 있습니다.

internal class Program {
    private static void Main(string[] args) {

        NewServerProxy proxy = new NewServerProxy();
        Console.WriteLine("What would you like to order? ");
        string order = Console.ReadLine();
        proxy.TakeOrder(order);

        Console.WriteLine("Sure thing! Here's your " + proxy.DeliverOrder() + ".");
        Console.WriteLine("How would you like to pay?");
        string payment = Console.ReadLine();
        proxy.Processpayment(payment);

        Console.ReadKey();
    }
}

public interface IServerP {
    void TakeOrder(string order);
    string DeliverOrder();
    void Processpayment(string payment);
}
public class ServerP : IServerP {
    private string Order;
    public string DeliverOrder() {
        return Order;
    }

    public void Processpayment(string payment){
        Console.WriteLine("Server Processes the payment " + payment);
    }
    public void TakeOrder(string order) {
        Console.WriteLine("Server takes order " + order);
        Order = order;
    }
}
public class NewServerProxy : IServerP {
    private string Order;
    ServerP _server = new ServerP();
    public string DeliverOrder() {
        return Order;
    }
    public void Processpayment(string payment) {
        _server.Processpayment(payment);
    }
    public void TakeOrder(string order) {
        Console.WriteLine("Server takes order " + order);
        Order = order;
    }
}

예제 코드 해설

IServerP는 Subject 역할을 하며 주문 접수(TakeOrder), 음식 전달(DeliverOrder), 결제 처리(Processpayment) 세 가지 기능을 정의합니다. ServerP는 실제 주문과 결제를 처리하는 RealSubject이며, NewServerProxy는 동일한 인터페이스를 구현하면서 내부적으로 ServerP 인스턴스에 작업을 위임합니다. 클라이언트 입장에서는 어느 객체를 사용하든 동일하게 동작하기 때문에, 나중에 로깅이나 캐싱 같은 부가 기능을 프록시에 추가하더라도 클라이언트 코드는 전혀 수정할 필요가 없습니다.

마무리

프록시 패턴은 접근 제어라는 명확한 목적을 가진 단순하면서도 강력한 패턴입니다. 원본 코드를 수정하지 않고도 기능을 추가하거나 동작을 제어할 수 있다는 점에서 유지보수성과 테스트 용이성을 크게 높여 줍니다. 데코레이터(Decorator) 패턴과 구조가 유사하지만, 프록시는 객체 자체를 장식하기보다 객체에 대한 '접근'을 제어한다는 점에서 차별화됩니다.