ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 프록시 패턴(Proxy Pattern)이란?
    Design Pattern/구조 디자인 패턴 2024. 4. 13. 22:41
    반응형

     프록시 패턴은 구조적 디자인 패턴 중 하나로, 다른 객체에 대한 접근을 제어하거나 그 기능을 확장하는 데 사용됩니다. 프록시는 실제 객체와 같은 인터페이스를 구현하여 클라이언트로부터의 요청을 실제 객체로 전달하기 전에 처리합니다. 이 패턴은 실제 객체를 직접 참조하는 대신 프록시를 통해 상호작용하는 방식으로, 접근 제어, 비용 절감, 네트워크 최적화 등을 목적으로 사용됩니다.

    1. 프록시 패턴은 주로 어디에 쓰이나?

    원격 프록시 (Remote Proxy)

    • 예시: 원격 서버에 저장된 대용량 비디오 파일을 재생할 때
    • 설명: 사용자가 비디오를 재생하려고 할 때, 모든 데이터를 한 번에 다운로드하지 않고 필요한 부분만 불러와 재생합니다. 이처럼 원격 프록시는 네트워크를 통해 다른 서버에 있는 객체(여기서는 비디오 파일)를 마치 로컬에서 접근하듯이 사용할 수 있도록 해줍니다.

    가상 프록시 (Virtual Proxy)

    • 예시: 유튜브에서 비디오 썸네일 미리 보기
    • 설명: 유튜브는 수많은 비디오 콘텐츠를 갖고 있습니다. 모든 비디오를 한 번에 불러오는 것은 매우 비효율적입니다. 유튜브는 사용자가 실제로 비디오를 클릭하기 전까지는 비디오의 미리 보기 이미지만을 보여줍니다. 이런 방식으로 가상 프록시는 실제 객체(비디오)의 로딩을 사용자가 필요로 할 때까지 지연시킵니다.

    보호 프록시 (Protection Proxy)

    • 예시: 기업 네트워크에서 문서 접근 제어
    • 설명: 회사에서 중요한 문서에 접근할 때, 사용자의 권한을 확인하고 적절한 권한이 있는 사용자만 문서를 볼 수 있도록 합니다. 보호 프록시는 이처럼 객체에 대한 접근을 제어하는 역할을 합니다.

    캐싱 프록시 (Caching Proxy)

    • 예시: 웹 브라우저의 캐시 기능
    • 설명: 웹 페이지를 처음 방문할 때, 페이지의 내용을 로컬에 저장합니다. 다음에 같은 페이지를 방문할 때는 서버에 요청하지 않고 로컬의 캐시된 데이터를 사용하여 빠르게 페이지를 불러옵니다. 캐싱 프록시는 이처럼 데이터를 임시로 저장해 두었다가 빠르게 제공하는 역할을 합니다.

    2. Java와 Go로 프록시 패턴을 구현할 때의 차이점

    • Java: 자바에서는 인터페이스를 통해 프록시를 구현하며, java.lang.reflect.Proxy 클래스를 활용하여 동적 프록시를 생성할 수 있습니다.
    • Go: Go에서는 인터페이스를 묵시적으로 구현함으로써 프록시 패턴을 적용할 수 있습니다. Go의 단순성과 유연성으로 인해, 구조체를 통해 직접 메서드를 재정의하는 방식이 일반적입니다.

    3. 프록시 패턴 예시

    Java 예시:

    interface Image {
        void display();
    }
    
    class RealImage implements Image {
        private String fileName;
    
        public RealImage(String fileName) {
            this.fileName = fileName;
            loadFromDisk(fileName);
        }
    
        private void loadFromDisk(String fileName) {
            System.out.println("Loading " + fileName);
        }
    
        @Override
        public void display() {
            System.out.println("Displaying " + fileName);
        }
    }
    
    class ProxyImage implements Image {
        private RealImage realImage;
        private String fileName;
    
        public ProxyImage(String fileName) {
            this.fileName = fileName;
        }
    
        @Override
        public void display() {
            if(realImage == null) {
                realImage = new RealImage(fileName);
            }
            realImage.display();
        }
    }
    
    public class ProxyDemo {
        public static void main(String[] args) {
            Image image = new ProxyImage("test_image.jpg");
            image.display();
            image.display();
        }
    }

    Java 예시에서는 Image 인터페이스와 이를 구현하는 두 가지 클래스 RealImage와 ProxyImage를 사용하여 프록시 패턴을 구현합니다.

    • Image 인터페이스: 이 인터페이스는 display 메서드를 정의하여, 이를 구현하는 모든 클래스가 이미지를 표시하는 방법을 명시해야 함을 나타냅니다.
    • RealImage 클래스: 이 클래스는 Image 인터페이스를 구현합니다. 생성자에서 이미지 파일의 이름을 받고, loadFromDisk 메서드를 통해 디스크에서 이미지를 불러옵니다. display 메서드는 이미지의 표시 로직을 담당합니다.
    • ProxyImage 클래스: 이 클래스 또한 Image 인터페이스를 구현하지만, 실제 RealImage 객체의 생성을 사용자가 display 메서드를 호출할 때까지 지연시킵니다. 이는 메모리 사용을 최적화하고, 필요한 시점에만 리소스를 로드하는 데 유용합니다.

    클라이언트 코드(ProxyDemo 클래스)는 ProxyImage 객체를 생성하고 display 메서드를 호출합니다. 첫 호출에서는 RealImage 객체가 생성되고 이미지가 로드되며, 두 번째 호출에서는 이미 로드된 이미지를 재사용합니다.

     

     

    Go 예시:

    type Image interface {
        Display()
    }
    
    type RealImage struct {
        FileName string
    }
    
    func (r *RealImage) Display() {
        fmt.Println("Displaying " + r.FileName)
    }
    
    func NewRealImage(fileName string) *RealImage {
        fmt.Println("Loading " + fileName)
        return &RealImage{FileName: fileName}
    }
    
    type ProxyImage struct {
        realImage *RealImage
        fileName  string
    }
    
    func (p *ProxyImage) Display() {
        if p.realImage == nil {
            p.realImage = NewRealImage(p.fileName)
        }
        p.realImage.Display()
    }
    
    func main() {
        image := &ProxyImage{fileName: "test_image.jpg"}
        image.Display()
        image.Display()
    }

    Go에서의 프록시 패턴 구현은 Java와 비슷한 논리를 따릅니다. Go는 인터페이스를 묵시적으로 구현하기 때문에 Java보다 코드가 좀 더 간결합니다.

    • Image 인터페이스: Display 메서드를 정의하여 구현체가 이 메서드를 구현하도록 요구합니다.
    • RealImage 구조체: Display 메서드를 구현하며, 파일 이름을 바탕으로 이미지를 표시합니다.
    • ProxyImage 구조체: RealImage 포인터를 내부에 가지고 있으며, Display 메서드에서 RealImage 객체의 생성을 지연시킵니다. Display가 처음 호출될 때 RealImage를 생성하고, 이후 호출에서는 이미 생성된 객체를 사용합니다.

    이 예시에서는 main 함수에서 ProxyImage 객체를 생성하고, Display 메서드를 두 번 호출하여 프록시의 동작을 보여줍니다. 첫 번째 호출에서 이미지를 로드하고, 두 번째 호출에서는 로드된 이미지를 재사용하는 것을 확인할 수 있습니다.

     

    프록시 패턴의 적용

    이러한 방식은 프로그램에서 리소스의 관리를 최적화하고, 불필요한 연산을 줄이며, 실제 객체에 대한 접근을 제어하는 데 유용하게 사용됩니다. 특히 대규모 시스템이나 리소스 사용이 중요한 애플리케이션에서 프록시 패턴은 매우 효과적인 설계 기법입니다.

     

    4. 프록시 패턴의 장단점

    장점:

    • 실제 객체의 수명 주기를 효율적으로 관리할 수 있습니다.
    • 실제 객체의 메서드 호출을 가로채어 추가적인 기능을 제공할 수 있습니다.
    • 클라이언트와 실제 객체 사이의 결합도를 낮춥니다.

    단점:

    • 코드의 복잡성이 증가할 수 있습니다.
    • 프록시 객체를 통한 요청 처리는 때로는 성능 저하를 초래할 수 있습니다.
    반응형
Designed by Tistory.