SlideShare a Scribd company logo
1 of 13
Download to read offline
Java RMI guide
(RMI 개발 및 활용 가이드)
2015년 06월 15일
(Sunny Kwak)
sunnykwak@daum.net
SunnyKwak’s Technical document
2 / 13
Copyrights
© 2004Sunny Kwak. All rights reserved.
Other disclaimers
The names of actual companies and products mentioned herein may be the trademarks of their respective owners.
Version
Version 1.0 (draft)
History
2004년 12월 13일 월요일 First created.
SunnyKwak’s Technical document
3 / 13
Table of Contents
1. INTRODUCTION ......................................................................................................... 4
1.1 분산시스템.................................................................................................................. 4
1.2 RMI(REMOTE METHOD INVOCATION)이란? ............................................................................. 4
1.3 WHY RMI? .................................................................................................................. 4
2. RMI에 관해서................................................................................................................ 5
2.1 RMI의 기본 개념 .......................................................................................................... 5
2.2 STUB과 PARAMETER MARSHALLING ....................................................................................... 5
2.3 동적클래스 로등(DYNAMIC CLASS LOADING)........................................................................... 6
3. 자바 RMI 프로그래밍 ...................................................................................................... 7
3.1 자바에서의 RMI 구현 .................................................................................................... 7
3.2 서버 측 프로그래밍 ....................................................................................................... 7
3.3 클라이언트 측 프로그래밍............................................................................................... 9
3.4 RMI 실행 ..................................................................................................................10
3.4.1 서버.....................................................................................................................................................10
3.4.2 클라이언트 ..........................................................................................................................................12
3.5 동적 클래스로딩에 관해 주의할 점 ...................................................................................12
SunnyKwak’s Technical document
4 / 13
1. Introduction
www.webdox.co.kr 서창근 님의 글 참조
1.1 분산시스템
소프트웨어 개발에서 가장 주목 받고 있는 것이 객체지향 개발이라면 시스템 분야에서 가장 주목
받는 것은 분산시스템의 개념이다. 어쩌면 이 분산시스템의 개념이 객체지향 개발을 절대적으로 필
요하게 했을지도 모른다. 분산시스템이란 기본적으로 다수의 컴퓨터가 하나의 시스템처럼 작동하며
사용자에게 그 차이를 느끼지 못하게 하는 시스템을 말한다. 분산시스템의 중요한 개념은 명령 실행
의 투명성 (transparency), 효율성 (efficiency), 일관성 (consistency), 유연성 (flexibility), 그리고
robustness 등을 들 수 있는데 그 중 가장 많이 강조하는 것이 투명성이다. 즉, 사용자가 어떤 명령을
실행시켰을 경우 그 명령이 지금 사용자가 사용하고 있는 컴퓨터에서 실행되고 있는지, 아니면 네트
워크에 연결된 다른 컴퓨터에서 실행되고 있는지 분간할 수 없음을 말한다. 대표적인 분산시스템으
로 UNIX의 NFS (Network File System), AFS (Andrew File System), CODA 등이 있다.
이러한 시스템에서 중요한 것은 바로 시스템에 소속되어 있는 컴퓨터 간의 통신이다. 더욱 자세하게
말하면 각 컴퓨터에서 실행되고 있는 프로그램, 또는 프로세스들이 서로 통신이 가능해야 한다는 것
이다. 이 프로그램 사이의 통신을 가능하게 하는 것 중 현재 UNIX에서 가장 많이 사용되고 있는 통
신 프로토콜이 바로 RPC (Remote Procedure Call)이다. RPC는 네트워크에 연결되어 있는 다른 컴퓨터
에 존재하는 함수를 사용자가 눈치채지 못하도록 실행해 줌으로써 분산시스템의 구현을 가능하게
했다. 그러나, RPC는 객체지향 개발개념을 구현하지는 않으며 단지 다른 컴퓨터에 저장되어 있는 순
차적인 언어로 개발된 애플리케이션의 함수를 부를 수 있도록 해주는 역할을 할 따름이다.
1.2 RMI(Remote Method Invocation)이란?
위에서 객체지향 개발 개념을 설명할 때 프로그램이 객체 간의 메시지 전송으로 실행된다고 했다.
그렇다면 만약 이 객체들이 서로 다른 컴퓨터에 존재한다면 어떻게 해야 할까? 즉, 예전의 분산시스
템의 개념은 서로 다른 프로그램들이 다른 컴퓨터에 존재하고 실행되는 것이었다면 근래에는 각 객
체가 분산되어 존재하는 개념인 것이다. 특히 현대 시스템 개발은 3계층 시스템 (three-tier system)이
주류를 이루므로 데이터베이스, 애플리케이션, 그리고 클라이언트가 모두 객체를 통하여 구현되고
따로 저장되는 일이 많아졌다. 이렇게 분산되어 존재하는 객체간의 메시지 전송(메소드를 부르는 것
과 동일함)을 가능케 해주는 것이 바로 RMI 프로토콜이다. 그리고, 객체지향 개발 개념을 기본으로
RMI의 기능과 transaction, security등 엔터프라이즈급 기능을 포괄적으로 컴포넌트 모델화한 것이 바
로 EJB (Enterprise JavaBeans)가 되겠다. 일반적으로 자바 RMI라고 하면 자바 애플리케이션 간의 통
신을 가능케 해주는 프로토콜로 생각하나 실제로 RMI 자체는 자바 RMI 이전부터 쓰여지던 단어이
며 더욱 포괄적인 의미를 지닌다. RMI는 분산객체 개념을 구현하는 CORBA 또는 DCOM 등에서 분
산객체 간의 통신을 구현하는 모든 프로토콜을 의미한다. CORBA나 DCOM은 이 글의 범위 밖이므
로 접어두고 이번 글에선 특히 자바 애플리케이션 간의 RMI에 대해 집중적으로 살펴보도록 하자.
1.3 Why RMI?
네트워크 프로그래밍을 해보신 독자라면 곧 이런 의문을 가지게 될 것이다. 왜냐하면 많은 독자들이
지금까지 데이터 전송을 위해서 소켓(socket)을 사용해 왔기 때문이다. 그러나, RMI를 배우고 나면
아마도 최소한 자바 애플리케이션에서 소켓을 사용하고자 하는 개발자는 별로 없을 것이다. 프로그
래밍의 기본은 데이터의 전송이다. RMI는 분산객체 간의 데이터 전송을 메소드를 부르는 것과 같은
방법으로 실현시킨다. 소켓을 다루어 본 독자라면 벌써 알겠지만 메소드를 부르는 것과 소켓으로 데
이터를 보내는 것 중 어느 것이 더 편한가? 당연히 메소드를 부르는 것이 훨씬 더 편하다. 똑같은
데이터 전송 방법의 일환인데 더 어렵고 불편한 방법을 쓸 필요가 없는 것이다.
SunnyKwak’s Technical document
5 / 13
2. RMI에 관해서
2.1 RMI의 기본 개념
RMI가 자바에서 어떻게 구현되는지 살펴보기 전에 먼저 RMI가 어떻게 가능한지 살펴보기로 하자.
RMI의 기본적인 이론을 먼저 공부하는 것이 나중에 자바 RMI를 공부할 때 이해하기 쉽다. 그리고
뭐든지 그렇지만 기반 이론을 이해하는 것이 나중에 문제가 발생하였을 경우 문제를 알아내고 해결
하기 쉽기 때문이다. RMI의 개념을 자바 중심으로 서술하였지만 다른 RMI 기법들도 이와 비슷하다
고 보면 된다.
그렇다면 도대체 RMI에서 어떻게 한 객체가 다른 컴퓨터에 존재하는 객체에게 메시지를 전송할 수
있을까? 가장 기본적인 문제는 그 객체가 다른 객체의 존재를 알아야 하며, 그 객체에게 메시지를
전송하는 방법을 알아야 하고, 그리고 마지막으로 그 객체로부터 메시지의 답변을 받아야 한다는 것
이다. 이 과정에서 수반되는 문제는 메시지를 전송하고 답변을 받을 때 어떠한 종류의 데이터를 주
고 받아야 하는가 이다. RMI는 위에 나열된 문제를 두 가지의 방법으로 해결한다. 첫째가 스텁(stub)
이며 둘째가 parameter marshalling 이다.
2.2 Stub과 Parameter marshalling
설명을 위해서 메소드를 부
르려고 하는 객체가 존재하
는 컴퓨터를 클라이언트라고
하고 그 메소드를 실행하는
객체가 존재하는 컴퓨터를
서버라고 하자. 일반적인 의
미의 클라이언트와 서버와는
약간 다르므로 혼동하지 말
기를 바란다. 여기선 누가 누
구의 메소드를 부르냐에 따
라 서버와 클라이언트의 역
할이 바뀔 수도 있다.
클라이언트의 객체가 서버에
존재하는 객체의 메소드를
부르려고 할 경우 그 메소드
의 존재를 알려주고 추상적
으로 나타내 주는 것이 바로
스텁이다. 이 스텁은 실제로 인터페이스의 형태로 서버에 있는 객체가 원격적으로 부를 수 있는 메
소드를 공개하는 것으로 서버에 저장되어 있으며 클라이언트의 객체가 서버에 있는 메소드를 부르
려고 할 때 클라이언트로 전달이 된다. 스텁 클래스는 원래 서버의 객체로부터 생성이 되지만 클라
이언트에 머무르면서 클라이언트가 서버의 메소드를 부를 수 있도록 도와준다. EJB에서는 이것을 원
격 인터페이스 (remote interface)라고도 한다.
스텁이 하는 중요한 일은 두 가지가 있다. 첫째는 당연히 서버에 존재하는 메소드를 불러줘야 하고
둘째는 그 메소드의 인자를 일정한 포맷으로 바꿔주는 것이다. 이렇게 인자를 일정한 포맷으로 바꿔
주는 일을 parameter marshalling 이라고 한다. 즉, 인자를 조율하고 정리하는 역할을 하는 것이다. 왜
이런 parameter marshalling 이 필요한 것일까? 예를 들어 어떤 시스템은 정수 데이터를 big-endian으로
표현하고 어떤 시스템은 little-endian으로 표시한다. 정수가 차지하는 메모리의 양도 다를 수 있다.
자바는 플랫폼과는 독립적으로 작동하는 언어이다. 만약 한 객체가 윈도우 시스템에서 작동하고 다
SunnyKwak’s Technical document
6 / 13
른 객체는 유닉스 환경에서 작동하고 있다면 이러한 데이터의 구현 방법을 통일해 줄 필요가 있다.
특히 3계층 시스템(three-tier system)의 경우 데이터베이스 서버, 애플리케이션 서버, 그리고 클라이언
트가 일일이 다른 플랫폼으로 구현될 수도 있으므로 데이터의 구현방법을 일관적으로 표현하는 일
은 아주 중요하다. 그 일이 바로 parameter marshalling 이다.
Parameter marshalling 이 끝났으면 스텁은 서버에 존재하는 객체의 메소드를 부르게 된다. 이때 스텁
이 서버에 보내는 정보는 서버 객체의 아이디, 실행해야 할 메소드, 그리고 변환된 인자이다. 클라이
언트 쪽에서 스텁이 대리인의 역할을 하듯 서버 쪽에서도 이러한 대리인의 역할을 하는 존재가 있
는데 수신자(receiver) 또는 스켈레톤(skeleton) 이라고도 한다. 수신자가 하는 역할은 스텁의 반대라고
생각하면 된다. 조율된 인자들을 다시 그 서버에 맞는 형태로 바꿔준 뒤 스텁이 보낸 아이디의 객체
가 갖고 있는 알맞은 메소드를 실행시킨다. 그리고 실행이 끝났으면 결과를 다시 parameter
marshalling 을 통하여 변환한 뒤, 다시 스텁에게 보내준다. 스텁은 이 결과를 받아서 다시 시스템에
맞게 변환시킨 뒤, 원래 메소드를 불렀던 객체에게 결과를 넘겨준다. 물론 결과가 예외일 수도 있다.
2.3 동적 클래스 로딩(Dynamic class loading)
그렇다면 여기서 또 하나의 의문점이 생기게 된다. 만약 인자가 일반적인 데이터 타입, 즉 숫자나
문자 등일 경우엔 parameter marshalling이 간단하지만 만약 어떠한 객체라면 어떻게 할까? 특히 결과
값이 서버에 존재하고 있는 또 다른 원격객체라면 클라이언트엔 그 객체의 코드가 없으므로 문제는
심각하다. 예를 들어 서버에 Computer라는 객체가 실행되고 있다고 하자. 이 객체의 메소드 중에
getType()이라는 메소드가 있는데 컴퓨터의 종류를 추상화하는 ComputerType이라는 객체를 돌려준다
고 하자. 즉, 클라이언트에서 다음과 같이 실행시킬 수 있다는 얘기가 되겠다.
Computer remote_comp = new Computer();
ComputerType mycomp = (ComputerType)remote_comp.getType();
mycomp.someMethod();
문제는 여기서 getType() 메소드를 불렀을 때 돌아오는 값이 어떤 숫자나 문자가 아니라 서버에 존
재하는 객체라는 것이다. 그리고 클라이언트는 이 새로운 원격객체의 someMethod()라는 메소드를 불
러주고 있다. 물론 클라이언트는 함수 값으로 돌아오는 객체가 ComputerType이라는 객체라는 것은
알지만 실제로 돌아오는 객체는 ComputerType의 서브클래스일 수도 있다. 게다가 이 새로운 객체가
서버에 정의돼 있는 새로운 예외 객체를 필요로 할 수도 있다. 물론 이 새로운 객체의 someMethod()
라는 메소드를 부르려면 클라이언트가 이 새로운 객체에 대해서 알고 있어야 한다. 이 문제를 해결
하기 위해 클라이언트에 이 객체를 정의해주고 저장하는 방법도 있고 서버와 동시에 일일이 클라이
언트를 업데이트 해주는 것도 방법이겠지만 새로운 종류의 ComputerType을 서버에 정의할 때마다
일일이 클라이언트를 업데이트 할 수는 없다. 그래서 필요한 것이 바로 동적 클래스 로딩이다.
동적 클래스 로딩은 이렇게 클라이언트가 알지 못하는 객체가 필요할 경우 서버에서 그때 그때 필
요할 때마다 클라이언트로 그 필요한 객체의 스텁클래스를 불러오는 것을 말한다. 자바의 애플릿과
비슷한 개념이라고 생각하면 되겠다. 위의 예제의 경우 자동으로 ComputerType의 서브클래스의 스텁
클래스가 클라이언트로 로딩되어 someMethod()를 부르는 것을 가능케 한다. 하지만 이렇게 다른 컴
퓨터에서 네트워크를 통하여 스텁클래스를 불러올 경우 보안의 문제가 발생할 수 있으므로 실제로
이러한 시스템을 가동시킬 땐 꼭 보안에 신경을 써서 자바 보안 매니저 (Java Security Manager)등을
사용하는 것이 좋다.
SunnyKwak’s Technical document
7 / 13
3. 자바 RMI 프로그래밍
3.1 자바에서의 RMI 구현
자바 RMI를 구현하기 위해선 일단 클라이언트와 서버에 적절한 코딩을 해야 하는데 자바 RMI는 개
발자에게 편리하게 개발되었으므로 실제로 코딩은 중요한 문제가 아니다. 하지만, RMI라는 것이 근
본적으로 간단한 문제가 아니므로 실제로 RMI를 실행시킬 수 있는 환경을 만드는 것이 실제 코딩
보다 더 복잡하다.
3.2 서버 측 프로그래밍
클라이언트 쪽에서는 서버에 존재하는 객체와 통신을 하기 위해서 인터페이스를 정의해 주어야 한
다. 클라이언트의 객체가 서버의 객체가 가지고 있는 메소드를 부를 때 이 인터페이스를 사용하여
부르게 된다. 이 인터페이스는 서버와 클라이언트 양쪽에 존재하게 된다. 예를 들어 비행기 좌석을
예약하는 프로그램을 만든다고 하자. 만약 이 좌석을 서버에 존재하는 객체로 표현하고 클라이언트
가 이 좌석이 현재 빈 좌석인지 알아보려고 한다고 하자. 그렇다면 일단 다음과 같이 인터페이스를
정의해주어야 한다.
import java.rmi.Remote;
import java.rmi.RemoteException;
interface Seat extends Remote
{
public boolean isReserved(int seat_number) throws RemoteException;
}
여기서 주의해야 할 것은 모든 원격 인터페이스는 Remote 클래스를 상속하여야 하고 원격적으로 공
개되는 메소드는 모두 RemoteException 예외클래스를 던져야 한다는 것이다. 이렇게 인터페이스를
정의했으면 다음엔 실제 객체를 구현해야 한다.
import java.rmi.RemoteException;
public class SeatImpl implements Seat
{
/**
* 상위 클래스에서 RemoteException이 발생하기 때문에 생성자 정의
*/
public SeatImpl() throws RemoteException
{
}
public boolean isReserved( int seat_number ) throws RemoteException
{
return false;
}
SunnyKwak’s Technical document
8 / 13
}
다음으로 해야 할 것이 객체의 이름을 RMI Registry에 등록한 후 클라이언트 측에 서비스하는 것이
다. JNDI (Java Naming and Directory Interface)의 일환이기도 한데 클라이언트에 있는 객체에게 서버 객
체의 존재를 알려주는 역할을 한다. 즉, 서버 객체를 어떠한 이름 하에 등록하여 클라이언트의 객체
가 그 이름으로 원격 객체를 찾을 수 있도록 해주는 것을 말한다. 클라이언트는 이 이름으로 서버
객체를 찾으며 찾았을 경우 그 이름에 해당하는 서버 객체의 스텁 클래스가 클라이언트로 전송되어
서버 객체의 메소드를 부르는 것을 가능케 한다.
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.NotBoundException;
import java.rmi. RMISecurityManager;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.net.MalformedURLException;
public class RmiServer
{
/**
* RmiServer 프로그램의 main() 메소드.
*/
public static void main( String args[] )
{
// 보안 매니저를 설정한다.
// RMI 보안에 위배되지 않으려면 반드시 설정.
if( System.getSecurityManager() == null )
System.setSecurityManager( new RMISecurityManager() );
try
{
// 이름을 등록한다.
SeatImpl si = new SeatImpl();
UnicastRemoteObject.exportObject( si );
// make object findable
Registry r = LocateRegistry.getRegistry( "localhost", 1099 );
r.rebind( "seat", si );
// Serve clients
BufferedReader rdr = new BufferedReader(
new InputStreamReader(System.in) );
SunnyKwak’s Technical document
9 / 13
while(true)
{
System.out.println( "Type EXIT to shutdown the server." );
if( "EXIT".equals(rdr.readLine().toUpperCase()) )
break;
}
// unregister object
r.unbind("rendition");
// unexport object
UnicastRemoteObject.unexportObject( rfi, true );
}
catch(Exception e )
{
e.printStackTrace();
}
}
}
위의 예제를 보면 seat이라는 이름 하나만 등록한 것을 알 수 있다. 실제로 비행기 좌석 예약 시스
템을 만든다고 하자. SeatImpl 이라는 서버 객체가 만약에 좌석 하나하나를 구현한다고 하면 그 좌석
하나하나를 다 등록할 수는 없는 일이다. 왜냐하면 이 이름은 클라이언트에게 서버 객체의 존재를
알게 해주는 것이므로 클라이언트가 어떠한 메소드를 부르기 전에 벌써 등록이 되어 있어야 한다.
하지만 여기서 bind() 메소드의 인자 중 하나가 바로 SeatImpl 객체이다. 그렇다면 좌석 하나 하나의
객체를 다 생성해서 그 이름들을 따로 등록해야 한다는 예기가 된다. 특히 좌석의 데이터가 데이터
베이스에 저장되어 있을 경우 그것을 일일히 동적으로 불러와야 하는데 그렇다면 클라이언트가 메
소드를 부르기 전에 그 이름을 등록한다는 것은 불가능하다. 이것은 상당히 비효율적인 개발방법이
다. 여기선 SeatImpl 객체가 한 비행기의 모든 좌석을 구현하게 개발하는 것이 더욱 효율적이며 RMI
Registry도 더 간단해진다. seat 이라는 이름을 통하여 클라이언트에게 서버객체의 존재를 알려주고
SeatImpl 이라는 서버객체가 따로 각 좌석의 데이터를 데이터베이스로부터 불러오는 것이 더 효율적
인 것이다. 게다가 RMI Registry는 모든 객체들이 공유하는 서비스이므로 한 프로그램이 다 독차지해
서도 안된다.
3.3 클라이언트 측 프로그래밍
위와 같이 서버 쪽의 프로그래밍이 되어 있으면 이젠 클라이언트 쪽을 구현하면 된다. 일단 위에서
코딩한 인터페이스 파일이 필요하고 그 다음엔 그 인터페이스를 이용하여 서버 객체의 메소드를 불
러주면 되겠다.
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.RMISecurityManager;
public class SeatClient
SunnyKwak’s Technical document
10 / 13
{
public boolean getReserved() throws Exception
{
String strResult;
// 먼저 원격 객체를 RMI Registry에서 찾아본다.
Seat st = (Seat)Naming.lookup("rmi://localhost/seat");
// 원격 객체를 찾는데 성공했으면 메소드를 호출
return st. isReserved (1);
}
/**
* SeatClient 프로그램의 main() 메소드.
*/
public static void main( String args[] )
{
try
{
SeatClient sc = new SeatClient();
System.out.println( sc.getReserved() ? “reserved”, “not” );
}
catch( Exception e )
{
e.printStackTrace();
}
}
}
먼저 클라이언트 프로그램이 실행되기 시작될 때 자바 보안매니저를 꼭 실행시키도록 하자. 만약 클
라이언트가 자바 애플릿일 경우엔 자동으로 보안이 실행되므로 상관없지만 자바 애플리케이션이라
면 꼭 실행시켜 주도록 한다. 그리고 서버객체의 이름을 찾을때 URL을 사용하는것을 기억하도록 하
자. "rmi://서버이름/등록된 객체이름" 의 형식이면 되겠다.
3.4 RMI 실행
위와 같이 코딩이 끝났으면 이젠 프로그램을 실행하는 일만 남았다. 그러나 자바 RMI를 실행하기
위해선 약간의 복잡한 준비가 필요하다. 이렇게 서버와 클라이언트 사이를 연결해주는 고리가 필요
한데 그것이 바로 rmiregistry 이다. rmiregistry 는 RMI를 사용하는 객체의 등록을 관장하는 역할을 한
다. 이제부터 rmiregistry 의 사용방법과 함께 서버와 클라이언트가 RMI를 실행하려면 어떠한 과정을
거쳐야 하는지 알아보도록 하자.
3.4.1 서버
1. 서버 객체와 인터페이스 파일 컴파일하기
2. rmic 를 이용하여 스텁 클래스 생성
3. 보안 정책 파일 작성
SunnyKwak’s Technical document
11 / 13
4. rmiregistry 실행
5. 서버 프로그램 실행
위에 나열한 것과 같이 실행하면 RMI를 사용할 수 있게 된다. 일단 인터페이스와 서버 프로그램을
자바 컴파일러로 컴파일 한다. 처음에 서술했듯이 클라이언트 객체가 서버 객체를 사용하려면 스텁
클래스가 있어야 한다. 이 스텁 클래스를 생성하는 것이 rmic 프로그램이 되겠다. rmic 로 생성된 스
텁클래스가 동적 클래스로딩을 통해서 클라이언트가 필요로 할 때 클라이언트로 옮겨 가게 되는 것
이다. rmic 는 다음과 같이 실행시키면 된다. (자바 1.2 이전 버전일 쓸일이 없다면, -v1.2 옵션을 사용
하길 권장한다.)
rmic –v1.2 SeatImpl
클래스 이름은 패키지 이름까지 다 사용하여야 한다. 만약에 SeatImpl 클래스가 com.myairline 이라는
패키지의 일부라면
rmic com.myairline.SeatImpl
이라고 해야 한다. rmic 를 실행시키면 앞에서 말한 바와 같이 스텁 클래스 SeatImpl_Stub.class 라는
이름으로 생성된다.
이렇게 스텁을 만들었으면 다음은 rmiregistry 를 실행해야 한다. 윈도우즈 서버에서는
start rmiregistry
유닉스나 리눅스에선
rmiregistry &
과 같이 실행 시켜주면 된다. 여기서 rmiregistry 의 역할은 명확할 것이다. 클라이언트가 서버의 이
름과 객체의 이름으로 서버 객체를 찾으면 그 요구가 rmiregistry 로 전달된다. 서버 측에선
rmiregistry 에 서버 객체가 등록되어 있으므로 그 서버 객체의 스텁 클래스가 클라이언트로 로딩된
다. 그러면 클라이언트 객체는 그 스텁 클래스를 이용하여 서버 객체의 메소드를 부를 수 있게 된다.
다음은 보안정책을 설정해주는 파일을 만들어야 한다. 파일 이름은 아무래도 상관없다. 여기선
security.policy 라고 하겠다. 위에서 자바 보안매니저를 실행시킨 것을 기억할 것이다. 자바 보안매니
저는 기본적으로 어떠한 네트웍 연결도 허용하지 않는다. 그러므로 이러한 보안정책을 설정해 주어
야 하는 것이다. 보안정책 파일엔 다음과 같이 해주면 되겠다.
grant
{
permission java.net.SocketPermission
"*:1024-65535", "connect,listen";
};
SunnyKwak’s Technical document
12 / 13
여기선 RMI가 포트번호 1024에서 65535 사이의 아무 포트나 사용할 수 있도록 하였다. RMI의 기본
설정은 1099이다.
이렇게 rmiregistry 가 실행되고, 보안 정책 파일을 작성한 후 서버 프로그램을 실행하여야 한다
여기서 주의해야 할 것은 서버 프로그램을 서비스 프로그램으로 실행시켜야 한다는 것이다. 또한,
RMI 서버 측에서 스텁 클래스 파일을 동적 로딩할 수 있도록, codebase 인자를 명령줄에 추가해야
하며, 보안 정책 파일을 지정해야 한다. 위의 예제에선
start java -Djava.security.policy=security.policy
-Djava.rmi.server.codebase=file:/{serverlocation}/ SeatImpl
와 같이 실행시켜야 한다.
3.4.2 클라이언트
1. 클라이언트와 인터페이스 파일 컴파일 하기
2. 클라이언트 프로그램의 실행
서버와 마찬가지로 클라이언트 객체의 자바 파일과 인터페이스 파일을 자바 컴파일러로 컴파일 해
준다. 여기서 인터페이스 파일은 서버에서 컴파일한 것과 같은 파일이어야 한다.
이렇게 보안정책이 설정되었으면 프로그램을 실행시키는 일만이 남았다. 프로그램은 다음과 같이 실
행시키면 된다. 보안정책 파일을 지정해 주는 것을 잊지 않도록 하자.
java SeatClient -Djava.security.policy=security.policy
3.5 동적 클래스 로딩에 관해 주의할 점
위에서 자바 RMI의 개념 중에 동적 클래스 로딩이라는 것이 있었다. 클라이언트가 필요로 하는 스
텁 클래스를 그때 그때마다 서버에서부터 받아오는 것을 말한다. 이 동적 클래스 로딩 때문에 실제
로 프로그램을 개발할 때 주의해야 할 사항이 몇 가지 있으므로 여기서 짚고 넘어가려고 한다.
위에서 언급한대로 클라이언트 객체는 원격 인터페이스를 통하여, 즉 스텁 클래스를 통하여 서버 객
체의 메소드를 부르게 된다. 다시 말하면 클라이언트는 원격 인터페이스에 공개되어 있는 메소드 밖
에 부를 수 없다는 말이다. 서버객체 내에 다른 여러 가지 메소드가 있을 수 있다. 하지만 원격 인
터페이스에 공개되어 있지 않으면 클라이언트 객체는 그 메소드를 부를 수 없다.
예를 들어 클라이언트 객체가 서버 객체의 어떠한 메소드를 불렀는데 그 값으로 원격 객체를 상속
한 어떠한 객체가 돌아왔다고 하자. 그리고 원격 객체는 원격 인터페이스를 구현하지만 그 원격 객
체를 상속한 객체는 원격 인터페이스를 구현하지 않는다고 하자. 그렇다면 클라이언트 객체가 받은
이 객체는 어떠한 메소드의 실현이 가능할까? 답은 수퍼 클래스가 구현한 원격 인터페이스에 공개
되어 있는 메소드 뿐이다. 상속한 클래스가 다른 어떤 메소드를 정의했을지라도 그것은 클라이언트
가 실행시킬 수 없다. 이것은 그 객체가 메소드의 인자로 사용되던 돌아오는 값으로 사용되던 마찬
가지다.
그렇다면 원격 인터페이스를 구현하지 않는 다른 클래스들은 어떻게 될까? 예를 들어 서버 객체의
메소드를 부를 때 String 클래스를 인자로 사용했다고 하자. 이럴 경우 String 클래스는 Remote 인터
페이스를 구현하지 않으므로 실제론 객체가 복사가 되어 서버로 보내지게 된다. 만약 서버가 String
객체의 어떠한 메소드를 사용한다고 해도 객체 자체가 복사가 되어 보내졌으므로 문제가 없다.
SunnyKwak’s Technical document
13 / 13
간단히 말하면 원격 인터페이스를 구현하는 객체가 인자나 메소드 값으로 보내질 경우 스텁 클래스
만이 보내지고 원격 인터페이스를 구현하지 않는 객체를 보낼 경우 객체 자체가 복사되어 보내진다
는 얘기가 되겠다.

