본문 바로가기
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 어댑터 패턴

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

 

 

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

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