책너두 (헤드 퍼스트 디자인 패턴) 35일차 (~474p)

요약

  • 프록시 패턴
    • 원격 프록시의 역할
    • 모니터링 코드에 원격 프록시 추가하기
    • 원격 메소드의 기초
      • 자바 RMI의 개요
      • 원격 서비스 만들기
        • 1단계: 원격 인터페이스 만들기
        • 2단계: 서비스 구현 클래스 만들기
        • 3단계: rmiregistry 실행하기
        • 4단계: 원격 서비스 실행하기

메모

원격 프록시의 역할

  • 원격 프록시는 원격 객체의 로컬 대변자 역할을 함.
    • 원격 객체란 다른 자바 가상 머신의 힙에서 살고 있는 객체를 뜻함.
      • 다른 주소 공간에서 돌아가고 있는 객체
    • 로컬 대변자란 로컬 대변자의 어떤 메소드를 호출하면, 다른 원격 객체에게 그 메소드 호출을 전달해 주는 객체를 뜻함.
  • 클라이언트 객체는 원격 객체의 메소드 호출을 하는 것처럼 행동함.
    • 하지만 실제로 로컬 힙에 들어있는 프록시 객체의 메소드를 호출하고 있음.
    • 네트워크 통신과 관련된 저수준 작업은 이 프록시 객체에서 처리해 줌.

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

  • 다른 JVM에 들어있는 객체의 메소드를 호출하는 프록시를 어떻게 만드는가?
    • 자바의 원격 메소드 호출(RMI : Remote Method Invocation)이 쓰임.
    • RMI를 사용하면 원격 JVM에 있는 객체를 찾아서 그 메소드를 호출할 수 있음.

원격 메소드의 기초

  • 로컬 객체의 메소드를 호출하면, 그 요청을 원격 객체에 전달해 주는 시스템을 디자인해야 함.
    • 우선 우리 대신 통신을 처리해 주는 보조 객체가 필요함.
    • 보조 객체를 사용하면 클라이언트는 로컬 객체의 메소드만 호출하면 됨.
    • 클라이언트 보조 객체의 메소드를 호출하는 것임.
    • 클라이언트는 그 보조 객체가 실제 서비스를 제공한다고 생각함.
    • 클라이언트 보조 객체가 그 요청을 원격 객체에게 전달함.
    • 클라이언트 보조 객체는 서버에 연락을 취하고, 메소드 호출에 관한 정보(메소드 이름, 인자)등을 전달하고, 서버로부터 리턴되는 정보를 기다림.
    • 서버는 서비스 보조 객체가 있어서, Socket 연결로 클라이언트 보조 객체로부터 요청을 받아 오고, 호출 정보를 해석해서 진짜 서비스 객체에 있는 진짜 메소드를 호출함.
    • 따라서, 서비스 객체는 그 메소드 호출이 원격 클라이언트가 아닌 로컬 객체로부터 들어온다고 생각함.
    • 서비스 보조 객체는 서비스로부터 리턴값을 받아서 그 정보를 해석해서 클라이언트 객체에 리턴함.

자바 RMI의 개요

  • RMI는 우리 대신 클라이언트와 서비스 보조 객체를 만들어 줌.
    • 보조 객체에는 원격 서비스와 똑같은 메소드가 들어 있음.
    • RMI를 사용하면 네트워킹 및 입출력 관련 코드를 직접 작성하지 않아도 됨.
    • 클라이언트는 그냥 같은 로컬 JVM에 있는 메소드를 호출하듯이 원격 메소드(진짜 서비스 객체에 있는 메소드)를 호출할 수 있음.
  • 클라이언트가 원격 객체를 찾아서 접근할 때 쓸 수 있는 룩업(lookup) 서비스도 RMI에서 제공함.
  • 클라이언트 보조 객체는 네트워크 호출을 전송해야 하므로 네트워킹 및 입출력 기능이 반드시 필요함.
📍 RMI에서 클라이언트 보조 객체는 스텁(stub), 서비스 보조 객체는 스켈레톤(skeleton)이라고 부름.
  • RMI 스텁이 프록시 역할을 맡음.