More Related Content

Similar to Java rmi 개발 가이드

MSA와 infra
MSA와 infraMSA와 infra
MSA와 infraJe Hun Kim
 
[HaU] 신입 기술 면접 준비 java
[HaU] 신입 기술 면접 준비 java[HaU] 신입 기술 면접 준비 java
[HaU] 신입 기술 면접 준비 java유리 하
 
Implementing remote procedure calls rev2
Implementing remote procedure calls rev2Implementing remote procedure calls rev2
Implementing remote procedure calls rev2Sung-jae Park
 
쇼핑검색 React 전환 경험 공유
쇼핑검색 React 전환 경험 공유쇼핑검색 React 전환 경험 공유
쇼핑검색 React 전환 경험 공유NAVER SHOPPING
 
[NDC 2018] 테라 콘솔 포팅기 - 현세대 콘솔 이식을 위한 렌더링 최적화 여정
[NDC 2018] 테라 콘솔 포팅기 - 현세대 콘솔 이식을 위한 렌더링 최적화 여정[NDC 2018] 테라 콘솔 포팅기 - 현세대 콘솔 이식을 위한 렌더링 최적화 여정
[NDC 2018] 테라 콘솔 포팅기 - 현세대 콘솔 이식을 위한 렌더링 최적화 여정SangHyeok Hong
 
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - Http Request
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - Http Request[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - Http Request
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - Http RequestNAVER D2
 
All about JDBC Performance Tuning_Wh apm
All about JDBC Performance Tuning_Wh apmAll about JDBC Performance Tuning_Wh apm
All about JDBC Performance Tuning_Wh apm엑셈
 
프로그래밍 언어 기초(델파이,C++)
프로그래밍 언어 기초(델파이,C++)프로그래밍 언어 기초(델파이,C++)
프로그래밍 언어 기초(델파이,C++)Devgear
 
버클리Db 를 이용한 게임 서버 제작
버클리Db 를 이용한 게임 서버 제작버클리Db 를 이용한 게임 서버 제작
버클리Db 를 이용한 게임 서버 제작Vong Sik Kong
 
[NEXT] 화면 재갱신이 되는 안드로이드 앱 만들기 - 네트워크에 독립하는 구조로 변경
[NEXT] 화면 재갱신이 되는 안드로이드 앱 만들기 - 네트워크에 독립하는 구조로 변경[NEXT] 화면 재갱신이 되는 안드로이드 앱 만들기 - 네트워크에 독립하는 구조로 변경
[NEXT] 화면 재갱신이 되는 안드로이드 앱 만들기 - 네트워크에 독립하는 구조로 변경YoungSu Son
 
서버 아키텍처 이해를 위한 프로세스와 쓰레드
서버 아키텍처 이해를 위한 프로세스와 쓰레드서버 아키텍처 이해를 위한 프로세스와 쓰레드
서버 아키텍처 이해를 위한 프로세스와 쓰레드KwangSeob Jeong
 
C#강좌
C#강좌C#강좌
C#강좌e12g
 
TP-Monitor_Wh apm
TP-Monitor_Wh apmTP-Monitor_Wh apm
TP-Monitor_Wh apm엑셈
 
C Language II
C Language IIC Language II
C Language IISuho Kwon
 
LucideWorks Banana 소개
LucideWorks Banana 소개 LucideWorks Banana 소개
LucideWorks Banana 소개 SuHyun Jeon
 
실전 DataSnap!
실전 DataSnap!실전 DataSnap!
실전 DataSnap!Devgear
 
제 5회 D2 CAMPUS SEMINAR - Swift로 만든 serverframework 개발기
제 5회 D2 CAMPUS SEMINAR - Swift로 만든 serverframework 개발기제 5회 D2 CAMPUS SEMINAR - Swift로 만든 serverframework 개발기
제 5회 D2 CAMPUS SEMINAR - Swift로 만든 serverframework 개발기NAVER D2
 

Similar to Java rmi 개발 가이드 (20)

MSA와 infra
MSA와 infraMSA와 infra
MSA와 infra
 
[HaU] 신입 기술 면접 준비 java
[HaU] 신입 기술 면접 준비 java[HaU] 신입 기술 면접 준비 java
[HaU] 신입 기술 면접 준비 java
 
Implementing remote procedure calls rev2
Implementing remote procedure calls rev2Implementing remote procedure calls rev2
Implementing remote procedure calls rev2
 
Showroom 2019-react
Showroom 2019-reactShowroom 2019-react
Showroom 2019-react
 
쇼핑검색 React 전환 경험 공유
쇼핑검색 React 전환 경험 공유쇼핑검색 React 전환 경험 공유
쇼핑검색 React 전환 경험 공유
 
[NDC 2018] 테라 콘솔 포팅기 - 현세대 콘솔 이식을 위한 렌더링 최적화 여정
[NDC 2018] 테라 콘솔 포팅기 - 현세대 콘솔 이식을 위한 렌더링 최적화 여정[NDC 2018] 테라 콘솔 포팅기 - 현세대 콘솔 이식을 위한 렌더링 최적화 여정
[NDC 2018] 테라 콘솔 포팅기 - 현세대 콘솔 이식을 위한 렌더링 최적화 여정
 
Node week1
Node week1Node week1
Node week1
 
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - Http Request
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - Http Request[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - Http Request
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - Http Request
 
All about JDBC Performance Tuning_Wh apm
All about JDBC Performance Tuning_Wh apmAll about JDBC Performance Tuning_Wh apm
All about JDBC Performance Tuning_Wh apm
 
프로그래밍 언어 기초(델파이,C++)
프로그래밍 언어 기초(델파이,C++)프로그래밍 언어 기초(델파이,C++)
프로그래밍 언어 기초(델파이,C++)
 
ice_grad
ice_gradice_grad
ice_grad
 
버클리Db 를 이용한 게임 서버 제작
버클리Db 를 이용한 게임 서버 제작버클리Db 를 이용한 게임 서버 제작
버클리Db 를 이용한 게임 서버 제작
 
[NEXT] 화면 재갱신이 되는 안드로이드 앱 만들기 - 네트워크에 독립하는 구조로 변경
[NEXT] 화면 재갱신이 되는 안드로이드 앱 만들기 - 네트워크에 독립하는 구조로 변경[NEXT] 화면 재갱신이 되는 안드로이드 앱 만들기 - 네트워크에 독립하는 구조로 변경
[NEXT] 화면 재갱신이 되는 안드로이드 앱 만들기 - 네트워크에 독립하는 구조로 변경
 
서버 아키텍처 이해를 위한 프로세스와 쓰레드
서버 아키텍처 이해를 위한 프로세스와 쓰레드서버 아키텍처 이해를 위한 프로세스와 쓰레드
서버 아키텍처 이해를 위한 프로세스와 쓰레드
 
C#강좌
C#강좌C#강좌
C#강좌
 
TP-Monitor_Wh apm
TP-Monitor_Wh apmTP-Monitor_Wh apm
TP-Monitor_Wh apm
 
C Language II
C Language IIC Language II
C Language II
 
LucideWorks Banana 소개
LucideWorks Banana 소개 LucideWorks Banana 소개
LucideWorks Banana 소개
 
실전 DataSnap!
실전 DataSnap!실전 DataSnap!
실전 DataSnap!
 
제 5회 D2 CAMPUS SEMINAR - Swift로 만든 serverframework 개발기
제 5회 D2 CAMPUS SEMINAR - Swift로 만든 serverframework 개발기제 5회 D2 CAMPUS SEMINAR - Swift로 만든 serverframework 개발기
제 5회 D2 CAMPUS SEMINAR - Swift로 만든 serverframework 개발기
 

More from 중선 곽

자바로 배우는 자료구조
자바로 배우는 자료구조자바로 배우는 자료구조
자바로 배우는 자료구조중선 곽
 
오픈소스 프레임워크 기반 웹 서비스 설계 (Example)
오픈소스 프레임워크 기반 웹 서비스 설계 (Example)오픈소스 프레임워크 기반 웹 서비스 설계 (Example)
오픈소스 프레임워크 기반 웹 서비스 설계 (Example)중선 곽
 
프로그래밍 방식의 변천 과정
프로그래밍 방식의 변천 과정프로그래밍 방식의 변천 과정
프로그래밍 방식의 변천 과정중선 곽
 
젠킨스 설치 및 설정
젠킨스 설치 및 설정젠킨스 설치 및 설정
젠킨스 설치 및 설정중선 곽
 
지속적인 통합
지속적인 통합지속적인 통합
지속적인 통합중선 곽
 
Test driven development short lesson
Test driven development   short lessonTest driven development   short lesson
Test driven development short lesson중선 곽
 
Tomcat monitoring using_javamelody
Tomcat monitoring using_javamelodyTomcat monitoring using_javamelody
Tomcat monitoring using_javamelody중선 곽
 
Web service performance_test_using_jmeter_ver1.2
Web service performance_test_using_jmeter_ver1.2Web service performance_test_using_jmeter_ver1.2
Web service performance_test_using_jmeter_ver1.2중선 곽
 
Online service 계층별 성능 모니터링 방안
Online service 계층별 성능 모니터링 방안Online service 계층별 성능 모니터링 방안
Online service 계층별 성능 모니터링 방안중선 곽
 
Intranet query tuning (example)
Intranet query tuning (example)Intranet query tuning (example)
Intranet query tuning (example)중선 곽
 
Db 진단 및 튜닝 보고 (example)
Db 진단 및 튜닝 보고 (example)Db 진단 및 튜닝 보고 (example)
Db 진단 및 튜닝 보고 (example)중선 곽
 
Scale up and scale out
Scale up and scale outScale up and scale out
Scale up and scale out중선 곽
 
컴퓨터 네트워크와 인터넷
컴퓨터 네트워크와 인터넷컴퓨터 네트워크와 인터넷
컴퓨터 네트워크와 인터넷중선 곽
 
자바 직렬화 (Java serialization)
자바 직렬화 (Java serialization)자바 직렬화 (Java serialization)
자바 직렬화 (Java serialization)중선 곽
 
숫자 구분자 처리 (Digit group separators)
숫자 구분자 처리 (Digit group separators)숫자 구분자 처리 (Digit group separators)
숫자 구분자 처리 (Digit group separators)중선 곽
 
서버 아키텍쳐 입문
서버 아키텍쳐 입문서버 아키텍쳐 입문
서버 아키텍쳐 입문중선 곽
 
서버 아키텍쳐 입문
서버 아키텍쳐 입문서버 아키텍쳐 입문
서버 아키텍쳐 입문중선 곽
 
서버 성능에 대한 정의와 이해
서버 성능에 대한 정의와 이해서버 성능에 대한 정의와 이해
서버 성능에 대한 정의와 이해중선 곽
 
Apache ZooKeeper 소개
Apache ZooKeeper 소개Apache ZooKeeper 소개
Apache ZooKeeper 소개중선 곽
 
객체지향 철학 그리고 5대 개념
객체지향 철학 그리고 5대 개념객체지향 철학 그리고 5대 개념
객체지향 철학 그리고 5대 개념중선 곽
 

More from 중선 곽 (20)

자바로 배우는 자료구조
자바로 배우는 자료구조자바로 배우는 자료구조
자바로 배우는 자료구조
 
오픈소스 프레임워크 기반 웹 서비스 설계 (Example)
오픈소스 프레임워크 기반 웹 서비스 설계 (Example)오픈소스 프레임워크 기반 웹 서비스 설계 (Example)
오픈소스 프레임워크 기반 웹 서비스 설계 (Example)
 
프로그래밍 방식의 변천 과정
프로그래밍 방식의 변천 과정프로그래밍 방식의 변천 과정
프로그래밍 방식의 변천 과정
 
젠킨스 설치 및 설정
젠킨스 설치 및 설정젠킨스 설치 및 설정
젠킨스 설치 및 설정
 
지속적인 통합
지속적인 통합지속적인 통합
지속적인 통합
 
Test driven development short lesson
Test driven development   short lessonTest driven development   short lesson
Test driven development short lesson
 
Tomcat monitoring using_javamelody
Tomcat monitoring using_javamelodyTomcat monitoring using_javamelody
Tomcat monitoring using_javamelody
 
Web service performance_test_using_jmeter_ver1.2
Web service performance_test_using_jmeter_ver1.2Web service performance_test_using_jmeter_ver1.2
Web service performance_test_using_jmeter_ver1.2
 
Online service 계층별 성능 모니터링 방안
Online service 계층별 성능 모니터링 방안Online service 계층별 성능 모니터링 방안
Online service 계층별 성능 모니터링 방안
 
Intranet query tuning (example)
Intranet query tuning (example)Intranet query tuning (example)
Intranet query tuning (example)
 
Db 진단 및 튜닝 보고 (example)
Db 진단 및 튜닝 보고 (example)Db 진단 및 튜닝 보고 (example)
Db 진단 및 튜닝 보고 (example)
 
Scale up and scale out
Scale up and scale outScale up and scale out
Scale up and scale out
 
컴퓨터 네트워크와 인터넷
컴퓨터 네트워크와 인터넷컴퓨터 네트워크와 인터넷
컴퓨터 네트워크와 인터넷
 
자바 직렬화 (Java serialization)
자바 직렬화 (Java serialization)자바 직렬화 (Java serialization)
자바 직렬화 (Java serialization)
 
숫자 구분자 처리 (Digit group separators)
숫자 구분자 처리 (Digit group separators)숫자 구분자 처리 (Digit group separators)
숫자 구분자 처리 (Digit group separators)
 
서버 아키텍쳐 입문
서버 아키텍쳐 입문서버 아키텍쳐 입문
서버 아키텍쳐 입문
 
서버 아키텍쳐 입문
서버 아키텍쳐 입문서버 아키텍쳐 입문
서버 아키텍쳐 입문
 
서버 성능에 대한 정의와 이해
서버 성능에 대한 정의와 이해서버 성능에 대한 정의와 이해
서버 성능에 대한 정의와 이해
 
Apache ZooKeeper 소개
Apache ZooKeeper 소개Apache ZooKeeper 소개
Apache ZooKeeper 소개
 
객체지향 철학 그리고 5대 개념
객체지향 철학 그리고 5대 개념객체지향 철학 그리고 5대 개념
객체지향 철학 그리고 5대 개념
 

Java rmi 개발 가이드

  • 1. Java RMI guide (RMI 개발 및 활용 가이드) 2015년 06월 15일 (Sunny Kwak) sunnykwak@daum.net
  • 2. SunnyKwak’s Technical document 2 / 13 Copyrights © 2004Sunny Kwak. All rights reserved. Other disclaimers The names of actual companies and products mentioned herein may be the trademarks of their respective owners. Version Version 1.0 (draft) History 2004년 12월 13일 월요일 First created.
  • 3. SunnyKwak’s Technical document 3 / 13 Table of Contents 1. INTRODUCTION ......................................................................................................... 4 1.1 분산시스템.................................................................................................................. 4 1.2 RMI(REMOTE METHOD INVOCATION)이란? ............................................................................. 4 1.3 WHY RMI? .................................................................................................................. 4 2. RMI에 관해서................................................................................................................ 5 2.1 RMI의 기본 개념 .......................................................................................................... 5 2.2 STUB과 PARAMETER MARSHALLING ....................................................................................... 5 2.3 동적클래스 로등(DYNAMIC CLASS LOADING)........................................................................... 6 3. 자바 RMI 프로그래밍 ...................................................................................................... 7 3.1 자바에서의 RMI 구현 .................................................................................................... 7 3.2 서버 측 프로그래밍 ....................................................................................................... 7 3.3 클라이언트 측 프로그래밍............................................................................................... 9 3.4 RMI 실행 ..................................................................................................................10 3.4.1 서버.....................................................................................................................................................10 3.4.2 클라이언트 ..........................................................................................................................................12 3.5 동적 클래스로딩에 관해 주의할 점 ...................................................................................12
  • 4. SunnyKwak’s Technical document 4 / 13 1. Introduction www.webdox.co.kr 서창근 님의 글 참조 1.1 분산시스템 소프트웨어 개발에서 가장 주목 받고 있는 것이 객체지향 개발이라면 시스템 분야에서 가장 주목 받는 것은 분산시스템의 개념이다. 어쩌면 이 분산시스템의 개념이 객체지향 개발을 절대적으로 필 요하게 했을지도 모른다. 분산시스템이란 기본적으로 다수의 컴퓨터가 하나의 시스템처럼 작동하며 사용자에게 그 차이를 느끼지 못하게 하는 시스템을 말한다. 분산시스템의 중요한 개념은 명령 실행 의 투명성 (transparency), 효율성 (efficiency), 일관성 (consistency), 유연성 (flexibility), 그리고 robustness 등을 들 수 있는데 그 중 가장 많이 강조하는 것이 투명성이다. 즉, 사용자가 어떤 명령을 실행시켰을 경우 그 명령이 지금 사용자가 사용하고 있는 컴퓨터에서 실행되고 있는지, 아니면 네트 워크에 연결된 다른 컴퓨터에서 실행되고 있는지 분간할 수 없음을 말한다. 대표적인 분산시스템으 로 UNIX의 NFS (Network File System), AFS (Andrew File System), CODA 등이 있다. 이러한 시스템에서 중요한 것은 바로 시스템에 소속되어 있는 컴퓨터 간의 통신이다. 더욱 자세하게 말하면 각 컴퓨터에서 실행되고 있는 프로그램, 또는 프로세스들이 서로 통신이 가능해야 한다는 것 이다. 이 프로그램 사이의 통신을 가능하게 하는 것 중 현재 UNIX에서 가장 많이 사용되고 있는 통 신 프로토콜이 바로 RPC (Remote Procedure Call)이다. RPC는 네트워크에 연결되어 있는 다른 컴퓨터 에 존재하는 함수를 사용자가 눈치채지 못하도록 실행해 줌으로써 분산시스템의 구현을 가능하게 했다. 그러나, RPC는 객체지향 개발개념을 구현하지는 않으며 단지 다른 컴퓨터에 저장되어 있는 순 차적인 언어로 개발된 애플리케이션의 함수를 부를 수 있도록 해주는 역할을 할 따름이다. 1.2 RMI(Remote Method Invocation)이란? 위에서 객체지향 개발 개념을 설명할 때 프로그램이 객체 간의 메시지 전송으로 실행된다고 했다. 그렇다면 만약 이 객체들이 서로 다른 컴퓨터에 존재한다면 어떻게 해야 할까? 즉, 예전의 분산시스 템의 개념은 서로 다른 프로그램들이 다른 컴퓨터에 존재하고 실행되는 것이었다면 근래에는 각 객 체가 분산되어 존재하는 개념인 것이다. 특히 현대 시스템 개발은 3계층 시스템 (three-tier system)이 주류를 이루므로 데이터베이스, 애플리케이션, 그리고 클라이언트가 모두 객체를 통하여 구현되고 따로 저장되는 일이 많아졌다. 이렇게 분산되어 존재하는 객체간의 메시지 전송(메소드를 부르는 것 과 동일함)을 가능케 해주는 것이 바로 RMI 프로토콜이다. 그리고, 객체지향 개발 개념을 기본으로 RMI의 기능과 transaction, security등 엔터프라이즈급 기능을 포괄적으로 컴포넌트 모델화한 것이 바 로 EJB (Enterprise JavaBeans)가 되겠다. 일반적으로 자바 RMI라고 하면 자바 애플리케이션 간의 통 신을 가능케 해주는 프로토콜로 생각하나 실제로 RMI 자체는 자바 RMI 이전부터 쓰여지던 단어이 며 더욱 포괄적인 의미를 지닌다. RMI는 분산객체 개념을 구현하는 CORBA 또는 DCOM 등에서 분 산객체 간의 통신을 구현하는 모든 프로토콜을 의미한다. CORBA나 DCOM은 이 글의 범위 밖이므 로 접어두고 이번 글에선 특히 자바 애플리케이션 간의 RMI에 대해 집중적으로 살펴보도록 하자. 1.3 Why RMI? 네트워크 프로그래밍을 해보신 독자라면 곧 이런 의문을 가지게 될 것이다. 왜냐하면 많은 독자들이 지금까지 데이터 전송을 위해서 소켓(socket)을 사용해 왔기 때문이다. 그러나, RMI를 배우고 나면 아마도 최소한 자바 애플리케이션에서 소켓을 사용하고자 하는 개발자는 별로 없을 것이다. 프로그 래밍의 기본은 데이터의 전송이다. RMI는 분산객체 간의 데이터 전송을 메소드를 부르는 것과 같은 방법으로 실현시킨다. 소켓을 다루어 본 독자라면 벌써 알겠지만 메소드를 부르는 것과 소켓으로 데 이터를 보내는 것 중 어느 것이 더 편한가? 당연히 메소드를 부르는 것이 훨씬 더 편하다. 똑같은 데이터 전송 방법의 일환인데 더 어렵고 불편한 방법을 쓸 필요가 없는 것이다.
  • 5. SunnyKwak’s Technical document 5 / 13 2. RMI에 관해서 2.1 RMI의 기본 개념 RMI가 자바에서 어떻게 구현되는지 살펴보기 전에 먼저 RMI가 어떻게 가능한지 살펴보기로 하자. RMI의 기본적인 이론을 먼저 공부하는 것이 나중에 자바 RMI를 공부할 때 이해하기 쉽다. 그리고 뭐든지 그렇지만 기반 이론을 이해하는 것이 나중에 문제가 발생하였을 경우 문제를 알아내고 해결 하기 쉽기 때문이다. RMI의 개념을 자바 중심으로 서술하였지만 다른 RMI 기법들도 이와 비슷하다 고 보면 된다. 그렇다면 도대체 RMI에서 어떻게 한 객체가 다른 컴퓨터에 존재하는 객체에게 메시지를 전송할 수 있을까? 가장 기본적인 문제는 그 객체가 다른 객체의 존재를 알아야 하며, 그 객체에게 메시지를 전송하는 방법을 알아야 하고, 그리고 마지막으로 그 객체로부터 메시지의 답변을 받아야 한다는 것 이다. 이 과정에서 수반되는 문제는 메시지를 전송하고 답변을 받을 때 어떠한 종류의 데이터를 주 고 받아야 하는가 이다. RMI는 위에 나열된 문제를 두 가지의 방법으로 해결한다. 첫째가 스텁(stub) 이며 둘째가 parameter marshalling 이다. 2.2 Stub과 Parameter marshalling 설명을 위해서 메소드를 부 르려고 하는 객체가 존재하 는 컴퓨터를 클라이언트라고 하고 그 메소드를 실행하는 객체가 존재하는 컴퓨터를 서버라고 하자. 일반적인 의 미의 클라이언트와 서버와는 약간 다르므로 혼동하지 말 기를 바란다. 여기선 누가 누 구의 메소드를 부르냐에 따 라 서버와 클라이언트의 역 할이 바뀔 수도 있다. 클라이언트의 객체가 서버에 존재하는 객체의 메소드를 부르려고 할 경우 그 메소드 의 존재를 알려주고 추상적 으로 나타내 주는 것이 바로 스텁이다. 이 스텁은 실제로 인터페이스의 형태로 서버에 있는 객체가 원격적으로 부를 수 있는 메 소드를 공개하는 것으로 서버에 저장되어 있으며 클라이언트의 객체가 서버에 있는 메소드를 부르 려고 할 때 클라이언트로 전달이 된다. 스텁 클래스는 원래 서버의 객체로부터 생성이 되지만 클라 이언트에 머무르면서 클라이언트가 서버의 메소드를 부를 수 있도록 도와준다. EJB에서는 이것을 원 격 인터페이스 (remote interface)라고도 한다. 스텁이 하는 중요한 일은 두 가지가 있다. 첫째는 당연히 서버에 존재하는 메소드를 불러줘야 하고 둘째는 그 메소드의 인자를 일정한 포맷으로 바꿔주는 것이다. 이렇게 인자를 일정한 포맷으로 바꿔 주는 일을 parameter marshalling 이라고 한다. 즉, 인자를 조율하고 정리하는 역할을 하는 것이다. 왜 이런 parameter marshalling 이 필요한 것일까? 예를 들어 어떤 시스템은 정수 데이터를 big-endian으로 표현하고 어떤 시스템은 little-endian으로 표시한다. 정수가 차지하는 메모리의 양도 다를 수 있다. 자바는 플랫폼과는 독립적으로 작동하는 언어이다. 만약 한 객체가 윈도우 시스템에서 작동하고 다
  • 6. SunnyKwak’s Technical document 6 / 13 른 객체는 유닉스 환경에서 작동하고 있다면 이러한 데이터의 구현 방법을 통일해 줄 필요가 있다. 특히 3계층 시스템(three-tier system)의 경우 데이터베이스 서버, 애플리케이션 서버, 그리고 클라이언 트가 일일이 다른 플랫폼으로 구현될 수도 있으므로 데이터의 구현방법을 일관적으로 표현하는 일 은 아주 중요하다. 그 일이 바로 parameter marshalling 이다. Parameter marshalling 이 끝났으면 스텁은 서버에 존재하는 객체의 메소드를 부르게 된다. 이때 스텁 이 서버에 보내는 정보는 서버 객체의 아이디, 실행해야 할 메소드, 그리고 변환된 인자이다. 클라이 언트 쪽에서 스텁이 대리인의 역할을 하듯 서버 쪽에서도 이러한 대리인의 역할을 하는 존재가 있 는데 수신자(receiver) 또는 스켈레톤(skeleton) 이라고도 한다. 수신자가 하는 역할은 스텁의 반대라고 생각하면 된다. 조율된 인자들을 다시 그 서버에 맞는 형태로 바꿔준 뒤 스텁이 보낸 아이디의 객체 가 갖고 있는 알맞은 메소드를 실행시킨다. 그리고 실행이 끝났으면 결과를 다시 parameter marshalling 을 통하여 변환한 뒤, 다시 스텁에게 보내준다. 스텁은 이 결과를 받아서 다시 시스템에 맞게 변환시킨 뒤, 원래 메소드를 불렀던 객체에게 결과를 넘겨준다. 물론 결과가 예외일 수도 있다. 2.3 동적 클래스 로딩(Dynamic class loading) 그렇다면 여기서 또 하나의 의문점이 생기게 된다. 만약 인자가 일반적인 데이터 타입, 즉 숫자나 문자 등일 경우엔 parameter marshalling이 간단하지만 만약 어떠한 객체라면 어떻게 할까? 특히 결과 값이 서버에 존재하고 있는 또 다른 원격객체라면 클라이언트엔 그 객체의 코드가 없으므로 문제는 심각하다. 예를 들어 서버에 Computer라는 객체가 실행되고 있다고 하자. 이 객체의 메소드 중에 getType()이라는 메소드가 있는데 컴퓨터의 종류를 추상화하는 ComputerType이라는 객체를 돌려준다 고 하자. 즉, 클라이언트에서 다음과 같이 실행시킬 수 있다는 얘기가 되겠다. Computer remote_comp = new Computer(); ComputerType mycomp = (ComputerType)remote_comp.getType(); mycomp.someMethod(); 문제는 여기서 getType() 메소드를 불렀을 때 돌아오는 값이 어떤 숫자나 문자가 아니라 서버에 존 재하는 객체라는 것이다. 그리고 클라이언트는 이 새로운 원격객체의 someMethod()라는 메소드를 불 러주고 있다. 물론 클라이언트는 함수 값으로 돌아오는 객체가 ComputerType이라는 객체라는 것은 알지만 실제로 돌아오는 객체는 ComputerType의 서브클래스일 수도 있다. 게다가 이 새로운 객체가 서버에 정의돼 있는 새로운 예외 객체를 필요로 할 수도 있다. 물론 이 새로운 객체의 someMethod() 라는 메소드를 부르려면 클라이언트가 이 새로운 객체에 대해서 알고 있어야 한다. 이 문제를 해결 하기 위해 클라이언트에 이 객체를 정의해주고 저장하는 방법도 있고 서버와 동시에 일일이 클라이 언트를 업데이트 해주는 것도 방법이겠지만 새로운 종류의 ComputerType을 서버에 정의할 때마다 일일이 클라이언트를 업데이트 할 수는 없다. 그래서 필요한 것이 바로 동적 클래스 로딩이다. 동적 클래스 로딩은 이렇게 클라이언트가 알지 못하는 객체가 필요할 경우 서버에서 그때 그때 필 요할 때마다 클라이언트로 그 필요한 객체의 스텁클래스를 불러오는 것을 말한다. 자바의 애플릿과 비슷한 개념이라고 생각하면 되겠다. 위의 예제의 경우 자동으로 ComputerType의 서브클래스의 스텁 클래스가 클라이언트로 로딩되어 someMethod()를 부르는 것을 가능케 한다. 하지만 이렇게 다른 컴 퓨터에서 네트워크를 통하여 스텁클래스를 불러올 경우 보안의 문제가 발생할 수 있으므로 실제로 이러한 시스템을 가동시킬 땐 꼭 보안에 신경을 써서 자바 보안 매니저 (Java Security Manager)등을 사용하는 것이 좋다.
  • 7. SunnyKwak’s Technical document 7 / 13 3. 자바 RMI 프로그래밍 3.1 자바에서의 RMI 구현 자바 RMI를 구현하기 위해선 일단 클라이언트와 서버에 적절한 코딩을 해야 하는데 자바 RMI는 개 발자에게 편리하게 개발되었으므로 실제로 코딩은 중요한 문제가 아니다. 하지만, RMI라는 것이 근 본적으로 간단한 문제가 아니므로 실제로 RMI를 실행시킬 수 있는 환경을 만드는 것이 실제 코딩 보다 더 복잡하다. 3.2 서버 측 프로그래밍 클라이언트 쪽에서는 서버에 존재하는 객체와 통신을 하기 위해서 인터페이스를 정의해 주어야 한 다. 클라이언트의 객체가 서버의 객체가 가지고 있는 메소드를 부를 때 이 인터페이스를 사용하여 부르게 된다. 이 인터페이스는 서버와 클라이언트 양쪽에 존재하게 된다. 예를 들어 비행기 좌석을 예약하는 프로그램을 만든다고 하자. 만약 이 좌석을 서버에 존재하는 객체로 표현하고 클라이언트 가 이 좌석이 현재 빈 좌석인지 알아보려고 한다고 하자. 그렇다면 일단 다음과 같이 인터페이스를 정의해주어야 한다. import java.rmi.Remote; import java.rmi.RemoteException; interface Seat extends Remote { public boolean isReserved(int seat_number) throws RemoteException; } 여기서 주의해야 할 것은 모든 원격 인터페이스는 Remote 클래스를 상속하여야 하고 원격적으로 공 개되는 메소드는 모두 RemoteException 예외클래스를 던져야 한다는 것이다. 이렇게 인터페이스를 정의했으면 다음엔 실제 객체를 구현해야 한다. import java.rmi.RemoteException; public class SeatImpl implements Seat { /** * 상위 클래스에서 RemoteException이 발생하기 때문에 생성자 정의 */ public SeatImpl() throws RemoteException { } public boolean isReserved( int seat_number ) throws RemoteException { return false; }
  • 8. SunnyKwak’s Technical document 8 / 13 } 다음으로 해야 할 것이 객체의 이름을 RMI Registry에 등록한 후 클라이언트 측에 서비스하는 것이 다. JNDI (Java Naming and Directory Interface)의 일환이기도 한데 클라이언트에 있는 객체에게 서버 객 체의 존재를 알려주는 역할을 한다. 즉, 서버 객체를 어떠한 이름 하에 등록하여 클라이언트의 객체 가 그 이름으로 원격 객체를 찾을 수 있도록 해주는 것을 말한다. 클라이언트는 이 이름으로 서버 객체를 찾으며 찾았을 경우 그 이름에 해당하는 서버 객체의 스텁 클래스가 클라이언트로 전송되어 서버 객체의 메소드를 부르는 것을 가능케 한다. import java.io.IOException; import java.io.BufferedReader; import java.io.InputStreamReader; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.NotBoundException; import java.rmi. RMISecurityManager; import java.rmi.server.UnicastRemoteObject; import java.rmi.registry.Registry; import java.rmi.registry.LocateRegistry; import java.net.MalformedURLException; public class RmiServer { /** * RmiServer 프로그램의 main() 메소드. */ public static void main( String args[] ) { // 보안 매니저를 설정한다. // RMI 보안에 위배되지 않으려면 반드시 설정. if( System.getSecurityManager() == null ) System.setSecurityManager( new RMISecurityManager() ); try { // 이름을 등록한다. SeatImpl si = new SeatImpl(); UnicastRemoteObject.exportObject( si ); // make object findable Registry r = LocateRegistry.getRegistry( "localhost", 1099 ); r.rebind( "seat", si ); // Serve clients BufferedReader rdr = new BufferedReader( new InputStreamReader(System.in) );
  • 9. SunnyKwak’s Technical document 9 / 13 while(true) { System.out.println( "Type EXIT to shutdown the server." ); if( "EXIT".equals(rdr.readLine().toUpperCase()) ) break; } // unregister object r.unbind("rendition"); // unexport object UnicastRemoteObject.unexportObject( rfi, true ); } catch(Exception e ) { e.printStackTrace(); } } } 위의 예제를 보면 seat이라는 이름 하나만 등록한 것을 알 수 있다. 실제로 비행기 좌석 예약 시스 템을 만든다고 하자. SeatImpl 이라는 서버 객체가 만약에 좌석 하나하나를 구현한다고 하면 그 좌석 하나하나를 다 등록할 수는 없는 일이다. 왜냐하면 이 이름은 클라이언트에게 서버 객체의 존재를 알게 해주는 것이므로 클라이언트가 어떠한 메소드를 부르기 전에 벌써 등록이 되어 있어야 한다. 하지만 여기서 bind() 메소드의 인자 중 하나가 바로 SeatImpl 객체이다. 그렇다면 좌석 하나 하나의 객체를 다 생성해서 그 이름들을 따로 등록해야 한다는 예기가 된다. 특히 좌석의 데이터가 데이터 베이스에 저장되어 있을 경우 그것을 일일히 동적으로 불러와야 하는데 그렇다면 클라이언트가 메 소드를 부르기 전에 그 이름을 등록한다는 것은 불가능하다. 이것은 상당히 비효율적인 개발방법이 다. 여기선 SeatImpl 객체가 한 비행기의 모든 좌석을 구현하게 개발하는 것이 더욱 효율적이며 RMI Registry도 더 간단해진다. seat 이라는 이름을 통하여 클라이언트에게 서버객체의 존재를 알려주고 SeatImpl 이라는 서버객체가 따로 각 좌석의 데이터를 데이터베이스로부터 불러오는 것이 더 효율적 인 것이다. 게다가 RMI Registry는 모든 객체들이 공유하는 서비스이므로 한 프로그램이 다 독차지해 서도 안된다. 3.3 클라이언트 측 프로그래밍 위와 같이 서버 쪽의 프로그래밍이 되어 있으면 이젠 클라이언트 쪽을 구현하면 된다. 일단 위에서 코딩한 인터페이스 파일이 필요하고 그 다음엔 그 인터페이스를 이용하여 서버 객체의 메소드를 불 러주면 되겠다. import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.RMISecurityManager; public class SeatClient
  • 10. SunnyKwak’s Technical document 10 / 13 { public boolean getReserved() throws Exception { String strResult; // 먼저 원격 객체를 RMI Registry에서 찾아본다. Seat st = (Seat)Naming.lookup("rmi://localhost/seat"); // 원격 객체를 찾는데 성공했으면 메소드를 호출 return st. isReserved (1); } /** * SeatClient 프로그램의 main() 메소드. */ public static void main( String args[] ) { try { SeatClient sc = new SeatClient(); System.out.println( sc.getReserved() ? “reserved”, “not” ); } catch( Exception e ) { e.printStackTrace(); } } } 먼저 클라이언트 프로그램이 실행되기 시작될 때 자바 보안매니저를 꼭 실행시키도록 하자. 만약 클 라이언트가 자바 애플릿일 경우엔 자동으로 보안이 실행되므로 상관없지만 자바 애플리케이션이라 면 꼭 실행시켜 주도록 한다. 그리고 서버객체의 이름을 찾을때 URL을 사용하는것을 기억하도록 하 자. "rmi://서버이름/등록된 객체이름" 의 형식이면 되겠다. 3.4 RMI 실행 위와 같이 코딩이 끝났으면 이젠 프로그램을 실행하는 일만 남았다. 그러나 자바 RMI를 실행하기 위해선 약간의 복잡한 준비가 필요하다. 이렇게 서버와 클라이언트 사이를 연결해주는 고리가 필요 한데 그것이 바로 rmiregistry 이다. rmiregistry 는 RMI를 사용하는 객체의 등록을 관장하는 역할을 한 다. 이제부터 rmiregistry 의 사용방법과 함께 서버와 클라이언트가 RMI를 실행하려면 어떠한 과정을 거쳐야 하는지 알아보도록 하자. 3.4.1 서버 1. 서버 객체와 인터페이스 파일 컴파일하기 2. rmic 를 이용하여 스텁 클래스 생성 3. 보안 정책 파일 작성
  • 11. SunnyKwak’s Technical document 11 / 13 4. rmiregistry 실행 5. 서버 프로그램 실행 위에 나열한 것과 같이 실행하면 RMI를 사용할 수 있게 된다. 일단 인터페이스와 서버 프로그램을 자바 컴파일러로 컴파일 한다. 처음에 서술했듯이 클라이언트 객체가 서버 객체를 사용하려면 스텁 클래스가 있어야 한다. 이 스텁 클래스를 생성하는 것이 rmic 프로그램이 되겠다. rmic 로 생성된 스 텁클래스가 동적 클래스로딩을 통해서 클라이언트가 필요로 할 때 클라이언트로 옮겨 가게 되는 것 이다. rmic 는 다음과 같이 실행시키면 된다. (자바 1.2 이전 버전일 쓸일이 없다면, -v1.2 옵션을 사용 하길 권장한다.) rmic –v1.2 SeatImpl 클래스 이름은 패키지 이름까지 다 사용하여야 한다. 만약에 SeatImpl 클래스가 com.myairline 이라는 패키지의 일부라면 rmic com.myairline.SeatImpl 이라고 해야 한다. rmic 를 실행시키면 앞에서 말한 바와 같이 스텁 클래스 SeatImpl_Stub.class 라는 이름으로 생성된다. 이렇게 스텁을 만들었으면 다음은 rmiregistry 를 실행해야 한다. 윈도우즈 서버에서는 start rmiregistry 유닉스나 리눅스에선 rmiregistry & 과 같이 실행 시켜주면 된다. 여기서 rmiregistry 의 역할은 명확할 것이다. 클라이언트가 서버의 이 름과 객체의 이름으로 서버 객체를 찾으면 그 요구가 rmiregistry 로 전달된다. 서버 측에선 rmiregistry 에 서버 객체가 등록되어 있으므로 그 서버 객체의 스텁 클래스가 클라이언트로 로딩된 다. 그러면 클라이언트 객체는 그 스텁 클래스를 이용하여 서버 객체의 메소드를 부를 수 있게 된다. 다음은 보안정책을 설정해주는 파일을 만들어야 한다. 파일 이름은 아무래도 상관없다. 여기선 security.policy 라고 하겠다. 위에서 자바 보안매니저를 실행시킨 것을 기억할 것이다. 자바 보안매니 저는 기본적으로 어떠한 네트웍 연결도 허용하지 않는다. 그러므로 이러한 보안정책을 설정해 주어 야 하는 것이다. 보안정책 파일엔 다음과 같이 해주면 되겠다. grant { permission java.net.SocketPermission "*:1024-65535", "connect,listen"; };
  • 12. SunnyKwak’s Technical document 12 / 13 여기선 RMI가 포트번호 1024에서 65535 사이의 아무 포트나 사용할 수 있도록 하였다. RMI의 기본 설정은 1099이다. 이렇게 rmiregistry 가 실행되고, 보안 정책 파일을 작성한 후 서버 프로그램을 실행하여야 한다 여기서 주의해야 할 것은 서버 프로그램을 서비스 프로그램으로 실행시켜야 한다는 것이다. 또한, RMI 서버 측에서 스텁 클래스 파일을 동적 로딩할 수 있도록, codebase 인자를 명령줄에 추가해야 하며, 보안 정책 파일을 지정해야 한다. 위의 예제에선 start java -Djava.security.policy=security.policy -Djava.rmi.server.codebase=file:/{serverlocation}/ SeatImpl 와 같이 실행시켜야 한다. 3.4.2 클라이언트 1. 클라이언트와 인터페이스 파일 컴파일 하기 2. 클라이언트 프로그램의 실행 서버와 마찬가지로 클라이언트 객체의 자바 파일과 인터페이스 파일을 자바 컴파일러로 컴파일 해 준다. 여기서 인터페이스 파일은 서버에서 컴파일한 것과 같은 파일이어야 한다. 이렇게 보안정책이 설정되었으면 프로그램을 실행시키는 일만이 남았다. 프로그램은 다음과 같이 실 행시키면 된다. 보안정책 파일을 지정해 주는 것을 잊지 않도록 하자. java SeatClient -Djava.security.policy=security.policy 3.5 동적 클래스 로딩에 관해 주의할 점 위에서 자바 RMI의 개념 중에 동적 클래스 로딩이라는 것이 있었다. 클라이언트가 필요로 하는 스 텁 클래스를 그때 그때마다 서버에서부터 받아오는 것을 말한다. 이 동적 클래스 로딩 때문에 실제 로 프로그램을 개발할 때 주의해야 할 사항이 몇 가지 있으므로 여기서 짚고 넘어가려고 한다. 위에서 언급한대로 클라이언트 객체는 원격 인터페이스를 통하여, 즉 스텁 클래스를 통하여 서버 객 체의 메소드를 부르게 된다. 다시 말하면 클라이언트는 원격 인터페이스에 공개되어 있는 메소드 밖 에 부를 수 없다는 말이다. 서버객체 내에 다른 여러 가지 메소드가 있을 수 있다. 하지만 원격 인 터페이스에 공개되어 있지 않으면 클라이언트 객체는 그 메소드를 부를 수 없다. 예를 들어 클라이언트 객체가 서버 객체의 어떠한 메소드를 불렀는데 그 값으로 원격 객체를 상속 한 어떠한 객체가 돌아왔다고 하자. 그리고 원격 객체는 원격 인터페이스를 구현하지만 그 원격 객 체를 상속한 객체는 원격 인터페이스를 구현하지 않는다고 하자. 그렇다면 클라이언트 객체가 받은 이 객체는 어떠한 메소드의 실현이 가능할까? 답은 수퍼 클래스가 구현한 원격 인터페이스에 공개 되어 있는 메소드 뿐이다. 상속한 클래스가 다른 어떤 메소드를 정의했을지라도 그것은 클라이언트 가 실행시킬 수 없다. 이것은 그 객체가 메소드의 인자로 사용되던 돌아오는 값으로 사용되던 마찬 가지다. 그렇다면 원격 인터페이스를 구현하지 않는 다른 클래스들은 어떻게 될까? 예를 들어 서버 객체의 메소드를 부를 때 String 클래스를 인자로 사용했다고 하자. 이럴 경우 String 클래스는 Remote 인터 페이스를 구현하지 않으므로 실제론 객체가 복사가 되어 서버로 보내지게 된다. 만약 서버가 String 객체의 어떠한 메소드를 사용한다고 해도 객체 자체가 복사가 되어 보내졌으므로 문제가 없다.
  • 13. SunnyKwak’s Technical document 13 / 13 간단히 말하면 원격 인터페이스를 구현하는 객체가 인자나 메소드 값으로 보내질 경우 스텁 클래스 만이 보내지고 원격 인터페이스를 구현하지 않는 객체를 보낼 경우 객체 자체가 복사되어 보내진다 는 얘기가 되겠다.