본문 바로가기
개발 일지/디자인패턴 & 아키텍쳐

프록시 패턴(Proxy Pattern)

by osul_world 2021. 12. 30.
728x90

프록시 패턴


Proxy

무언가를 대신하여 처리하는 중간자 혹은 비서 및 대리인

  1. 프록시는 다른 객체를 대신하여 클라이언트의 요청을 받아 대신 해당 객체에게 요청을 전달하여 주는 객체
  2. 프록시는 그것이 대신하는 객체와 동일한 인터페이스를 가진다.
  3. 원격 프록시를 제외하고는 장식 패턴처럼 is-a와 has-a를 동시에 사용하는 형태이다.
  4. 프록시가 원 객체를 대신하는 이유에 따라 프록시의 종류를 구분한다.

원격프록시,보호프록시 등

 

프록시에 대한 기본을 보고 와야한다.

출처: [https://coding-factory.tistory.com/711]

 

프록시는 역할에 따라 여러 종류로 구분될수있다.

원격(remote) 프록시: 원격에 있는 객체를 대신할 지역 객체

가상 프록시: 생성 비용이 비싼 객체를 대신할 객체, 보통 생성을 필요할 때까지 지연하거나 생성에 소요되는 시간이 길면 그 과정에서 대신 프록시를 사용함

보호 프록시: 원 객체에 대한 접근 제어를 제공하는 객체, 기존 언어에서 제공하는 접근 제어로 제어할 수 없는 것을 제공함

JAVA: private, public, protected와 같은 접근 제어를 통해 제어할 수 없는 접근 제어를 제공하여 줌

스마트 프록시: 원래 객체에 대한 접근하기 전에 필요한 사전 처리(동시 접근 제한, 접근 수 통계 처리, 로그)를 해주는 객체, 요청 결과를 cache하는 기능을 프록시가 제공할 수 있음

 

image

 

프록시가 보통 대상 객체 interface만 알면 되지만 객체 생성까지 담당해야 하는 경우에는 구체적 대상 객체와 직접적 의존 관계를 가지게 됨.

즉, 일반적으로는 구체적 대상 객체와 의존 관계 없이 프록시 기능을 제공할 수 있음

 

가상프록시

두가지 기능적 특성이 있다.

1. 꼭 필요로 하는 시점까지 객체의 생성을 연기하고, 해당 객체가 생성된 것처럼 동작하도록 만들고 싶을때 사용하는 패턴

2. 프록시 클래스에서 자잘한 작업들을 처리하고 리소스가 많이 요구되는 작업들이 필요할 때에만 주체 클래스를 사용하도록 구현

  1. 주체 객체가 무거워 생성이 느린경우 생성이 완료될때까지 가상프록시 객체가 클라이언트에게 임시적인 응답을 주고 생성시간을 벌어줌
  2. image

 

2번 예제의 경우 구현을 살펴보자.

 

Book 객체는 사용자가 활용하는 객체이다.

//사용자가 접근하는 객체
public class Book{ 
    private Content content; //프록시 객체 유지
    
    public Book(ISBN isbn){
    	content = new ContentProxy(isbn); //프록시 객체 주입
    }
    
    public void viewContents(){
    	content.show(); //ContentProxy 의 show() 실행
    }
}
//testCode(Client)
public class Client {
    public static void main(String[] args) {
    	Book book = new Book("123-45"); //생성시 프록시 객체를 유지하고있다.	
        book.viewContents(); //무거운 작업 요청시 프록시 객체가 진짜 객체 접근
	}
}
//HeavyBook: 책에 대한 모든 정보를 유지하는 진짜 객체, 프록시객체가 접근시 무거운 정보 제공
public class Content{
    public List<Chapter> chapters;
    
    public Content(ISBN isbn){
    	chapters = DB.getContentsFromDB(isbn);
    }
    
    public void show(){
    	for(var chapter: chapters)
    		chapter.show(); //진짜 객체가 가지고잇는 heavy한 내용 송출
    }
}
//LightBook: 책에 대한 가벼운 정보만 유지, 무거운 정보는 HeavyBook 객체를 이용
public class ContentProxy{ 
    private ISBN isbn;
    private Content content; //HeavyBook has-a
    
    public ContentProxy(ISBN isbn){
    	this.isbn = isbn;
    }
    
    public void show(){
        if(content == null) 
        	content = new Content(isbn); //HeavyBook 진짜 객체 주입 이전까지는 null로 유지 (메모리 아낄수있음)
    
        content.show(); //진짜 객체의 show 호출, for 진짜 객체가 가지고잇는 heavy한 내용
    }
}

클라이언트가 Book객체를 생성하면 진짜 객체가 아닌 프록시 객체를 우선적으로 가지게 된다.

프록시 객체는 가벼운 내용을 가지고있기때문에 간단한 요청은 프록시가 처리하게 되며 진짜 객체를 호출할 필요가 없어 메모리를 아낄수있다.

클라이언트가 Book의 viewContents() 즉, 무거운 리소스 접근을 요청하면 그때 프록시객체는 진짜 객체에 접근해 해당 리소스를 가져온다.

 

원격프록시

원격 객체에 대한 접근을 제어 로컬 환경에 존재하며, 원격객체에 대한 대변자 역할을 하는 객체 서로다른 주소 공간에 있는 객체에 대해 마치 같은 주소 공간에 있는 것처럼 동작하게 만드는 패턴

ex) Google Docs

