기술 백서 Delphi 2010 DataSnap 밥 슈워트(Bob Swart) – 밥 슈워트 교육&컨설팅 (eBob42) 2009년 12월 Corporate Headquarters Asia-Pacific Headquarters DEVGEAR 100 California Street, 12th Floor L7. 313 La Trobe Street 서울특별시 서초구 San Francisco, California 94111 Melbourne VIC 3000 반포동 743-14 Australia 4층 데브기어
54
Embed
Delphi 2010 DataSnap - devgear.co.kr · Delphi 2010의 새로운 DataSnap . 2.1.1.1.1. TDSServer . TDSServer 컴포넌트는 AutoStart, HideDSAdmin, Name, Tag 등 네 개의 속성들을
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
100 California Street, 12th Floor L7. 313 La Trobe Street 서울특별시 서초구
San Francisco, California 94111 Melbourne VIC 3000 반포동 743-14
Australia 4층 데브기어
Delphi 2010의 새로운 DataSnap
DataSnap 히스토리
델파이 3 에서 처음 등장한 MIDAS는 델파이 4와 델파이 5에서 각각 MIDAS II 와 MIDAS III로
업그레이드되었으며, COM에 기초하는 리모트 데이터 모듈을 작성하는 강력한 방법으로서 TCP/IP, HTTP
와 COM (DCOM) 연결 방법을 제공했었습니다. 델파이 6에서는 MIDAS가 DataSnap이라는 이름으로
바뀌었으며, 이 프레임워크는 델파이 2007까지 거의 그대로였습니다.
델파이 2009에서는 DataSnap에 새로운 아키텍처를 도입하여 COM에 대한 의존성을 배제하고 더 가벼운
연결 방식을 사용하는 리모트 서버를 개발할 수 있게 하였습니다. 이 DataSnap 2009에서는 오직 TCP/IP
통신만을 사용할 수 있었지만, 델파이 프리즘 2009로 .NET 클라이언트를 작성 할 수 있는 기능이
추가되었습니다.
델파이 2010에서는 DataSnap 2009의 기반에 새로운 기능들을 추가하여 이 프레임워크를 확장하였습니다.
2010 버전에서는 새 애플리케이션 타입들을 선택할 수 있는 두 개의 위저드 (일반 애플리케이션 / 윈도우
서비스 / 콘솔, ISAPI / CGI / Web App Debugger), HTTP/HTTPS 통신 프로토콜, HTTP 인증, 클라이언트
콜백 함수, REST 와 JSON의 지원, 압축을 지원하는 필터 등의 기능들이 추가되었습니다.
1.1 DataSnap 예제 데이터
이 문서에서는 여러분과 함께 여러 예제를 작성해볼 것입니다. 델파이에서는 DBX4, dbGo for ADO 등
여러 가지 데이터 액세스 기술들로 여러 데이터베이스들을 지원하지만, 편의상 DBX4와 BlackfishSQL 데이터베이스, 그리고 employee.jds 데이터베이스 파일을 사용할 것입니다.
이 파일은 Windows XP의 경우에는 C:\Documents and Settings\All Users\Documents\RAD Studio\7.0\Demos\database\databases\BlackfishSQL 위치에, Windows Vista나 Windows 7의
경우에는 C:\Users\Public\Documents\RAD Studio\7.0\Demos\database\databases \BlackfishSQL 위치에 있습니다. 아래의 스크린샷들을 보면 알겠지만, 저는 운영체제로 Windows 7
프로페셔널을 사용하고 있으며, 추가적으로 DataSnap ISAPI 서버를 테스트하기 위해 Windows Server
2008 웹 에디션을 사용합니다.
2. DataSnap Windows 애플리케이션
DataSnap 2010 은 VCL 폼 애플리케이션, 윈도우 서비스 애플리케이션, 그리고 콘솔 애플리케이션 등 3
가지 윈도우 애플리케이션 타입들을 지원합니다. 여기서는 각각의 장점, 차이점과 이러한 타입들을
사용하기 위한 최적의 경우 등을 살펴보겠습니다.
지금부터 만들어 볼 DataSnap 서버 및 클라이언트 예제에서는 TDSServer, TDSServerClass,
디자인 영역을 보여줍니다. 데이터 액세스 컨트롤 등의 넌비주얼 컴포넌트들을 배치할 수 있습니다.
3장에서는 데이터 액세스 컴포넌트를 배치할 것이지만, 지금은 디자인 영역을 비워두고 TServerMethods1
클래스에 메소드만 추가합니다.
만일 프로젝트 그룹에 추가적인 타겟으로 DataSnap 콘솔 애플리케이션이나 DataSnap 윈도우 서비스
애플리케이션을 추가하지 않을 것이라면, 서버 메소드의 구현을 착수하는 2.1.4 장으로 넘어가셔도 됩니다.
2.1.2. 멀티-타겟프로젝트 그룹 – 콘솔 애플리케이션
프로젝트 그룹으로 돌아와서, 이번에는 콘솔 애플리케이션으로 두 번째 타겟을 추가합니다: 프로젝트 그룹
노드에서 오른쪽 마우스를 클릭하여, “ Add New Project” 를 선택합니다. 오브젝트 리포지토리에서,
다시 DataSnap 서버 페이지로 가서 DataSnap 서버 아이콘을 더블 클릭합니다. 이번에는, New DataSnap
Server 다이얼로그에서 콘솔 애플리케이션을 선택합니다.
여기서 어떤 옵션들을 선택할지는 중요하지 않습니다. DataSnapServer 프로젝트의
ServerContainerUnitDemo.pas 및 ServerMethodsUnitDemo.pas 유닛을 재사용할 것이기 때문입니다.
OK를 클릭하여 새 프로젝트를 생성합니다. ServerContainerUnit2.pas 와 ServerMethodsUnit2.pas를
추가하여 다시 Project1.dproj를 작성합니다. 프로젝트 매니저에서 ServerMethodsUnit2.pas 유닛에서
오른쪽 마우스를 클릭하고 프로젝트에서 삭제합니다. 이 유닛에서 메소드 하나를 복사해야 하기 때문에
ServerContainerUnit2.pas를 당분간 보관합니다. 프로젝트를 DataSnapConsoleServer.dproj로
저장합니다.
ServerContainerUnitDemo.pas 와 새롭게 작성된 ServerContainerUnit2.pas (콘솔 애플리케이션용)의
내용을 비교해보면, RunDSServer라는 글로벌 프로시저가 포함되어 있다는 것을 볼 수 있을 것입니다. 이
전역 프로시저는 콘솔 애플리케이션에만 사용 가능하므로, DataSnapConsoleServer.dproj 프로젝트 소스
코드 안의 메인 블록의 begin 바로 앞에 프로시저를 복사하여 붙여 넣습니다.
12Embarcadero Technologies / DEVGEAR
Delphi 2010의 새로운 DataSnap
코드 몇 부분에서 에러 인사이드(에러가 있는 부분을 표시해주는 붉은 색 밑줄) 표시가 나타날 것입니다.
DataSnapConsoleServer.dpr의 uses 절에 Windows 유닛을 추가하면 문제가 해결됩니다. 이제
DataSnapConsoleServer 는 아래와 같이 될 것입니다.
program DataSnapConsoleServer; {$APPTYPE CONSOLE} uses SysUtils, Windows, ServerContainerUnit2 in 'ServerContainerUnit2.pas' {ServerContainer2: TDataModule}; procedure RunDSServer; var LModule: TServerContainer2; LInputRecord: TInputRecord; LEvent: DWord; LHandle: THandle; begin Writeln(Format('Starting %s', [TServerContainer2.ClassName])); LModule := TServerContainer2.Create(nil); try LModule.DSServer1.Start; try Writeln('Press ESC to stop the server'); LHandle := GetStdHandle(STD_INPUT_HANDLE); while True do begin Win32Check(ReadConsoleInput(LHandle, LInputRecord, 1, LEvent)); if (LInputRecord.EventType = KEY_EVENT) and LInputRecord.Event.KeyEvent.bKeyDown and (LInputRecord.Event.KeyEvent.wVirtualKeyCode = VK_ESCAPE) then break; end; finally LModule.DSServer1.Stop; end ; finally LModule.Free; end; end; begin try RunDSServer; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end end.
아직 간단한 3 가지 작업을 더 해야 합니다. DataSnapConsoleServer 프로젝트가 아직
ServerContainerUnit2.pas를 uses 하고 있으므로, 지금 이 유닛을 삭제하겠습니다. 프로젝트 매니저에서
소스 위에서 오른쪽 마우스를 클릭하고 “ Remove From Project” 를 선택합니다. 다음으로
DataSnapConsoleServer.exe 노드에서 오른쪽 마우스를 클릭하여, “ Add” 를 선택하고
ServerContainerUnitDemo.pas 와 ServerMethodsUnitDemo.pas을 선택하여 프로젝트에 추가합니다.
그 결과, TServerContainer2를 참조하는 모든 곳에서 문법 에러로 표시될 것입니다.
ServerMethodsUnitDemo.pas는 TServerContainer1 타입을 선언해야 하며, 따라서 이 마지막 문제를
13Embarcadero Technologies / DEVGEAR
Delphi 2010의 새로운 DataSnap
해결하기 위해서 DataSnapConsoleServer의 소스 코드에서 TServerContainer2를 TServerContainer1로
이름을 변경해야 합니다. (전체적으로 3곳 수정)
이제 원래의 DataSnapServer.dproj 프로젝트뿐만 아니라 새로운 DataSnapConsoleServer.dproj
프로젝트도 컴파일할 수 있게 되었습니다. 두 프로젝트에서 서로 ServerMethodsUnitDemo.pas 유닛과
ServerContainerUnitDemo.pas 를 공유합니다.
2.1.3. 멀티 타겟 프로젝트 그룹 – 윈도우 서비스
이제 DataSnap VCL 폼 서버와 DataSnap 콘솔 서버 프로젝트를 한 프로젝트 그룹에 포함시켰으니, 이제
DataSnap 윈도우 서비스 애플리케이션 하나가 남았습니다. 이 타겟을 추가하기 위해서, 프로젝트
그룹에서 오른쪽 마우스를 클릭하여 “ Add New Project” 을 선택한 후, 오브젝트 리포지토리에서 New
DataSnap Server 아이콘을 다시 더블 클릭합니다. 이번에는 서비스 애플리케이션을 선택하고 통신
프로토콜 옵션 (TCP/IP, HTTP, HTTP 인증) 또한 선택합니다. 곧 살펴보겠지만, 서비스 애플리케이션에
대한 서버 컨테이너는 VCL Forms 나 콘솔 애플리케이션 측 서버 컨테이너와는 조금 다릅니다. 그래서
여기 있는 모든 프로토콜 옵션을 선택하여야 합니다.
New DataSnap Server 다이얼로그의 결과로 ServerContainerUnit과 ServerMethodsUnit이 추가된 새로운
프로젝트가 생성됩니다. ServerMethodsUnit은 전에 본 것과 동일하므로 새 프로젝트에서 삭제하고 VCL
Forms와 콘솔 DataSnap 애플리케이션에서 공통으로 사용하고 있는 ServerMethodsUnitDemo.pas로
대체합니다.
반면, 새로운 유닛 ServerCotainerUnit1.pas는 다릅니다. TDSServer, TDSServerClass 와 트랜스포트
컴포넌트를 포함하는 데에 TDataModule를 사용하지 않고, TService로부터 파생되는 새로운 클래스에서
DataSnap 컴포넌트들을 포함합니다. 또한 TService로부터 파생되는 것과는 별도로 서비스의 Stop,
Pause, Continue 와 Interrogate 이벤트를 구현하기 위한 4 개의 특별한 메소드가 추가되어 있습니다.
type TServerContainer3 = class(TService) DSServer1: TDSServer; DSTCPServerTransport1: TDSTCPServerTransport; DSHTTPService1: TDSHTTPService; DSHTTPServiceAuthenticationManager1: TDSHTTPServiceAuthenticationManager; DSServerClass1: TDSServerClass; procedure DSServerClass1GetClass(DSServerClass: TDSServerClass; var PersistentClass: TPersistentClass); procedure ServiceStart(Sender: TService; var Started: Boolean); private { Private declarations } protected function DoStop: Boolean; override; function DoPause: Boolean; override; function override; DoContinue: Boolean; procedure DoInterrogate; override; public function GetServiceController: TServiceController; override; end;
바꾸어 말하면, 윈도우 서비스 프로젝트에서는 원래의 유닛 ServerContainerUnitDemo.pas를 공유할 수
없습니다. ServerContainerUnit1.pas를 ServerContainerUnitServiceDemo.pas로 이름 바꾸어야 합니다.
다음으로, 프로젝트 자체를 DataSnapServiceServer.dproj로 저장합니다.
ServerContainerUnitServiceDemo에서 예전의 유닛 ServerMethodsUnit2.pas를 참조부분을 찾아
ServerMethodsUnitDemo.pas로 수정해야 합니다. 그래서 3 개의 타겟 모두에서 같은 서버 메소드를
사용하게 됩니다.
14Embarcadero Technologies / DEVGEAR
Delphi 2010의 새로운 DataSnap
2.1.4. 서버 메소드
일단 ServerMethodsUnitDemo 유닛이 포함된 DataSnap 서버 프로젝트가 준비되었다면, 이제 서버
메소드에 대해 좀 더 자세히 알아보기로 합시다.
앞에서 언급한 것처럼, TServerMethod1 클래스는 RTTI를 이용하여 DataSnap 클라이언트에게
메소드들을 노출하는 DataSnap 서버 객체입니다. 여러분이 “ Include sample methods” 옵션을
체크하였다면 클래스에 샘플 메소드인 EchoString 함수가 추가되었을 것입니다. 이제 다른 함수로서
DataSnap 서버 컴퓨터로부터 현재 시간을 반환하는 함수를 추가해봅시다. 그러기 위해, 다음과 같이
수정하여 TServerMethods1의 정의 부분에 2개의 퍼블릭 메소드를 추가합니다.
type TServerMethods1 = class(TDSServerModule) private { Private declarations } public { Public declarations } function EchoString(Value: string): string; function ServerTime: TDateTime; end;
ServerTime 메소드의 구현은 예제로 제공된 EchoString 메소드만큼이나 매우 간단합니다.
function TServerMethods1.EchoString(Value: string): string; begin Result := Value; end; function TServerMethods1.ServerTime: TDateTime; begin Result := Now; end;
이제 이 서버 애플리케이션을 컴파일하고 실행합니다. 여러 프로젝트 타겟으로 구성했다면 바로
테스트하기 가장 편리한 것은 DataSnapServer 실행파일입니다. 윈도우 버전과 보안 설정 수준 에 따라
DataSnapServer 애플리케이션의 일부 기능을 막고 있다는 것을 알려주는 윈도우 보안 경고를 보게 될
수도 있습니다.
이것은 DataSnapServer 애플리케이션이 TCP/IP와 HTTP로 요청을 리스닝하고 있기 때문입니다. “ Allow
access” 버튼을 클릭하여 액세스를 허용하도록 하고, 이제 DataSnap 서버가 제대로 실행됩니다.
15Embarcadero Technologies / DEVGEAR
Delphi 2010의 새로운 DataSnap
2.2. DataSnap 클라이언트
첫 번째 DataSnap 서버가 실행되어 들어오는 요청을 리스닝하는 상태가 되었으니, 이제 클라이언트를
작성할 때가 되었습니다. 이 장에서는, 어떻게 클라이언트에서 서버로 연결할 수 있는지, 어떻게 서버
클래스를 생성하여 메소드를 임포트할 수 있는지를 설명하겠습니다.
DataSnap 서버가 실행되고 있으므로 DataSnap 클라이언트 애플리케이션 프로젝트를 작성 할 수
있습니다. 디자인타임에 DataSnap 서버 프로젝트와 DataSnap 클라이언트 프로젝트를 쉽게 스위치 하기
위해, DataSnap 클라이언트 프로젝트를 같은 프로젝트 그룹에 추가합니다. 어떠한 프로젝트도 DataSnap
클라이언트가 될 수 있지만, 이번 데모에서는 VCL Forms Application을 선택하여 프로젝트를 생성하고
메인 폼을 ClientForm.pas로, 프로젝트를 DataSnapClient.dpr로 저장합니다.
툴 팔레트의 DataSnap Server 페이지에는 6 개의 DataSnap 서버 컴포넌트들이 있지만, DataSnap Client
페이지에는 우리가 지금 사용할 컴포넌트들이 포함되어 있지 않습니다.
DataSnap Client 페이지에 있는 컴포넌트들은 "예전의" DataSnap
컴포넌트들로서, 지금도 사용할 수는 있으나 이제는 DataSnap을
사용하는 데 있어 추천할 만한 방법은 아닙니다.
여기에는 한 가지 예외가 있습니다. DataSnap 2010에서 새로 추가된
TDSProviderConnection 컴포넌트는 "이전의" DataSnap 서버를 새로운
DataSnap 클라이언트와 연결하기 위해서 사용할 수 있습니다.
(3.2장에서 보여드릴 것입니다)
DataSnap Client 페이지 대신, 우리는 dbExpress 페이지에서 TSqlServerMethod라는 새로운 컴포넌트를
찾아보아야 합니다. (아래 그림에서 보면 기존의 다른 dbExpress 컴포넌트들은 TSQL로 시작하지만
TSqlServerMethod는 유일하게 TSql로 시작하기 때문에 금방 알아볼 수 있을 것입니다)
TSqlServerMethod 컴포넌트는 DataSnap 서버에 있는 원격의 메소드를
호출하기 위하여 사용되지만, 먼저 DataSnap 서버가 연결될 필요가
있습니다.
서버와의 연결에는 예전의 TxxxConnection 컴포넌트들이 아닌
TSQLConnection 컴포넌트를 사용됩니다. 이렇게 하기 위해,
TSQLConnection 컴포넌트의 Driver 속성의 드롭다운 목록에
DataSnap이라는 새로운 드라이버 이름을 선택할 수 있습니다.
따라서, TSQLConnection 컴포넌트를 ClientForm 위에 놓고, Driver
속성을 DataSnap으로 지정합니다. 그 결과로, 오브젝트 인스펙터에서
Driver 속성의 이름 왼편에 플러스 기호가 표시되며 속성을 펼쳐서 모든
하위 속성들을 볼 수 있습니다.
16Embarcadero Technologies / DEVGEAR
Delphi 2010의 새로운 DataSnap
CommunicationProtocol 속성은 기본적으로 비어 있으며, TSQLConnection은 이 상태에서는 프로토콜로
TCP/IP를 사용할 것입니다 (지정된 포트 211를 통해 통신).
서버 측과 똑같이 BufferKBSize은 디폴트값인 32 (KB)로, Port 는 디폴트값인 211로 지정되어 있습니다.
실제 상황에서는, 포트 211은 DataSnap 서버와 연결하는 포트로 잘 알려져 있으므로, 저는 항상 포트
번호를 211에서 다른 값으로 변경합니다 (서버 측과 클라이언트 측 양쪽 모두)
HostName, UserName과 Password는 DataSnap 서버와 연결하기 위해서 사용됩니다. 로컬 환경에서의
테스트를 위해서는 HostName을 localhost로 지정할 수 있으나, 일반적으로 이 속성의 값으로 특정 호스트
이름, DNS 이름 또는 직접 IP 번호를 지정할 수 있습니다.
연결 과정에서 로그인 다이얼로그가 나타나는 것을 막기 위해서는 TSQLConnection 컴포넌트의
연결할 수 있습니다. 서버 측에 연결하기 위해서는 DataSnap 서버가 반드시 실행되고 있어야 한다는 것에
주의 하십시오.
2.2.1. DataSnap 클라이언트 클래스들
서버와의 연결이 된 후에는, TSQLConnection 컴포넌트 위에서 오른쪽 마우스를 클릭하고 “ Generate
17Embarcadero Technologies / DEVGEAR
Delphi 2010의 새로운 DataSnap
DataSnap client classes” 메뉴를 선택하면, TServerMethods1Client 라 불리는 클래스를 가진
Unit1이라는 이름의 새 유닛을 생성됩니다 (서버 측의 DataSnap 서버 메소드 클래스의 실제 이름에
“ Client” 부분이 덧붙여진 이름입니다). 이 유닛을 ServerMethodsClient.pas라는 이름으로 저장합니다.
생성된 TServerMethods1Client 클래스의 정의 부분은 아래와 같을 것입니다;
type TServerMethods1Client = class private FDBXConnection: TDBXConnection; FInstanceOwner: Boolean; FEchoStringCommand: TDBXCommand; FServerTimeCommand: TDBXCommand; public constructor Create(ADBXConnection: TDBXConnection); overload; constructor Create(ADBXConnection: TDBXConnection; AInstanceOwner: Boolean); overload; destructor Destroy; override; function EchoString(Value: string): string; function ServerTime: TDateTime; end;
보이는 것처럼, TServerMethods1Client 클래스에 대한 2 개의 생성자와 1 개의 파괴자가 있으며, 우리가
DataSnap 서버 쪽에서 정의했던 2 개의 서버 메소드가 있을 것입니다.
이 메소드들을 사용하기 위해서, ServerMethodsClient 유닛을 ClientForm의 uses 절에 추가하고, TButton
컴포넌트를 클라이언트 폼 위에 배치하고 그 OnClick 이벤트 핸들러에서 다음과 같이 코드를 작성합니다:
procedure TForm2.Button1Click(Sender: TObject); var Server: TServerMethods1Client; begin Server := TServerMethods1Client.Create(SQLConnection1.DBXConnection); try ShowMessage(DateTimeToStr(Server.ServerTime)); finally Server.Free; end;
end;
이 코드는 TServerMethods1Client의 인스턴스를 생성하고, ServerTime 서버 메소드를 호출하고,
마지막으로 DataSnap 서버로의 프록시 메소드를 해제합니다.
컴파일하고 실행한 후 이 버튼을 클릭하면, 예상대로 현재 서버 시간을 표시하는 메시지박스가 나타날
것입니다.
비슷한 방법으로 여러분이 직접 EchoString 메소드를 가지고 연습해보시기 바랍니다.
18Embarcadero Technologies / DEVGEAR
Delphi 2010의 새로운 DataSnap
2.2.1.1. HTTP 통신 프로토콜
TSQLConnection 컴포넌트의 CommunicationProtocol 속성의 디폴트 값이 TCP/IP 라고 언급했었습니다.
따라서 우리는 HTTP 추적 메시지를 전혀 볼 수 없습니다 (2.1.1.1.4장에서 정의했었습니다). 그러나 통신
프로토콜을 변경하는 것은 어려운 일이 아닙니다. TSQLConnection의 Driver 속성의 하위 속성인
CommunicationProtocol의 값으로 HTTP를 지정하기만 하면 됩니다. 211 값은 TCP/IP용으로 사용되므로
Port 속성 또한 변경해야 할 것입니다. ServerContainer에서 TDSHTTPService 컴포넌트에서 지정한 포트
값과 동일한 값을 지정해야 한다는 것을 잊지 마십시오.
이렇게 수정한 후에 DataSnap 클라이언트를 다시 실행하면, 다음과 같은 애플리케이션 에러가 발생할
것입니다:
이 에러를 해결하려면 DSHTTPLayer 유닛을 DataSnap 클라이언트(예를 들어 ClientForm)의 uses 절에
추가하면 됩니다.
2.2.1.2. HTTP 인증
HTTP 통신 프로토콜을 사용했을 때의 장점 중 하나는 HTTP 인증 기능을 추가할 수 있다는 것입니다. 이
것은 2.1.1.1.5장에서 언급한 것처럼 DataSnap 서버 측의 TDSHTTPServiceAuthenticationManager
컴포넌트에 의해서 지원됩니다.
OnHTTPAuthenticate 이벤트 핸들러를 구현되면 거기서 HTTP 인증에 관한 체크가 이루어지며,
TDSHTTPServiceAuthenticationManager에서 액세스를 허용하도록 올바른 정보를 전달해야 합니다.
그렇지 않으면 “ HTTP/1.1 401 Unauthorized error” 라는 에러가 발생하게 됩니다.
DataSnap 클라이언트에서 DataSnap 서버, 더 정확하게는 TDSHTTPServiceAuthentication 컴포넌트로
HTTP 인증 유저이름과 패스워드 정보를 전달하려면, 클라이언트 폼 위에 있는 TSQLConnection
컴포넌트의 DSAuthUser와 DSAuthPassword 속성의 값을 지정해야 합니다.
19Embarcadero Technologies / DEVGEAR
Delphi 2010의 새로운 DataSnap
DataSnap 서버를 로컬 컴퓨터에서 테스트하는 경우가 아니라면 HostName에도 값을 지정해야 합니다.
2.3. DataSnap 서버 배포
서버와 클라이언트 모두 같은 로컬 컴퓨터에서 실행되고 있다면 위 예제는 잘 동작합니다. 그러나 실제
상황에서는, DataSnap 서버는 서버 컴퓨터에서 실행될 것이고, 하나 이상의 클라이언트가 네트워크를
통해 이 서버 컴퓨터로 연결할 것입니다. DataSnap 서버 애플리케이션이 실행되는 컴퓨터는 일반적으로
델파이가 설치되지 않은 컴퓨터일 것입니다. 이것은 DataSnap 서버를 런타임 패키지 없이 컴파일할 것을
고려해야 한다는 것을 의미하므로, 이런 경우 단일의 큰 실행 파일을 만들게 됩니다.
지금까지는 데이터 액세스 컴포넌트를 사용하지 않았으므로, 추가로 데이터베이스 드라이버나 외부
DLL이 필요하지 않습니다.
2.3.1. DataSnap 클라이언트 배포
DataSnap 클라이언트 애플리케이션이 DataSnap 서버 애플리케이션과 다른 컴퓨터에서 실행되고 있다면,
클라이언트가 서버에 연결될 수 있도록 주의해야 합니다. 그러기 위해, 클라이언트 폼의 TSQLConnection
컴포넌트가 Driver 속성의 하위 속성인 CommunicationProtocol과 Port 값뿐만 아니라 HostName 속성
값도 지정해야 합니다. 가능하면 IP 주소를 지정하는 대신 DNS 이름을 사용하도록 하십시오. 제가
DataSnap 서버를 만드는 경우를 예로 들자면, HostName으로 www.bobswart.nl 주소를 사용할 것입니다.
(프로토콜은 CommunicationProtocol 속성에서 지정하므로 접두어 http:// 를 지정하지 않아야 합니다)
다음으로, SQLDataSet 컴포넌트를 TSQLConnection 컴포넌트 옆에 내려놓고, SQLConnection 속성을
TSQLConnection 컴포넌트로 지정합니다.
CommandType을 ctQuery 그대로 두고, SQL 쿼리를 입력하기 위해서 CommandText 속성을 더블
클릭합니다.
21Embarcadero Technologies / DEVGEAR
Delphi 2010의 새로운 DataSnap
아래와 같이 SQL문을 입력하고 OK를 클릭하여 다이얼로그를 닫습니다.
SELECT EMP_NO, FIRST_NAME, LAST_NAME, HIRE_DATE, JOB_COUNTRY FROM EMPLOYEE
TSQLConnection 컴포넌트의 LoginPrompt와 Connected 속성을 False로 하고, TSQLDataSet 의 Active
속성 또한 False로 지정해야 합니다.
이제 TSQLDataSet 컴포넌트의 내용을 받아오기 위해서 ServerMethodsUnitDemo 유닛의
TServerMethods1 클래스에 퍼블릭 함수를 추가해야 합니다.
type TServerMethods1 = class(TDSServerModule) SQLConnection1: TSQLConnection; SQLDataSet1: TSQLDataSet; private { Private declarations } public { Public declarations } function EchoString(Value: string): string; function ServerTime: TDateTime; function GetEmployees: TDataSet; end;
TServerMethod1 정의부분에서 보이는 것처럼, GetEmployees 라는 새로운 함수를 다음과 같이
구현합니다.
22Embarcadero Technologies / DEVGEAR
Delphi 2010의 새로운 DataSnap
function TServerMethods1.GetEmployees: TDataSet; begin SQLDataSet1.Open; // 데이터를 가져올 수 있도록 합니다 Result := SQLDataSet1 end;
서버를 재컴파일하고 다시 실행시킵니다. 만약 DataSnap 서버를 닫았는데도 컴파일이 안 된다면,
DataSnap 서버가 여전히 실행되고 있을 가능성이 높습니다.
3.1. TSqlServerMethod
다시 DataSnap 클라이언트 애플리케이션으로 돌아옵니다. TSQLConnection 컴포넌트는 더 이상
DataSnap 서버와 연결되어 있지 않을 것입니다. (만일 여전히 연결되어있다면, DataSnap 프로세스가
여전히 실행 중이기 때문에 서버를 재컴파일할 수 없을 것입니다). 서버의 정보를 다시 가져오기 위해서
Connected 속성을 다시 True로 설정하고 오른쪽 마우스 버튼을 다시 사용하여 클라이언트 클래스를 다시
생성합니다.
이전 ServerMethodsClient 유닛을 덮어쓰기 위해, 프로젝트에서 이전 버전의 ServerMethodsClient를
삭제하고 “ Generate DataSnap client classes” 메뉴를 다시 선택합니다. 새롭게 작성된 유닛을 다시
ServerMethodsClient.pas로 저장하게 되면, 이전 클라이언트 코드는 수정하지 않아도 됩니다.
“ Generate DataSnap client classes” 메뉴를 다시 실행하고 나면, 생성된 TServerMethods1Client
클래스에는 GetEmployees 메소드가 추가되어 있을 것입니다. (ServerTime와 EchoString 함수도 역시
나타납니다)
type TServerMethods1Client = class private FDBXConnection: TDBXConnection; FInstanceOwner: Boolean; FEchoStringCommand: TDBXCommand; FServerTimeCommand: TDBXCommand; FGetEmployeesCommand: TDBXCommand; public constructor Create(ADBXConnection: TDBXConnection); overload; constructor Create(ADBXConnection: TDBXConnection; AInstanceOwner: Boolean); overload; destructor Destroy; override; function EchoString(Value: string): string; function ServerTime: TDateTime; function GetEmployees: TDataSet; end;
GetEmployees 메소드에서 TDataSet 데이터를 가져오기 위해 툴 팔레트의 dbExpress 페이지에 있는
TSqlServerMethod 컴포넌트를 사용할 수 있습니다. 클라이언트 폼 위에 TSqlServerMethod 컴포넌트를
var i: Integer; begin Info(' nnect ' + DSConnectEventObject.ChannelInfo.Info)Log Co ; for i:=0 to DSConnectEventObject.Transport.Filters.Count-1 do LogInfo(' Filter: ' + DSConnectEventObject.Transport.Filters.GetFilter(i).Id); end;
4.1. ZlibCompression 필터
예제로서 기존의 DataSnap 필터를 살펴봅시다. 이것은 델파이 2010에 기본으로 내장된 것으로,
DataSnap 서버와 클라이언트 사이의 데이터 스트림을 압축해주는 역할을 합니다. 이 필터의 이름은
ZlibCompression 필터로, DbxCompressionFilter 유닛에 있습니다.
TCP/IP를 위한 TDSTCPServerTransport 컴포넌트와 HTTP를 위한 DSHTTPService 컴포넌트 모두
TTransportFiltersCollection 타입의 Filters 속성을 가지고 있습니다. 필터 콜렉션을 편집하려면 Filters
속성의 엘립시스(…) 버튼을 클릭합니다. 이 다이얼로그에서 새로운 TTransportFilterItem을 추가할 수
있는데, 오브젝트 인스펙터에서 FilterId와 선택적인 옵션을 지정할 수 있습니다. ZLibCompression 필터는
델파이 2010에 기본적으로 내장되어 있으며, TTransportFilterItem의 FilterId로 지정할 수 있습니다.
서버 측에서 TDSTCPServerTransport 컴포넌트에 Filters 속성에 지정하는 것과 별도로, 클라이언트
측에서도 이 필터를 지정해야 한다는 것을 기억하십시오(보내는 요청을 압축하고 돌아오는 응답을 압축
해제하기 위해). 이를 위해서는 ClientForm의 uses 절에 DbxCompressionFilter 유닛을 추가하기만 하면
됩니다. 그러면 TTransportCompressionFilter가 자동으로 등록되고 서버와의 통신에 사용되게 됩니다.
uses 절에 DbxCompressionFilter 유닛을 추가하지 않으면, 클라이언트를 실행할 때 "Communication
filter ZLibCompression is not registered. Filter class needs to be registered in order to communicate
with the server." (통신 필터
ZLibCompression가 등록되지
않았습니다. 서버와 통신하기
위해서는 필터 클래스가 등록되어
있어야 합니다) 라는 메시지의
예외가 발생할 것입니다.
32Embarcadero Technologies / DEVGEAR
Delphi 2010의 새로운 DataSnap
4.2. 로그 필터
DataSnap 2010에서는 개발자들이 직접 자신만의 전송 필터를 정의할 수 있습니다. TTransportFilter
타입에서 파생하는 새로운 클래스를 만들면 됩니다. 이 새로운 클래스에서 메소드들을 오버라이드하여
새로 구현할 수 있습니다. 예를 들면, 다음과 같이 TLogFilter 클래스를 만들 수 있습니다.
unit LogFilter; interface uses SysUtils, DBXPlatform, DBXTransport; type TLogFilter = class(TTransportFilter) private protected function GetParameters: TDBXStringArray; override; function GetUserParameters: TDBXStringArray; override; public function GetParameterValue(const ParamName: UnicodeString): UnicodeString;override;
function SetParameterValue(const ParamName: UnicodeString; const ParamValue: UnicodeString): Boolean; override; constructor Create; override; destructor Destroy; override; function ProcessInput(const Data: TBytes): TBytes; override; function ProcessOutput(const Bytes): TBytes; override; Data: T function Id: UnicodeString; override; end; const LogFilterName = 'Log';
이 클래스의 구현 부분 대부분을 비워놓는데, 이 로그 필터의 유일한 목적이 ProcessInput과
ProcessOutput 메소드에서 보내지는 데이터를 로깅하는 것이기 때문입니다. 비어있지 않은 메소드의
구현은 다음과 같습니다.
function TLogFilter.SetParameterValue(const ParamName, ParamValue: UnicodeString): Boolean;
begin Result := True; end; constructor TLogFilter.Create; begin inherited Create; end; destructor TLogFilter.Destroy; begin inherited Destroy; end; function TLogFilter.ProcessInput(const Data: TBytes): TBytes; begin Result := Data; // 들어오는 데이터를 로깅함 end; function TLogFilter.ProcessOutput(const Data: TBytes): TBytes; begin Result := Data; // 나가는 데이터를 로깅함 end;
33Embarcadero Technologies / DEVGEAR
Delphi 2010의 새로운 DataSnap
function TLogFilter.Id: UnicodeString; begin Result := LogFilterName; end;
마지막으로, DataSnap 전송 필터의 구현에서 중요한 부분은 initialization 섹션과 finalization 섹션에서의
등록 부분입니다. 이렇게 하면 DataSnap 클라이언트가 전송 필터를 찾을 수 있게 되고, 필요할 때 사용할
클라이언트 측에서, TDBXCallback에서 파생되는 새로운 클래스를 선언하고, Execute 메소드를
오버라이드 합니다.
type TCallbackClient = class(TDBXCallback) public function Execute(const Arg: TJSONValue): TJSONValue; override; end;
Execute 메소드 안에서, TJSONValue 타입의 Arg 인자를 받게 되며 이것을 클론(clone)하여 실제 내용에
접근할 수 있습니다. Execute 메소드는 자신인 TJSONValue 자체를 리턴할 수 있으므로, 동일한 값을 다시
리턴하도록 코딩하겠습니다.
function TCallbackClient.Execute(const Arg: TJSONValue): TJSONValue; var Data: TJSONValue; begin Data := TJSONValue(Arg.Clone); ShowMessage('Callback: ' + TJSONObject(Data).Get(0).JSonValue.value); Result := Data; end;
이 예제에서, 위의 콜백 메소드는 EchoString 메소드가 실제로 리턴되기 전에(예를 들어 메소드가 아직
실행되고 있는 동안) EchoString 메소드로 전달된 값을 보여줄 것입니다.
서버 측의 새로운 EchoString 메소드의 구현에서는, 다음과 같이 TJSONObject 안에 스트링 값을 넣어
callback.Execute 메소드에 전달합니다:
function TServerMethods2.EchoString(Value: string; callback: TDBXcallback): string; var msg: TJSONObject; pair: TJSONPair; begin Result := Value; msg := TJSONObject.Create; pair := TJSONPair.Create('ECHO', Value); pair.Owned := True; msg.AddPair(pair); callback.Execute(msg); end;
서버 쪽에서 실제 EchoString 메소드가 끝나기 전에 클라이언트 쪽에서 콜백 함수가 실행되고 리턴된다는
점을 주목하십시오.
마지막으로, 콜백 클래스(새로운 TCallbackClient의 인스턴스)를 두 번째 인자로 전달해야 하므로,
클라이언트 측의 EchoString 메소드 호출도 역시 수정해야 합니다.
var MyCallback: TCallbackClient; begin MyCallback := TCallbackClient.Create; try Server.EchoString(Edit1.text, MyCallback); finally MyCallback.Free; end;
end;
이 간단한 예제를 통해 DataSnap 2010에서 클라이언트 측 콜백 메소드를 사용하는 방법을 살펴봤습니다.
44Embarcadero Technologies / DEVGEAR
Delphi 2010의 새로운 DataSnap
7. DataSnap 과 .NET
델파이 프리즘 2010으로 이전에 우리가 만들었었던 Win32 서버에 대한 DataSnap .NET 클라이언트를
개발할 수 있습니다. 델파이 프리즘 2010 DataSnap 클라이언트를 작성하기 위해서는, 디자인 시점에
연결할 수 있도록 DataSnap 서버가 실행되어 있어야 합니다.
델파이 프리즘 2010을 시작하고, View | Server Explorer를 선택하여 델파이 프리즘 서버 익스플로러가
나타나도록 합니다. 실제로 DataSnap 서버와 동작하는지 확인하기 위해 여기에서 연결을 시도해봅니다.
서버 익스플로러는 트리뷰 형태의 창으로서, Data Connections라는 루트 노드를 가지고 있습니다. Data
Connections에서 오른쪽 마우스 클릭하여 “ Add Connection” 을 선택합니다. 아래와 같은
다이얼로그에서, Data sources 목록에서 DataSnap를 선택합니다(주의: datasource가 이미 선택되어
있다면 Change를 클릭해야 합니다).
향후 항상 DataSnap 데이터 커넥션만 사용하려 하지 않는 한, “ Always use this selection” 체크박스는
체크하지 않습니다.
“ Continue” 를 클릭하면 다이얼로그의 다음 페이지로 이동합니다. 여기에서, DataSnap 서버에
연결하기 위한 세부사항들을 지정할 수 있습니다. Protocol 드롭다운 콤보박스에서 tcp/ip 혹은 http를
선택합니다. 다음으로 Host를 지정해야 하며(DataSnap 서버가 실행되고 있는 컴퓨터 이름, 만약 동일한
로컬 컴퓨터에서 테스트하는 경우에는 localhost), 그리고 Port 번호를 지정해야 합니다. 포트 번호는
디폴트로 HTTP에 대해서는 Port 80, TCP/IP에 대해서는 Port 211 이지만, 앞에서부터 읽어왔다면 두 값이
달라질 수 있다는 것을 알 것입니다. ServerContainerUnitDemo 유닛의 트랜스포트 컴포넌트에서 지정한
값과 동일한 값으로 지정해야 합니다.
그 다음의 속성은 Path 입니다. 이 속성은 웹 브로커 기반의 DataSnap 서버에 연결하려는 경우에만
중요합니다 (DataSnap 웹 서버에 연결하기 위해 지정하는 URLPath로서, http://../ 도메인 부분의
뒷부분입니다.
마지막으로, DataSnap 서버가 HTTPAuthentication를 사용하는 경우, 인증 유저 이름과 패스워드를
지정하는 것을 잊지 마십시오.
45Embarcadero Technologies / DEVGEAR
Delphi 2010의 새로운 DataSnap
Test Connection 버튼을 클릭하여 지정한 DataSnap 서버에 연결되는지 확인합니다. 만일 모든 것을
제대로 지정했다면 “ Test connection succeeded” 다이얼로그가 나타납니다.
OK를 클릭하면 데이터 커넥션 트리에 DataSnap 커넥션의 새로운 항목이 추가됩니다. 이 경우에는
localhost 노드에 나타날 것입니다. 새로운 노드를 확장하면 하위 노드로 테이블, 뷰와 저장 프로시저들이
나타날 것입니다. 테이블과 뷰는 비어 있겠지만 저장 프로시저에는 우리가 작성한 서버 메소드인
EchoString, GetEmployees와 ServerTime 등 DataSnap 서버에 있는 서버 매소드들 모두가 포함되어
있습니다.
46Embarcadero Technologies / DEVGEAR
Delphi 2010의 새로운 DataSnap
서버 익스플로러에서 서버 메소드들을 테스트할 수 있습니다. 예를 들어, EchoString 메소드에서 오른쪽
마우스를 클릭하여 “ View Parameters” 를 선택해보십시오. 이렇게 하면 Value 파라미터의 값을 입력할
수 있는 새로운 창이 나타납니다. Value 파라마터의 값으로 42를 입력해보십시오. 이제 창에서 오른쪽
마우스를 클릭하고 “ Execute” 을 선택하여 DataSnap 서버의 EchoString 메소드를 실행합니다. 저장
프로시저 파라미터 창의 아래쪽에 결과가 나타날 것입니다.
47Embarcadero Technologies / DEVGEAR
Delphi 2010의 새로운 DataSnap
EchoString으로 테스트해보는 것도 좋지만, GetEmployees 메소드로 Employees 테이블의 데이터를
가져오고 사용해보는 것이 좀 더 교육적일 것 같습니다. 이 저장 프로시저에는 파라미터가 없지만 역시
“ View Parameters” 명령을 선택할 수 있으며, 프로시저 파라미터가 빈 리스트로 표시됩니다. 다시 이
창에서 오른쪽 마우스를 클릭하여 “ Execute” 을 선택합니다. 이번에는, GetEmployees 메소드에서
리턴되는 Employees 테이블의 전체 레코드셋이 그 결과로 나옵니다.
48Embarcadero Technologies / DEVGEAR
Delphi 2010의 새로운 DataSnap
7.1. WinForms 클라이언트
서버 익스플로러에서 DataSnap 서버 메소드를 작업하는 것이 흥미롭기는 하지만, 당연히 .NET
애플리케이션에서 서버 메소드를 호출하는 것이 실용적입니다. 마지막 예제로서, 델파이 프리즘에서 File |
New Project를 선택하여 New Project 위저드를 시작합니다. 그러면 생성 가능한 프로젝트 타겟들의
리스트를 보여줍니다.
왼쪽의 Project Types 리스트에서 Windows 프로젝트 타입을 선택하고 다시 Windows Application를
선택한 후, WindowsApplication1을 DataSnapClient로 이름을 변경합니다.
OK를 클릭하면, 델파이 프리즘 개발환경에 새로운 프로젝트 DataSnapClient가 생성되고 Main 폼으로
Main.pas 유닛이 추가되어 있습니다.
서버 익스플로러에서 우리가 앞 절에서 작성했던 DataSnap 서버에 대한 새 커넥션을 선택합니다.
Properties Explorer에 속성들이 나타날 것이며, 다음과 같은 값을 갖는 ConnectionString 속성도 나타날