원격 서비스 만들기

  1. 원격 인터페이스 만들기
    • 클라이언트가 원격으로 호출할 메소드를 정의함.
    • 클라이언트에서 이 인터페이스를 서비스의 클래스 형식으로 사용함.
    • 스텁과 실제 서비스에 이 인터페이스를 구현해야 함.
  2. 서비스 구현 클래스 만들기
    • 실제 작업을 처리하는 클래스임
    • 원격 메소드를 실제로 구현한 코드가 들어 있는 부분
    • 나중에 클라이언트에서 이 객체에 있는 메소드를 호출함.
  3. RMI 레지스트리(rmiregistry) 실행하기
    • 전화번호부와 비슷하다고 보면 됨.
    • 클라이언트는 이 레지스트리로부터 프록시(스텁, 클라이언트 보조 객체)를 받아 감.
  4. 원격 서비스 실행하기
    • 서비스 객체를 실행해야 함.
    • 서비스를 구현한 클래스에서 서비스의 인스턴스를 만들고, 그 인스턴스를 RMI 레지스트리에 등록함.
    • 그러면 서비스를 클라이언트에서 사용할 수 있음.

1단계: 원격 인터페이스 만들기

1. java.rmi.Remote를 확장함.

public interface MyRemote extends Remote {
  • Remote 인터페이스에서 원격 호출을 지원함.

2. 모든 메소드를 RemoteException을 던지도록 선언함.

import java.rmi.*;

public interface MyRemote extends Remote {
    public String sayHello() throws RemoteException;
}
  • 클라이언트는 서비스 원격 인터페이스 형식으로 선언해서 사용함.
    • 클라이언트는 원격 인터페이스를 구현하는 스텁의 메소드를 호출함.
    • 스텁이 각종 입출력 작업을 처리할 때 네트워크 등에 안 좋은 일이 일어날 수 있음.
    • 따라서, 클라이언트는 원격 예외를 처리하거나 선언해서 안 좋은 일에 대비해야 함.

3. 원격 메소드의 인자와 리턴값은 반드시 원시 형식(primitive) 또는 Serializable 형식으로 선언함.

  • 원격 메소드의 인자는 모두 네트워크로 전달되어야 하며, 직렬화로 포장됨.
    • 리턴값도 마찬가지임
  • 직접 만든 형식을 전달한다면, 클래스를 만들 때 Serializable 인터페이스도 구현해야 함.
public String sayHello() throws RemoteException;
  • 서버에서 클라이언트로 다시 전송해야 하므로 이 String 리턴값을 직렬화할 수 있어야 함.

2단계: 서비스 구현 클래스 만들기

1. 서비스 클래스에 원격 인터페이스를 구현함.

public class MyRemoteImpl extends UnicaseRemoteObject implements MyRemote {
    public String sayHello() {
        return "Server says, 'Hey'";
    }
    // 기타 코드
}

 

public class MyRemoteImpl extends UnicaseRemoteObject implements MyRemote {
    private static final long serialVersionUID = 1L;
  • 원격 서비스 객체 역할을 하려면 객체에 ‘원격 객체’ 기능을 추가해야 함.
    • 가장 간단한 방법은 UnicaseRemoteObject를 확장해서 슈퍼 클래스에서 제공하는 기능으로 처리함.
    • UnicaseRemoteObject는 Serializable을 구현하므로 serialVersionUID이 필요함.

2. RemoteException을 선언하는 생성자를 구현함.

  • 슈퍼클래스 UnicaseRemoteObject에는 생성자가 RemoteException을 던진다는 문제가 있음.
    • 이 문제를 해결하려면 서비스를 구현하는 클래스에 RemoteException을 선언하는 생성자를 만들어야 함.
    • 슈퍼 클래스 생성자가 어떤 예외를 던지면 서브 클래스 생성자도 그 예외를 선언해야 함.
      • 어떤 클래스가 생성될 때 슈퍼클래스의 생성자도 반드시 호출되기 때문

3. 서비스를 RMI 레지스트리에 등록함.

try {
    MyRemote service = new MyRemoteImpl();
    Naming.rebind("RemoteHello", service);
} catch(Exception ex) {...}
  • 원격 서비스를 원격 클라이언트에서 쓸 수 있게 만들어야 함.
  • 인스턴스를 만든 다음, RMI 레지스트리에 등록하면 됨.
  • 서비스를 구현한 객체를 등록하면 RMI 시스템은 레지스트리에 스텁만 등록함.
    • 클라이언트는 스텁만 필요하기 때문
  • 서비스를 등록할때 java.rmi.Naming 클래스에 있는 rebind() 정적 메소드를 사용함.

3단계: rmiregistry 실행하기

  • 터미널을 새로 띄워서 rmiregistry를 실행함.

4단계: 원격 서비스 실행하기

  • 다른 터미널을 열고 서비스를 실행함.

댓글

Designed by JB FACTORY