브라우저는 브라우저대로 필요한 자원을 로컬에 가지고 있고 또다른 자원은 Google 서버에 있는 형태

원격 프록시는 객체가 로컬이 아닌 원격에 존재해도 로컬에 있는것과 동일하게 접근하도록 해준다.

 

통신에 필요한 코드는 원격 프록시에 정의하여 로컬 코드에 통신을 위한 코드를 작성할 필요가 없어

로컬과 원격의 객체 코드가 동일하다.

 

보호프록시

주체 클래스에 대한 접근을 제어하기 위한 경우에 객체에 대한 접근 권한을 제어하거나 객체마다 접근 권한을 달리하고 싶을때 사용하는 패턴으로 프록시 클래스에서 클라이언트가 주체 클래스에 대한 접근을 허용할지 말지 결정하도록 할수가 있다.

자바의 private protected와 같은 해당 언어가 제공하는 기능으로 제어를 완전히 할수없는경우 사용한다.

보호 프록시의 구현은 직접할수도있고 자바가 제공하는 동적 프록시를 활용할수도있다.

동적 프록시는 자바 Reflection을 사용한다.

 

직접 구현하는 경우 예시를 먼저 보고 동적 프록시도 보겠다.

public interface PersonBean {
	String getName();
	int getHotOrNotRating();
	void setName(String name);
	void setHotOrNotRating(int rating);
}
//본 객체
public class PersonBeanImpl implements PersonBean {
	private String name;
	
    @Override
	public String getName() {
		return name;
	}
	@Override
	public int getHotOrNotRating() {
		if(ratingCount==0) return 0;
		return rating/ratingCount;
	}
	@Override
	public void setName(String name) {
		this.name = Objects.requireNonNull(name);
	}
	@Override
	public void setHotOrNotRating(int rating) {
		this.rating += rating;
		++ratingCount;
	}

}
//프록시 객체
public class OwnerProxy implements PersonBean{
    private Person person;
    public OwnerProxy(Person person){
    	this.person = Objects.requireNonNull(person);
    }
    public String getName(){
    	return person.getName();
    }
    void setName(String name){
    	return person.setName(name);
    }
    //접근을 막고싶은 메서드는 빈 메서드로 재정의
    public void setHotOrNotRating(int rating){}
}

이렇게 접근을 막고싶은 메서드를 빈메서드로 재정의하여 사용자에 따라 다양한 프록시 객체를 부여하여 접근을 제어할수있다.

 

InvocationHandler라는 자바 api를 이용하면 동적 프록시를 구현할수있다.

자바 Reflection 사용

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class OwnerInvocationHandler implements InvocationHandler {
	private PersonBean person;	
	public OwnerInvocationHandler(PersonBean person) {
		this.person = person;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws IllegalAccessException {
		try{
			if(method.getName().startsWith("get")) //java reflection으로 메서드명으로 접근
				return method.invoke(person, args);
			else if(method.getName().equals("setHotOrNotRating"))
				throw new IllegalAccessException(); //setHotOrNotRating 메서드는 접근 불가: 예외 발생 처리
			else if(method.getName().startsWith("set"))
				return method.invoke(person, args);
		}
		catch(InvocationTargetException e){
			e.printStackTrace();
		}
		return null;
	}
}

InvocationHandler 인터페이스의 invoke 메서드만 정의해서 사용하면 된다.

위에 직접 구현한 방식과 동일한 기능이지만 자바 api를 이용하여 더 정형화 된다는 점도있고 프록시클래스가 실행시간에 동적으로 생성된다는 특징도 있다.

 

 

스마트 프록시

원래 객체에 대한 접근하기 전에 사전에 처리할 것이 있는 경우

ex) 동시 접근 제한, 접근 수 통계처리, 캐싱 등)에 사용되는 프록시

 

유사 패턴 비교

어뎁터 패턴과 유사할수있지만 어뎁터 패턴은 인터페이스를 변경하지만 프록시는 원 객체를 변경하지 않는다.

장식패턴은 원객체의 기능을 향상하지만 프록시는 원객체의 접근을 제어하거나 역할 대행을 하는것이다.

장식은 항상 장식 대상을 생성자의 인자로 받는 형태이지만 프록시는 내부적으로 대신할 원 객체를 생성할 수 있음.

장식은 실행 시간에 동적으로 장식이 이루어지지만 프록시는 원 객체와 컴파일 시간에 그 관계가 설정됨

클라이언트가 사용하는 측면에서도 차이가 있음. 프록시의 클라이언트는 프록시를 주어진 대로 사용하지만 장식 패턴에서 클라이언트는 원하는 기능을 위해 직접 객체를 장식하여 사용함

 

장점

원격 프록시의 장점: 네트워크의 복잡성을 클라이언트로부터 숨길 수 있음

가상 프록시의 장점: 원 객체의 생성을 지연시키고 생성 시점을 최적화 시켜 줄 수 있음

보호 프록시의 장점: 접근 권한을 제어할 수 있음

 

Reference

https://coding-factory.tistory.com/711

https://www.youtube.com/watch?v=WDaSaiwF7Ns

 

728x90

'개발 일지 > 디자인패턴 & 아키텍쳐' 카테고리의 다른 글

[아키텍쳐]SOA vs MSA  (0) 2021.12.31
리펙토링  (0) 2021.12.30
스테이트 패턴 (State Pattern)  (0) 2021.12.30
반복자 패턴 & 합성 패턴  (0) 2021.12.30
템플릿 메소드 패턴  (0) 2021.12.29