본문 바로가기
CS/Design Pattern

[디자인 패턴] 프록시 패턴 (Proxy Pattern)

by Nahwasa 2023. 6. 18.

스터디 메인 페이지

목차

    - ☆ 표시가 붙은 부분은 스터디 중 나온 얘기 혹은 제 개인적인 생각이나 제가 이해한 방식을 적어놓은 것으로, 책에 나오지 않는 내용입니다. 따라서 책에서 말하고자 하는 바와 다를 수 있습니다. 또한 책에는 따로 Step으로 나오지 않습니다. 설명의 편의를 위해 임의로 나눈 것 입니다.

    - 모든 이미지의 출처는 헤드퍼스트 디자인패턴 개정판(한빛미디어) 입니다.

     


    프록시 패턴 (Proxy Pattern)

    코드 링크 : github

     

    GitHub - nahwasa/study-design-patterns: 헤드퍼스트 디자인패턴 스터디 진행하면서 각 패턴별 문제점이 있

    헤드퍼스트 디자인패턴 스터디 진행하면서 각 패턴별 문제점이 있는 코드부터 개선되는 코드까지 짜보기 위한 레포 - GitHub - nahwasa/study-design-patterns: 헤드퍼스트 디자인패턴 스터디 진행하면서

    github.com

     

    ☆ 프록시 패턴의 경우 종류가 다양합니다. 결국 개념은 클라이언트가 실제 객체의 메소드를 호출하면 그 호출을 중간에 가로챈다는 점이고, 그 외 프록시 패턴의 변종들은 그냥 이름 갔다 붙이기 나름인 것 같습니다.

     

     

    프록시 패턴?

    • 특정 객체로의 접근을 제어하는 대리인(특정 객체를 대변하는 객체)을 제공하는 패턴.

     

    • 프록시 패턴을 사용하면 원격 객체라든가 생성하기 힘든 객체, 보안이 중요한 객체와 같은 다른 객체로의 접근을 제어하는 대리인 객체를 만들 수 있습니다.
      • 예를들어 원격 프록시의 경우 자바 RMI(☆ 현재로썬 레거시에 RMI 사용된게 있는 경우가 아니라면 쓸 일은 없을 것 같다.) 등을 활용해 원격 객체로의 접근을 제어할 수 있습니다.
      • 가상 프록시를 써서 생성하기 힘든 자원으로의 접근을 제어할 수 있습니다.
      • 보호 프록시를 써서 접근 권한이 필요한 자원으로의 접근을 제어할 수 있습니다.
      • ☆ 그 외 캐시 프록시, 로드 밸런싱 프록시, 인증 프록시 등등 많은 것 같습니다. 뭐 어차피 프록시 패턴이 뭔지만 알면 그 이후론 결국 갔다 붙이기 나름인듯.

     

    • 이하 이미지는 원격 프록시

     

     

    가상 프록시

    • 가상 프록시는 생성하는 데 많은 비용이 드는 객체를 대신합니다. 진짜 객체가 필요한 상황이 오기 전까지 객체의 생성을 미루는 기능을 제공합니다.

     

     

    • 이하 예시로 짜본 코드 입니다.
    public class ProxyTestDrive {
    
        public static void main(String[] args) throws IOException {
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
    
            Subject subject = new Proxy();
            subject.print(new BufferedWriter(bw));
        }
    }
    
    ---
    
    public interface Subject {
    
        void print(final BufferedWriter bw) throws IOException;
    }
    
    ---
    
    public class Proxy implements Subject {
    
        volatile RealSubject realSubject;
        Thread retrievealThread;
    
        synchronized void setRealSubject(RealSubject realSubject) {
            this.realSubject = realSubject;
        }
    
        @Override
        public void print(final BufferedWriter bw) throws IOException {
            if (realSubject != null) {
                realSubject.print(bw);
                return;
            }
    
            bw.write("I'm Proxy :). wait!\n");
            bw.flush();
            retrievealThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    setRealSubject(new RealSubject());
                    try {
                        realSubject.print(bw);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            retrievealThread.start();
        }
    }
    
    ---
    
    public class RealSubject implements Subject {
    
        public RealSubject() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    
        @Override
        public void print(final BufferedWriter bw) throws IOException {
            bw.write("Real Subject!\n");
            bw.flush();
        }
    }

     

     

    프록시 패턴 변종들의 공통점

    • 원격 프록시, 가상 프록시, 보호 프록시 등등의 공통점은 '클라이언트가 실제 객체의 메소드를 호출하면 그 호출을 중간에 가로챈다는 점'

     

     

    프록시 패턴 vs 데코레이터 패턴

    • 용도로 구분 가능.
      • ☆ 같은 코드라도 개발자가 이 패턴을 사용한 용도나 목적이 다르다면 이름을 다르게 붙일 수 있을 것 같다. 어쨌든 커뮤니케이션 입장에서 보자면 용도나 목적에 따라 다른 이름을 붙이는게 충분히 의미 있을 것 같다. 
    • 데코레이터는 클래스에 새로운 행동을 추가하는 용도, 프록시는 어떤 클래스로의 접근을 제어하는 용도.

     

     

    프록시 패턴 vs 어댑터 패턴

    • 어댑터는 다른 객체의 인터페이스를 바꿔 주지만, 프록시는 똑같은 인터페이스를 사용한다는 차이점이 있음.
    • 단, 보호 프록시는 어댑터와 비슷하다고 함.

     

     

    클라이언트가 진짜 객체가 아닌 프록시 객체를 사용하도록 강제하는 법

    • 팩토리에서 프록시로 감싼 다음 리턴
    • 이 경우 클라이언트는 진짜 객체를 쓰고 있는지, 프록시 객체를 쓰고 있는지 알 수 없음.

    댓글