해당 포스팅은 한빛 미디어 헤드퍼스트 디자인 패턴(에릭 프리먼, 엘리자베스 롭슨 저)를 통해 공부한 내용을 정리한 블로그입니다.

아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
좋은 프로그래머는 자기 두뇌를 사용한다. 그러나 좋은 가이드라인은 모든 케이스를 고려해야만 하는 노력을 줄여준다.
(Francis Glassborow, 개발자)

Proxy Pattern


접근을 제어하고 관리 => 기본적으로 프록시가 하는 역할.

  • 원격 객체의 프록시만 넘기면 다른 머신에서도 상태 확인 가능.

 Proxy

  • 진짜 객체를 대신하는 역할.
  • 네트워크와 떨어져있는 클래스의 정보를 주고 받아야 함.
  • 서비스 역할 필요.
  • 네트워크로 들어오는 요청 수용
  • 프록시 객체의 레퍼런스를 받아오는 기능 필요

원격 프록시의 역할


원격 프록시 diagram

  • 원격 객체의 로컬 대변자 역할. => 어떤 메소드를 호출하면 다른 원격 객체에서 그 메소드 호출을 전달하는 객체.
    • 원격 객체 => JVM의 힙에 존재하는 객체(다른 주소 공간에서 돌아가고있는 객체)
    • 네트워크 통신과 같은 low level 작업은 프록시 객체에서 관리.

모니터링 코드에 원격 프록시 추가하기.

  • 다른 JVM에 들어있는 객체의 메소드를 호출할 수 없음.
  • RMI(Remote Method Invocation)를 이용하여 프록시 만들기
💡 모니터링 코드란
이전 State Pattern에서 예시를 통해 든 GumballMachine에서의 상태를 확인하는 코드 

원격 메소드 기초

원격 메소드 기초

  • 로컬 객체의 메소드를 호출하면 그 요청을 원격 객체에 전달해주는 시스템을 디자인.
  • 통신을 처리해주는 보조 객체가 필요하고 보조 객체를 사용.
    • 클라이언트는 로컬 객체의 메소드만 알면 됨.
    • 클라이언트 보조 객체의 메소드를 호출 => 서비스 보조 객체 호출(1)
    • 진짜 원격 서비스인 척하는 객체 => 클라이언트 보조 객체
    • 클라이언트 보조 객체는 서버에 연락을 취하고(2) 메소드 호출에 관한 정보를 전달하고 서버로부터 리턴되는 정보를 기다림.
    • 서버 보조 객체는 소켓으로 연결된 보조 객체로부터 요청을 받아오고(2) 호출 정보를 해석해서 진짜 메소드 호출(3)
    • 이후 반환되는 과정은 3 -> 2 -> 1 순으로 전달.

RMI 개요


  • 클라이언트와 서비스 보조 객체를 만들어주는 JAVA API
  • 네트워킹 및 입출력 관련 코드를 작성하지 않아도 JVM 메소드를 호출하듯 원격 메소드 호출 가능
  • 클라이언트보조 객체 => 스텁(stub)
    서버 보조 객체 => 스켈레톤(skeleton)

원격 서비스 만들기

원격 인터페이스 만들기

import java.rmi.*;

public interface MyRemote extends Remote {
	public String sayHello() throws RemoteException;
}
  1. 클라이언트가 원격으로 호출할 메소드 정의.
  2. 인터페이스를 서비스의 클래스 형식으로 사용
  3. java.rmi.Remote 확장 및 Remote 클래스 상속
  4. 모든 메소드를 RemoteException을 던지도록 선언.
  5. 원격 메소드의 인자나 반환 값은 반드시 primitive 또는 Serializble 형식으로

서비스 구현 클래스 만들기.

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class GumballMachine extends UnicastRemoteObject implements GumballMachineRemote {

	State soldOutState;
	State noQuarterState;
	State hasQuarterState;
	State soldState;
	State winnerState;

	State state = soldOutState;
	int count = 0;
	String location;

	public GumballMachine(String location, int numberGumballs) throws RemoteException {

		soldOutState = new SoldOutState(this);
		noQuarterState = new NoQuarterState(this);
		hasQuarterState = new HasQuarterState(this);
		soldState = new SoldState(this);
		winnerState = new WinnerState(this);

		this.location = location;
		this.count = numberGumballs;
		if (numberGumballs > 0) {
			state = noQuarterState;
		}
	}
}

import java.rmi.Naming;

public class GumballMachineTestDrive {

	public static void main(String[] args) {
		GumballMachineRemote gumballMachine = null;
		int count;

		if (args.length < 2) {
			System.out.println("GumballMachine <name> <inventory>");
			System.exit(1);
		}

		try {
			count = Integer.parseInt(args[1]);

			gumballMachine =
					new GumballMachine(args[0], count);
			Naming.rebind("//" + args[0] + "/gumballmachine", gumballMachine);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
  1. 실제 작업 처리 클래스
  2. UnicastRemoteObject 확장.
  3. RemoteException을 선언하는 생성자 구현
  4. 서비스를 RMI 레지스트리에 등록

RMI 레지스트리 구성

  1. 클라이언트는 레지스트리로부터 프록시(스텁, 클라이언트 보조 객체)를 받아감.

원격 서비스 실행하기

  1. 서비스 객체 실행

Proxy Pattern


Proxy Pattern

특정 객체로의 접근을 제어하는 대리인 제공 => 프록시

프록시에 접근을 제어하는 방법

  1. 원격 프록시를 써서 원격 객체로의 접근을 제어
  2. 가상 프록시를 써서 생성하기 힘든 자원으로의 접근을 제어
  3. 보호 프록시를 써서 접근 권한이 필요한 자원으로의 접근을 제어
  • RealSubject와 Proxy 인터페이스를 제공하는 Subject 인터페이스 존재
  • 두 객체는 똑같은 인터페이스를 구현하기에 RealSubject가 들어가야할 자리에 Proxy를 대신 넣음
  • 진짜 작업은 RealSubject가 처리. 대변인 역할이자 접근 제어

원격 프록시와 가상 프록시 비교하기

원격 프록시

  • 다른 JVM에 들어있는 객체의 대리인에 해당하는 로컬 객체

가상 프록시

  • 생성하는데 많은 비용이 드는 RealSubject를 대신하는 프록시 객체

보호 프록시 만들기

  • java.lang.reflect 패키지 안에 프록시 기능이 내장
  • 즉석에서 하나 이상의 인터페이스를 구현하고 지정한 클래스에 메소드 호출을 전달하는 프록시 클래스 만들기 가능.
    • 진짜 프록시 클래스는 실행 중에 생성되므로 이러한 자바 기술을 동적 프록시라함.
  • 자바에서 Proxy 클래스를 생성해주므로 Proxy 클래스에게 무슨 일을 해야하는지 알려줄 방법이 필요함.
    • InvocationHandler에 넣어주기
    • 프록시에 호출되는 모든 메소드에 응답.
  • 클라이언트에서 아무 메소드나 마음대로 호출할 수도 있기에 보호 프록시가 필요
    • 접근 권한을 바탕으로 객체로의 접근을 제어하는 프록시

+ Recent posts