본문 바로가기

JAVA

proxy 패턴

프록시 패턴이란?

실제 기능을 수행하는 객체 대신 가상의 객체를 사용해 로직의 흐름을 제어하는 디자인 패턴

프록시 패턴의 특징

  • 원래 하려던 기능을 수행하며 그외의 부가적인 작업(로깅, 인증, 네트워크 통신 등)을 수행하기에 좋음
  • 비용이 많이 드는 연산(DB 쿼리, 대용량 텍스트 파일 등)을 실제로 필요한 시점에 수행 가능
  • 프록시 객체와 실제 객체의 사용법이 유사하므로 사용성이 좋음

1) 가상 프록시

가상 프록시는 실제 객체의 사용 시점을 제어할 수 있습니다.

ex> 문서를 디코딩하는 프로그램

 

//인터페이스
interface TextFile {
    String fetch();
}

 

 

//구현체
class SecretTextFile implements TextFile {
    private String plainText;

    public SecretTextFile(String fileName) {
        this.plainText = SecretFileHolder.decodeByFileName(fileName);
    }

    @Override
    public String fetch() {
        return plainText;
    }
}

 

20개의 파일을 디코딩 해야 하는 프로그램이 있다고 가정하겠습니다. SecretTextFile 메서드는 암호화 된 파일을 디코딩하는 기능입니다.SecretTextFile 클래스에서 사용중인 SecretFileHolder.decodeByFileName() 메서드의 수행속도는 0.3초라고 가정합니다.

목록에 20개의 파일 내용을 노출해야 하는 상태였기 때문에 수행속도가 느리게 되는 문제점이 발생합니다.
화면을 구성할 때 이 SecretTextFile들을 전부 객체로 만들다 보니 6초 정도의 로딩 시간을 갖게 된 것입니다.

 

 

//프록시 패턴
class ProxyTextFile implements TextFile {
    private String fileName;
    private TextFile textFile;

    public ProxyTextFile(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public String fetch() {
        if (textFile == null) {
            textFile = new SecretTextFile(fileName);
        }
        return "[proxy] " + textFile.fetch();
    }
}

 

ProxyTextFile 같은 프록시 클래스를 만들고 기존SecretTextFile클래스 대신 사용한것만으로도 초기 객체 생성 시간을 감소시킬 수 있습니다. 이렇게 초기 비용이 많이 드는 연산이 포함된 객체의 경우 가상 프록시를 사용했을 때 효과를 볼 수 있습니다.

 

 

 

2) 보호 프록시

보호 프록시는 프록시 객체가 사용자의 실제 객체에 대한 접근을 제어합니다.

ex> 사원정보 조회 프로그램

 

// 직책 등급(차례대로 직원, 매니저, 사장)
enum GRADE {
    Staff, Manager, President
}

// 구성원
interface Employee {
    String getName(); // 이름 조회
    GRADE getGrade(); // 직책 조회
    String getInformation(Employee viewer); // 사원정보 조회
}

// 일반 구성원
class NormalEmployee implements Employee {
    private String name;
    private GRADE grade;

    public NormalEmployee(String name, GRADE grade) {
        this.name = name;
        this.grade = grade;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public GRADE getGrade() {
        return grade;
    }

    @Override
    public String getInformation(Employee viewer) {
        return "Display " + getGrade().name() + " '" + getName() + "' personnel information.";
    }
}

 

해당 사원정보 프로그램에서 정보 접근 권한을 제한하려고 합니다.

현재 상태에서는 모든 사람이 타인의 정보를 조회할 수 있습니다. 상위 직책이거나 본인만 사원정보를 조회할 수 있도록 하는 기능을 추가합니다.

 

만약 해당 코드를 본인이 수정할 수 있다면 기능을 추가하는 일은 쉽지만, 라이브러리와 같이 내부 코드를 수정할 수 없을 경우에 프록시 패턴을 사용한다면 유용합니다.

 

//보호 프록시 패턴 적용
class ProtectedEmployee implements Employee {
    private Employee employee;

    public ProtectedEmployee(Employee employee) {
        this.employee = employee;
    }

    @Override
    public String getInformation(Employee viewer) {
        // 본인 인사정보 조회
        if (this.employee.getGrade() == viewer.getGrade() 
        	&& this.employee.getName().equals(viewer.getName())) {
            	return this.employee.getInformation(viewer);
        }

        switch (viewer.getGrade()) {
            case President:
            	// 사장은 매니저, 스탭들을 볼 수 있다.
                if (this.employee.getGrade() == GRADE.Manager || this.employee.getGrade() == GRADE.Staff) {
                    return this.employee.getInformation(viewer);
                }
            case Manager:
                if (this.employee.getGrade() == GRADE.Staff) { // 매니저는 스탭들을 볼 수 있다.
                    return this.employee.getInformation(viewer);
                }
            case Staff:
            default:
                throw new RuntimeException(); // 스탭들은 다른 사람의 인사정보를 볼 수 없다.
        }
    }

    @Override
    public String getName() {
        return employee.getName();
    }

    @Override
    public GRADE getGrade() {
        return employee.getGrade();
    }
}

 

ProtectedEmployee 같은 프록시 클래스를 만들고 기존 NormarEmployee 클래스 대신 사용함으로써 기존 코드의 수정 없이 객체에 대한 접근을 제어하는 기능을 추가할 수 있습니다.

 

 

 

더보기

그 외 패턴

- 원격 프록시 : 원격 객체에 대한 접근 제어가 가능합니다.

- 방화벽 프록시 : 일련의 네트워크 자원에 대한 접근을 제어함으로써 주 객체를 '나쁜' 클라이언트들로부터 보호하는 역할을 합니다.

- 스마트 레퍼런스 프록시 (Smart Reference Proxy) : 주 객체가 참조될 때마다 추가 행동을 제공합니다. ex) 객체 참조에 대한 선 작업, 후 작업 등

- 캐싱 프록시 (Caching Proxy) : 비용이 많이 드는 작업의 결과를 임시로 저장 하고, 추후 여러 클라이언트에 저장된 결과를 실제 작업처리 대신 보여주고 자원을 절약하는 역할을 합니다.

- 동기화 프록시 (Synchronization Proxy) : 여러 스레드에서 주 객체에 접근하는 경우에 안전하게 작업을 처리할 수 있게 해줍니다. 주로 분산 환경에서 일련의 객체에 대한 동기화 된 접근을 제어해주는 자바 스페이스에서 쓰입니다.

- 복잡도 숨김 프록시 (Complexity Hiding Proxy) : 복잡한 클래스들의 집합에 대한 접근을 제어하고, 복잡도를 숨깁니다. 

- 지연 복사 프록시 (Copy-On-Write Proxy) : 클라이언트에서 필요로 할 때까지 객체가 복사되는 것을 지연시킴으로써 객체의 복사를 제어합니다. '변형된 가상 프록시'라고 할 수 있으며,

Java 5 의 CopyOnWriteArrayList 에서 쓰입니다.

 

 

Reference

https://jdm.kr/blog/235

'JAVA' 카테고리의 다른 글

DAO와 repository의 차이  (0) 2021.09.14
Proxy 객체  (0) 2021.09.07
디자인 패턴  (0) 2021.08.06
Object 클래스 - 메서드  (0) 2021.08.05
Constant Pool  (0) 2021.08.05