[데이터스냅] 데이터스냅 클라이언트에서 TCP/IP로 접속 시 ConnectTimeout이 적용되지 않는 경우 대처방법

2015.11.04 15:55

(이글은 RAD 스튜디오 10 시애틀 기준으로 테스트하고, 작성되었습니다. 다른 버전 사용자들은 이슈 발생여부를 먼저 확인하고 아래 내용을 참고하시기 바랍니다.)


이 글은 데이터스냅 클라이언트에서 TCP/IP로 접속 시 Timeout이 적용되지 않는 이슈에 대해 원인을 확인하고 회피하는 내용을 소개합니다.


데이터스냅 클라이언트 프로그램에서 서버에 접속하기 위해 SQLConnection을 사용합니다.


연결방식(CommunicationProtocol)은 tcp/ip, http, https가 있습니다.

Timeout 속성은 CommnunicationTimeout(송수신 시간제한)과 ConnectTimeout(연결 시간제한) 두가지가 있습니다.


결론적으로 Timeout 속성이 적용되지 않는 경우는 tcp/ip 연결방식으로 연결한 경우 두가지 Timeout 속성이 모두 적용되지 않습니다.(http, https 연결방식에서는 Timeout이 정상 적용됩니다.)

이미 엠바카데로 QC에 이슈가 등록되었지만 적용되지 않는다는 답변으로 아쉽게 마무리되었습니다.

http://qc.embarcadero.com/wc/qcmain.aspx?d=80954#sthash.9Apdxn5i.dpuf


제일  손쉬운 조치방법은 CommunicationProtocol을 http로 변경하는 것입니다.(물론 데이터스냅 서버에서 http를 지원해야 합니다.)


만약, TCP/IP로 연결하실 분들은 아래 내용을 참고하세요.
(RAD 스튜디오 10 시애틀에서 진행했습니다. 다른 버전 사용자분들은 참고해서 수정하시기 바랍니다.)

총 2개의 Unit을 수정하면됩니다. 
아래 2개 파일을 프로젝트 파일 경로로 복사 후 프로젝트에 추가(Add) 합니다.(즉, 해당 프로젝트에서만 수정된 내용이 반영됩니다.)
  • Data.DbxSocketChannelNative.pas(C:\Program Files (x86)\Embarcadero\Studio\17.0\source\data\dbx)
  • IPPeerClient.pas(C:\Program Files (x86)\Embarcadero\Studio\17.0\source\indy\implementation)

Data.DbxSocketChannelNative.pas(C:\Program Files (x86)\Embarcadero\Studio\17.0\source\data\dbx)
procedure TDBXIdTCPLayer.Open(const DBXProperties: TDBXProperties);
var
  timeout: string;
  commTimeout: string;
  LIPVersionStr: string;
  LIPVersion: Integer;
begin
  Close;
  FIPImplementationID := DbxProperties[TDBXPropertyNames.IPImplementationID];
  if FIdSocket = nil then
    FIdSocket := CreateClientSocket;
  FIdSocket.Host := DbxProperties[TDBXPropertyNames.HostName];
  FIdSocket.Port := DbxProperties.GetInteger(TDBXPropertyNames.Port);

  LIPVersionStr := DbxProperties[TDBXPropertyNames.CommunicationIPVersion].Trim;
  LIPVersion := GetEnumValue(TypeInfo(TIPVersionPeer), LIPVersionStr);
  if LIPVersion > -1 then
    FIdSocket.IPVersion := TIPVersionPeer(LIPVersion)
  else
    FIdSocket.IPVersion := TIPVersionPeer.IP_IPv4;

  timeout := DbxProperties[TDBXPropertyNames.ConnectTimeout];
  if timeout = '' then
    ConnectTimeout := 0
  else
    ConnectTimeout := StrToInt(timeout);
  commTimeout := DbxProperties[TDBXPropertyNames.CommunicationTimeout];
  if commTimeout = '' then
    CommunicationTimeout := 0
  else
    CommunicationTimeout := StrToInt(commTimeout);

  { Added Humphrey}
  // ConnectTimeout과 CommunicationTimeout을 FIdSocket에 전달
  if Supports(FIdSocket, IIPPeerClientSetTimeout) then
  begin
    (FIdSocket as IIPPeerClientSetTimeout).SetConnectTimeout(ConnectTimeout);
    (FIdSocket as IIPPeerClientSetTimeout).SetReadTimeout(CommunicationTimeout);
  end;
{ Added Humphrey}

  FIdSocket.UseNagle := false;
  FIdSocket.Connect;

  FConnected := false;
end;
위 메소드는 tcp/ip로 데이터스냅 클라이언트가 접속하는 일부 소스코드입니다.
주의깊게 볼 부분은 ConnectTimeout과 CommunicationTimeout 값을 가져오고 FIdSocket에 전달하는 내용이 누락되었습니다.
그래서 "Added Humphery" 주석으로 감싸진 내용을 추가했습니다.
uses 절에 IPPeerClient 추가해야 합니다.

IPPeerClient.pas(C:\Program Files (x86)\Embarcadero\Studio\17.0\source\indy\implementation)
interface
  { Added Humphrey}
type
  IIPPeerClientSetTimeout = interface
    ['{9924134C-9C7D-464F-8ABE-F3E1E408C566}']
    procedure SetConnectTimeout(const ATimeout: Integer);
    procedure SetReadTimeout(const ATimeout: Integer);
  end;
{ Added Humphrey}
상단 interface 아래에 IIPPeerClientSettimeout 인터페이스 추가

  TIdTCPClientPeerIP = class(TIdClassIP, IIPTCPClient, IIPObject{ Added Humphrey}, IIPPeerClientSetTimeout{ Added Humphrey})
  private
    FTCPClient: TIdTCPClientIP;
    FIOHandler: IIPIOHandler;
    FSocket: IIPIOHandlerSocket;
  protected
    function Connected: Boolean;
    function GetSocket: IIPIOHandlerSocket;
    procedure SetIOHandler(Handler: IIPIOHandler);
    function GetIOHandler: IIPIOHandler;
    function GetBoundIP: string;
    procedure SetBoundIP(IP: string);
    function GetHost: string;
    procedure SetHost(LHost: string);
    function GetPort: TIPPortPeer;
    procedure SetPort(LPort: TIPPortPeer);
    procedure SetIPVersion(const AValue: TIPVersionPeer);
    function GetIPVersion: TIPVersionPeer;
    function GetUseNagle: Boolean;
    procedure SetUseNagle(Use: Boolean);
    procedure Connect;
    procedure Disconnect;
    function GetManagedIOHandler: Boolean;
    procedure SetManagedIOHandler(AManagedIOHandler: Boolean);
  public
    function GetObject: TObject;
    function GetIPImplementationID: string;
    constructor Create(AOwner: TComponent);
    destructor Destroy; override;
{ Added Humphrey}
    procedure SetConnectTimeout(const ATimeout: Integer);
    procedure SetReadTimeout(const ATimeout: Integer);
{ Added Humphrey}
  end;
TIdTCPClientPeerIP 클래스 선언부에 위 IIPPeerClientSetTimeout 인터페이스 위임, 인터페이스 메소드 추가

구현부에 타임아웃 적용하도록 구현
{ Added Humphrey}
procedure TIdTCPClientPeerIP.SetConnectTimeout(const ATimeout: Integer);
begin
  FTCPClient.ConnectTimeout := ATimeout;
end;

procedure TIdTCPClientPeerIP.SetReadTimeout(const ATimeout: Integer);
begin
  FTCPClient.ReadTimeout := ATimeout;
end;
{ Added Humphrey}
위와 같이 조치하면 TSQLConnection으로 DataSnap을 TCP/IP로 연결하는 경우에도 Timeout 속성이 모두 적용됩니다.

완성된 소스코드는 저작권 문제(?)로 올려놓지 못하니 양해부탁드리며 필요하신 분들은 개인적으로 요청 해주세요.


저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/데이터 엑세스

  1. Blog Icon

    비밀댓글입니다

여러분의 앱에서 클라우드 서비스에 연결하세요.(REST Client이용)

2014.11.05 11:33

최근 다양한 웹서비스와 클라우드에서 JSON 기반의 RESTFul 인터페이스를 제공합니다.


오늘은 짐매키트가 발표한 동영상을 통해 REST 클라이언트로 클라우드 서비스에 연결하는 내용을 소개합니다.

짐은 대표적인 클라우드 서비스인 드랍박스에 REST 클라이언트 라이브러리를 통해 모바일앱에서 연결하는 데모를 진행합니다.

이 글에서는 아래 내용을 포함합니다.

  • 레스트 디버거(RESTFul 사이트 분석 도구)를 통한 웹사이트 분석
  • 클라우드 서비스(드랍박스) 연동
    • TOAuth1Authenticator 컴포넌트를 이용한 OAuth 1.0 인증과정 소개

아래의 동영상을 통해 여러분의 앱에서 클라우드 서비스(드랍박스)에 연결하는 방법을 익혀 보시기 바랍니다.


관련글


저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/데이터 엑세스

동영상강좌) 코드아카데미: 앱개발에 집중하고, 백엔드는 클라우드에 맡기세요!

2014.11.05 11:18

지난 2014년 7월에 제가 진행했던 코드아카데미 동영상입니다.


모바일 백엔드 서비스(BaaS)에 대한 개념과 사용하는 방법을 데모와 함께 진행합니다.

여러분의 앱에 클라우드를 연결해 백엔드 개발에 대한 부담을 덜어보세요.


총 4편으로 구성되었습니다.

  • BaaS와 Kinvey 자세히 알아보기
  • 사용자관리-사용자 인증과 관리방법
  • 클라우드 기반 스토리지
  • 푸쉬알림 사용하기




저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/데이터 엑세스 BAAS

RAD Studio XE7의 TListView에서 당겨서 새로고침 구현 샘플

2014.11.04 09:37

RAD Studio XE7에서 TListView의 당겨서 새로고침 기능이 추가되었습니다.(이전버전에서는 코드로 직접 구현이 필요했습니다.)

TListView에서 제공하는 기능은 목록을 당기면 UI 요소에서 새로고침 UI를 표시하고 이벤트를 발생하는 UI 요소에 대한 새로고침 기능입니다. 실제 데이터를 조회하는 기능은 이벤트 내에서 직접 구현해야 합니다.


엠바카데로의 사리나 듀폰의 블로그에서 TListView의 당겨서 새로고침(OnPullRefresh) 이벤트에서 데이터 처리하는 기능에 대한 샘플을 제공합니다.


아래와 같이 UI를 구성하고 BaaS(Backend as a Service: Cloud Service)에서 데이터를 조회하는 내용의 샘플입니다.


자세한 내용은 하단의 링크를 통해 따라해 보시기 바랍니다.



사용되는 구성요소

User Interface:

  • TListView, aligned to the client
    • populated with data using LiveBindings Designer
  • TToolbar, aligned to the top
    • TLabel, parented to TToolbar, aligned to contents; TextSettings->HorzAlign: Center
  • TToolbar, aligned to the bottom
    • TLabel, parented to TToolbar, aligned to contents; TextSettings->HorzAlign: Center; TextSettings->FontColor: Dodgerblue;
BaaS:
  • TKinveyProvider
    • AppKey, AppSecret and MasterSecret have been set; you can also use TApp42Provider and TParseProvider
  • TBackendQuery for querying existing data that lives in the cloud and was added via the BaaS web interface
    • connected to KinveyProvider; BackendService = Storage; BackendClassName = Recipes (this was defined inside my Kinvey account))
  • TRESTResponseDataSetAdapter
    • DataSet = FDMemTable1
    • ResponseJSON = BackendQuery1
    • Active = True
  • TFDMemTable


저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/데이터 엑세스

  1. 링크가 깨진것 같습니다. 아래 링크에서 내용을 확인할 수 있습니다.
    http://community.embarcadero.com/index.php/blogs/blogger/listall/sarina-dupont-embarcadero-com?start=10

REST Debugger 활용 팁(Copy Components)

2014.10.01 09:46

이전에 RESTful 웹사이트 분석에 유용한 REST Debugger 소개하는 글을 남겼습니다.

XE7 버전에 포함된 REST Debugger에 추가된 기능을 추가로 소개합니다.


이번에 소개할 내용은 [Copy Components] 입니다.

(RAD Studio XE7에서는 IDE 메인메뉴에서 Tools > REST Debugger 메뉴를 통해 실행할 수 있습니다.)

❑ Copy Components 버튼

이전의 레스트 디버거의 경우 웹 서비스 API 분석에 아주 유용했지만, 분석한 내용을 구현하기 위해서는 개발 시 분석한 내용을 일일히 컴포넌트 추가후 값을 설정하는 작업이 필요했습니다.

하지만, 이번에 추가된 Copy Components를 이용하면 위의 반복되는 작업 없이 레스트 디버거에서 분석한 내용을 그대로 개발 시 사용할 수 있습니다.


이제 아래와 같은 프로세스로 웹서비스 연동 앱을 쉽고 빠르게 개발할 수 있습니다.


1, 레스트 디버거(REST Debugger)로 웹서비스 분석

저는 이전 글과 같이 api.discogs.com의 rest api를 분석했습니다.


2, Copy Components로 REST Client 컴포넌트 복사

컴포넌트 복사 이전에 하단의 Response 영역에서 JSON Root Elements에 사용할 요소를 선택 후 Apply 버튼으로 적용 후 Tabular Data 그리드에 목록이 표시되는 상태에서 복사하시기 바랍니다.


3, 개발 폼(또는 데이터모듈)에 복 사 후 UI 연결

델파이(또는 C++빌더)의 폼에 Ctrl + V로 붙여넣기 하면 아래와 같이 5개의 컴포넌트가 복사됩니다.(저는 보기 좋게 정리했습니다.)

RESTRequest를 더블클릭해 새로운 데이터 요청 후 라이브 바인딩 기술 등을 이용해 UI 컨트롤과 데이터를 연결해 사용하면 손쉽게 웹서비스와 연결할 수 있습니다.



Tip. 데이터가 없을 경우 RESTful 데이터 요청

REST Debugger의 Copy Components로 컴포넌트 복사 직후 또는 프로젝트를 연 직후에는 데이터가 없는 상태로 표시됩니다.

이때에는 RESTRequest 컴포넌트를 통해 웹서비스에 데이터 요청을 하면 수신데이터를 받아 데이터 셋에 표시됩니다.

요청하는 방식은 

1, RESTRequest 컴포넌트를 더블클릭 해 요청합니다.

2, RESTRequest 컴포넌트 우측 마우스 팝업메뉴의  Execute... 버튼을 누릅니다.

3, RESTRequest 컴포넌트 선택 후 Object Inspector 하단의 Execute 버튼을 누릅니다.


관련글



저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/데이터 엑세스 REST Debugger

[REST Client] RESTRequest 비동기 호출방법

2014.08.26 09:53

델파이에서 이미 구축된 웹서비스와 연동하기 위해서 REST 클라이언트 컴포넌트를 이용할 수 있습니다.


그동안 비동기로 전송하는 부분이 궁금했는데 우크라이나(?) 개발자인 Dave Gill이 남긴  글(RestRequest.ExecuteAsync and TaniIndicator)을 보고 반가운 마음에 글을 남깁니다.


비동기로 웹서비스에 요청하면 아래의 장점이 있습니다.

  • 어플리케이션의 화면이 어는 현상이 없습니다.
  • 데이터 요청 시 TAniIndicator 컴포넌트로 진행여부를 표시할 수 있습니다.(동기로 호출 시 화면이 얼어 움직이지 않습니다.)
  • 백그라운드로 다른 작업을 진행할 수 있습니다.


글을 참고하니 이미 TRESTRequest 컴포넌트에는 비동기로 호출할 수 있는 ExecuteAsync 메소드가 있었습니다.(저만 몰랐네요.)


테스트를 진행해 봤습니다.(VCL에서 테스트 해봤습니다.)

아래와 같이 ExecuteAsync 호출 시 TProc(인자가 없는 프로시저)를 함께 전달하면 요청이 끝나고 호출됩니다.

호출된 메소드에서 응답데이터를 사용하도록 구현하면 비동기로 웹서비스를 연동할 수 있습니다.


저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/데이터 엑세스 Rest Client, TRESTRequest

[BaaS] 데스크탑(VCL) 어플리케이션에서 특정 유저에게 GCM/APN 전송하기

2014.07.24 17:25

안녕하세요. 험프리.김현수입니다.


요즘 BaaS 코드아카데미 준비하느라 바쁘면서도 즐겁습니다. 새로운 내용을 공부하고 누군가에게 도움을 줄 수 있다는 것이 보람되네요^^(코드아카데미 참석해 주세요. 좋은 내용이 많습니다. http://tech.devgear.co.kr/404011)


이번에 소개해드릴 내용은 얼마전 어느분께서 데브기어를 통해 질문하신 내용입니다.

질문 내용은 "VCL로 푸시메시지를 브로드캐스트는 되는데 특정 사용자에게 전송 가능하느냐? "라는 질문이었습니다.


이 글을 이해하시기 위해서는  BaaS와 RAD Studio 글을 먼저 읽어보시고, 데스크탑에서 클라우드 메시지 전송 글을 선행해야 이글을 이해하시는데 도움이 많이 됩니다.

VCL 어플리케이션으로 특정 사용자에게 푸시메시지 전송

앞에서 안내해 드린 글을 데스크탑에서 클라우드 메시지 전송 보면 VCL 어플리케이션에서 Kinvey의 PushEndPoint를 이용해 브로드캐스트 메시지를 전송합니다.

특정사용자에게 전송할 때에도 PushEndPoint를 설정해 전송 할 수 있습니다. 

❑ 모바일 수신 설정(Custom Endpoint)

먼저 Kinvey에 접속해 Custom EndPoints(Addons > Business Logic > Custom Endpoints > New)를 추가합니다.

이름을 지정(저는 SpecificUsersMessage로 정했습니다.)하고, 다음의 코드를 입력하고 저장합니다.

function onRequest(request, response, modules){
    // 모듈
  var push = modules.push, 
      collectionAccess = modules.collectionAccess, 
      logger = modules.logger;
 
  // 데이터
  var iOSAps = request.body.iosaps;
  var iOSExtras = request.body.iosextras;
  var androidPayload = request.body.androidpayload;
  var username = androidPayload.username;

  if(username){
    collectionAccess.collection('user').findOne({'username': username}, function (err, user) {
      if (err) {
        logger.error('Query failed: '+ err);
      } else {
        if(user){
          // 사용자가 있으면 대상에게
          logger.info('Pushing message to ' + user.username);
          push.sendPayload(user, iOSAps, iOSExtras, androidPayload);
        } else {
          logger.error('Not found user!!(username: ' + username + ')');
        }
      }
   
      // 콜백함수 안에 있어야 합니다. 밖에 있으면 콜백함수 수행전 먼저 응답합니다.
      response.complete(200);    
    });
  } else {
    // username 미 지정 시 전체
    logger.info('Pushing message to All');
    push.broadcastPayload(iOSAps, iOSExtras, androidPayload);
  }
}

그리고 어플리케이션 제작 전 API Console을 이용해 아래의 코드로 테스트(참고)합니다.

{
  "iosaps": {
    "alert": "안녕하세요."
  }, 
  "iosextras": {
    "username": "testuser"
  }, 
  "androidpayload": {
    "username": "testuser",
    "message": "안녕하세요.",
    "title": "메시지 제목"
  }
}

위 내용 중 username 항목을 변경해 사용하시기 바랍니다.(Kinvey API 콘솔 에디터가 이상하게 복사/붙여넣기가 안되네요. 저만 그런가요? 타이핑해서 넣어서 사용하기 바랍니다.)

❑ VCL 전송 어플리케이션

앞에서 만든 EndPoints를 이용해 푸시메시지 전송하는 VCL 전송어플리케이션을 작성합니다.

VCL 폼의 디자인을 다음과 같이 대상(edtUserName) 항목을 추가하도록 수정합니다.(꼭 같을 필요는 없습니다.)

저는 전체에게 보낼지 대상을 지정할지 선택하기 위해 체크박스를 넣었습니다.


KinveyProvider의 PushEndPoint 속성에 앞에서 작성한 Custom EndPoints(SpecificUsersMessage)를 입력합니다.


메시지 전송 버튼에는 아래의 코드를 이용합니다.

var
  Data: TPushData;
begin
  Data := TPushData.Create;
  try
    Data.Message      := edtMessage.Text;
    Data.GCM.Title    := edtTitle.Text;
    Data.GCM.Message  := edtMessage.Text;

    if CheckBox1.Checked then
      Data.Extras.Add('username', edtUserName.Text);

    BackEndPush1.PushData(Data);
  finally
    Data.Free;
  end;

위 소스의 키포인트는 Data.Extras에 username을 추가하는 것입니다.

Extras 정보에 username을 추가하면 실제 전송되는 JSON 데이터에의 androidpayload와 iosextras에 username항목이 추가되고 Custom Endpoints에서 androidpayload의 username으로 특정 사용자에게 전송하도록 설정되었습니다.


BaaS를 이용하면 여러분들은 별도의 서버 준비 없이 서버기능인 백엔드 기능을 클라우드 기반의 서비스에서 이용할 수 있습니다.

한번 익혀두면 모바일 및 데스크탑에서 유용하게 사용할 수 있으니 도전해 보시기 바랍니다.



관련글

 

저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/데이터 엑세스 APN, BAAS, Custom Endpoints, Delphi, gcm, Kinvey, vcl

BaaS 컴포넌트(TBackendFiles)를 이용해 파일 업로드/다운로드 샘플

2014.06.24 09:51

델파이(C++빌더) XE6에 추가된 BaaS 클라이언트 컴포넌트 중 TBackendFiles 컴포넌트를 활용해 BaaS에 파일을 업로드 하고 다운로드 하는 샘플입니다.

개인적으로 큰 관심을 갖는 BaaS에 샘플이 하나하나 추가되어 기분이 좋네요^^

(앞으로 다양한 활용방안과 잠재력을 갖고 있는 서비스라고 생각합니다.)


Uploading images to the cloud with BaaS in RAD Studio XE6





관련자료



저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/데이터 엑세스 BAAS, C++빌더, Delphi, XE6

  1. Blog Icon
    관심

    안녕하세요~~!!
    테스트 프로그램 작업중에 궁금한점이 있어서 ^^
    지금 서버에서 쿼리 실행해서 결과값을 TLabel 로 결과값을 뿌려주고 있는데 한글이 다 깨져 나오네요..
    StringGrid 로 보내면 잘 나오는데.. 무슨 설정을 해줘야 한글이 잘 나올까요?
    혹시 아시나요? ~~!!

  2. 이 내용과 관련이 없는 질문이신것 같아요 ㅠㅜ

    서버 측은 어떻게 구성되었나요? DataSnap인가요?
    DataSnap으로 처리해도 Label에 표시할 경우 문제가 되지 않습니다.

    여러모로 테스트 시도해 보시길 바래요^^

  3. Blog Icon
    델사랑

    안녕하세요?
    안드로이드 스마트폰(삼성 갤럭시 3)에서 테스트해 보는데
    업로드시에
    no mapping for the unicode character exists in the target multibyte code page 메시지가 뜨고
    더이상 진행되지 않는 현상인데, 무엇을 잘 못 한 것일까요?
    감사합니다.

  4. 아 그렇군요. 한글 캐릭터셋에 이슈가 있는 것 같은데요.
    저도 테스트 진행하고 답변을 드리도록 하겠습니다.

  5. Blog Icon
    관심

    죄송합니다... 글들 잘 보고 있고.. 따라해 보면서 다른 궁금증이 생긴지라.. ^^
    전송 방식은 Indy 를 이용하고 있고요...
    주고 받기 잘되고는 있는데.. 유독 한글만.. 쩝...

  6. Indy 방식으로 웹서비스와 연동하시는 것 같습니다.
    아마 웹서비스가 UTF8로 되어 있을 경우

    아래 문서에서 UTF8Decode 메소드를 이용하면 델파이에서 사용하는 Unicode(UTF16)으로 변환하면 잘 될 것 같아요. 테스트 해보시기 바랍니다.
    http://docwiki.embarcadero.com/RADStudio/XE6/en/UTF-8_Conversion_Routines

RESTful 웹서비스 연동(Rest Client 소개)

2014.02.24 23:21

현재 대상 서비스(discogs.com)에 인증 인터페이스가 추가되어 예제를 따라하면 인증오류가 발생합니다. 하지만 이 글을 통해 REST Client의 개념을 잡는데 도움이 될 것입니다.



안녕하세요. 험프리.김현수입니다.


모바일 개발 세미나 및 자료들에서 원격 데이터 연결 시 DataSnap을 강조하다 보니 원격지 데이터 연결 시 DataSnap만 써야 되는 것으로 오해가 종종있어, 기존 구성된 사이트에 RESTful(Http 요청)방식으로 연결하는 방법을 소개합니다.


여지껏 원격지 데이터를 받아오기 위해 일반적으로는 IdHttp 컴포넌트를 이용해 Get(또는 Post)하여 Response 데이터를 받아온 후 Parser를 이용해 데이터를 파싱하고, 파싱된 데이터를 사용하는 방법등 코딩 위의 방법이 대부분이었습니다.


이번에 소개해 드릴 컴포넌트는 XE5에서 새로 추가된 REST 컴포넌트입니다.


REST 컴포넌트는 RestClient, RestRequest, RestResponse 3가지 주요 컴포넌트를 주축으로 구성되어 있으며, 요청과 응답을 객체화 시키고, 응답의 경우 RESTResponseDataSetAdapter를 연결하여 사용자의 데이터 파싱없이 리스트 및 그리드에서 사용할 수 있는 DataSource로 데이터를 자동변환합니다. 


Source(대상) REST API 분석 및 준비

▶ 대상 사이트 API 분석

음반정보 제공 사이트인 discogs에 오픈된 API를 이용하겠습니다. 그중 Search API를 사용합니다.


대상정보

  • Request : http://api.discogs.com/database/search?q=psy&type=master&per_page=20
  • Response : JSON 포맷으로 응답, "results" 항목 이후의 배열데이터 사용(Search API 참고


REST Debugger을 실행하고 아래와 같은 정보를 입력합니다.(REST Debugger 설치 및 사용방법)

  • URL : http://api.discogs.com
  • Resource : database/search?q={query}&type=master&per_page={page}
    • query : psy
    • page : 20

[Send Request] 버튼을 누르고 응답이 성공한 경우 하단의 [Body] 탭에서 Root Element에 "results"를 입력 후 [Apply] 버튼을 누른 후 [Tabular Data] 탭으로 이동 후 목록이 표시되었는지 확인합니다.


(※ REST Debugger도 REST 컴포넌트 기반으로 개발되어 있습니다.)

REST 컴포넌트 추가

▶ 모바일 프로젝트 생성 및 데이터모듈 추가

새로운 프로젝트 생성 후 Data Module을 추가합니다.(Project Manager > Project 선택 > Add New > Other > Data Module)

아래와 같이 컴포넌트를 추가하고 속성값을 변경합니다.


RESTClient

  • BaseURL : http://api.discogs.com

RESTRequest

  • Client : RESTClient1
  • Resource : database/search?q={query}&type=master&per_page={page}
  • Params : query=psy, page=20

RESTResponse

RESTResponseDataSetAdapter

  • DataSet : ClientDataSet1
  • Response : RESTResponse1
  • RootElement : results

ClientDataSet

위와 같이 구성 후 RESTRequest1을 더블클릭(또는 우측마우스 > Execute) 하면 성공 시 "Response: 200 - HTTP/1.1 200 OK" 메시지가 실패 시 오류메시지가 표시됩니다.

연결이 성공한 경우 RESTResponse1의 Content항목을 선택하시면 응답받은 데이터를 확인할 수 있습니다.(만약, 목록이외의 항목이 필요한 경우 RESTResponse 컴포넌트의  Content 항목을 활용할 수도 있습니다.)

UI 구성(ListView 및 검색항목 추가)

위의 Data Module을 폼의 Uses 절에 추가합니다.(File > Use unit 후 선택)

(유닛명 및 컨트롤 명은 임의로 명명하였으니 본인의 이름으로 사용하세요.)

▶ 리스트 추가(ListView), 데이터연결

빈 폼에 ListView 하나 추가 후 align 속성을 "alClient"로 변경해 목록이 가득차도록 합니다. 

아래와 같이 DataModule의 ClientDataSet과 ListView의 LiveBinding을 연결해 리스트에 데이터를 표시합니다.


Live Binding 윈도우 표시하기 - Object Inspector 아래 Bind Visually를 누르면 메인폼 아래에 표시됩니다.


사실 위와 같이만 구성해도 얼마나 쉽게 원격지의 데이터를 사용할 수 있는지 느낌이 오실거에요.

▶ 검색 항목 추가

그럼 추가로 검색 UI를 추가하고 검색 인터페이스를 연동해보겠습니다.

검색 메소드 추가

DataModule 유닛의 public 절에 아래와 같이 검색용 Search메소드를 추가합니다.

type
  TDataModule9 = class(TDataModule)
    RESTClient1: TRESTClient;
    RESTRequest1: TRESTRequest;
    RESTResponse1: TRESTResponse;
    RESTResponseDataSetAdapter1: TRESTResponseDataSetAdapter;
    ClientDataSet1: TClientDataSet;
  private
    { Private declarations }
  public
    { Public declarations }
    procedure Search(AKeyword: string);
  end;

var
  DataModule9: TDataModule9;

implementation

{%CLASSGROUP 'FMX.Controls.TControl'}

{$R *.dfm}

{ TDataModule9 }

procedure TDataModule9.Search(AKeyword: string);
begin
    RESTRequest1.Params.ParameterByName('query').Value := AKeyword;
    RESTRequest1.Execute;
end;

위의 RESTRequest1.Params를 변경하고 실행(RESTRequest1.Execute;)하는 동작으로 요청 파라메터를 변경할 수 있습니다.

UI 추가

검색 키워드 입력을 위해  TEdit를 Form에 추가합니다.(가끔 ListView에 추가하는 경우가 있으니 주의하세요)

align 속성을 "alTop"으로 변경합니다.


Edit의 OnChange에 아래와 같이 코딩합니다.

procedure TForm8.Edit1Change(Sender: TObject);
begin
  DataModule9.Search(Edit1.Text);
end;

배포 및 확인

REST 컴포넌트를 이용한 경우 내부적으로 MIDAS를 이용하기 때문에 MIDAS Library를 함께 배포해야 합니다.

배포 메뉴에서 기능파일 추가(Project > Deployment > Add featured files) 메뉴를 이용해 MIDAS Library를 추가합니다.



기기배포 등의 확인 단계는 설명을 생략하니 각자의 디바이스에서 확인해 보시기 바랍니다.


목록표시 및 요청 등의 인터페이스를 설명하였습니다. 등록요청 등의 인터페이스는 위의 내용을 조금더 확장하시면 충분히 구현가능 하실 것 같습니다.


다음에는 XML을 이용한 외부 서비스 연동에 대해서도 살펴보도록 하겠습니다.


감사합니다.


관련정보


추가정보



    저작자 표시 비영리 동일 조건 변경 허락
    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    험프리.김현수 험프리.김현수 Firemonkey/데이터 엑세스 Firemonkey, FMX, JOSN, REST Debugger, RESTCilient, RESTful, 파이어몽키

    REST Debugger 설치 및 사용안내(RESTful interface 분석도구)

    2014.02.24 16:25

    원격지의 데이터와 연동하는 경우 RESTfull 방식으로 많이 구현됩니다. 델파이 및 C++빌더도 외부연동 시 RESTful 방식도 많이 사용되는데요. 이때 유용한 툴이 이미 엠바카데로 툴에 포함되어 있지만, 외부적으로 공개가되어 있지 않아 소개해 드립니다.

    REST Debugger

    바로, REST Debugger라는 툴인데요. 저도 모바일에서 외부 인터페이스 연동 시 인터페이스 분석에 꽤 유용하게 사용하고 있습니다. 


    툴 경로: 

     - C:\Program Files (x86)\Embarcadero\RAD Studio\n.n\bin\RESTDebugger.exe


    아래와 같은 모습을 하고 있습니다.


    REST Debugger는 아래와 같은 기능이 포함되어 있습니다.

    • Request
      • base-url 관리
      • Parameter 관리
      • 요청정보 파일 관리
    • Response
      • 응답코드 확인
      • 응답시간 확인
      • 응답내용 확인
      • 응답코드 목록확인(Root element 선택)
    • 인증정보 추가
    • Proxy 사용

    사용법

    간단한 사용법을 안내해 드릴게요.


    음원정보 제공 사이트인 http://www.discogs.com을 기반으로 설명합니다.

    discogs에서는 오픈API를 제공하므로 http://api.dicogs.com/database/search?q=psy&type=master&per_page=20 페이지를 기반으로 설명합니다.

    자세한 정보는 API 페이지(http://www.discogs.com/developers/)를 참고하시면 더 많은 정보를 확인하실 수 있습니다.


    ▶ Request

    • 상단 Request 항목의 [Request] 탭으로 이동 후 URL항목을 을 입력합니다.

      • URL : http://api.discogs.com

    • Site의 Base url을 입력하는 과정입니다.


      • [Parameters] 탭으로 이동 후 Resource 항목과 Request Parameters 항목을 입력합니다.
        • Resource : database/search?q={query}&type=master&per_page=20
        • Prameters : Resource의 대괄호("{}")에 포함된 항목을 추가합니다.
          • Resource를 입력하면 자동으로 Parameters항목이 추가됩니다.
          • 목록을 선택 후 Value를 "psy"로 입력합니다.
      • Send Request 버튼을 통해 정보요청
      • 입력한 내용을 관리하는 버튼을 제공합니다.
        • New Request : 새로운 요청 입력
        • Load Request : 기존 요청파일(*.json) 불러오기
        • Save Request : 요청내용을 파일로 저장하기


      • Respose 항목에 응답결과가 표시됩니다.
      • [Body] 탭으로 이동 후 JSON Root Element 항목에 "results"를 입력하여 "results"이후의 값으로 적용


      목록데이터는 [Tabular Data] 탭에서 그리드로 확인할 수 있습니다.


      실행파일 및 소스코드가 RAD Studio설치 경로에 있으니 개발에 참고하시면 아주 좋을 것 같습니다.


      경로


      • 실행파일 : C:\Program Files (x86)\Embarcadero\RAD Studio\n.n\bin\RESTDebugger.exe
      • 소스코드 : C:\Program Files (x86)\Embarcadero\RAD Studio\n.n\source\data\rest\restdebugger


      참고문서


      • http://docwiki.embarcadero.com/RADStudio/XE5/en/Installing_the_REST_Debugger
      • http://docwiki.embarcadero.com/RADStudio/XE5/en/REST_Debugger_Tool
      • http://docwiki.embarcadero.com/RADStudio/XE5/en/Using_the_REST_Debugger

      추가정보



      저작자 표시 비영리 동일 조건 변경 허락
      신고
      크리에이티브 커먼즈 라이선스
      Creative Commons License

      험프리.김현수 험프리.김현수 Firemonkey/데이터 엑세스 RESTDebugger, RESTful

      1. Blog Icon

        비밀댓글입니다

      FireDAC을 이용해 InterBase database 생성하기

      2014.02.18 16:02


      Database를 미리 생성하지 않고 실행 시 파일을 생성하는 팁입니다.

      배포를 신경쓰지 않고 로컬DB를 사용하니 매우 편할 것 같네요^^


      아래 링크에서 자세한 내용을 확인하세요.

      http://blogs.embarcadero.com/stephenball/2014/02/14/creating-an-interbase-database-on-the-fly-with-firedac/





      저작자 표시 비영리 동일 조건 변경 허락
      신고
      크리에이티브 커먼즈 라이선스
      Creative Commons License

      험프리.김현수 험프리.김현수 Firemonkey/데이터 엑세스 Firemonkey, IB, IBLite, Interbase, Interbase database

      Image URL로 TImage에 이미지 로드

      2013.12.13 00:37

      웹에 있는 이미지를 표시해야 할 것 같아 간단하게 라이브러리 형태로 만들었어요.

      TBitmap을 class helper로 확장했습니다.

      소스가 몇 줄 되지 않으니 설명은 생략합니다.


      iOS(아이폰5), Android(Nexus 7), 윈도우(Windows 7)에서 모두 정상 동작 확인했습니다.


      참고하세요^^







      사용법


      procedure TForm3.Button1Click(Sender: TObject);
      var
        Size: Int64;
      begin
        Image1.Bitmap.LoadFromUrl('http://cfile2.uf.tistory.com/image/2353573E529FDAAC032731', Size);
      
        Memo1.Lines.Add('1 : ' + Format('W: %d, H: %d, S: %d', [Image1.Bitmap.Width, Image1.Bitmap.Height, Size]))
      end;
      
      procedure TForm3.Button2Click(Sender: TObject);
      begin
        Image1.Bitmap.LoadFromUrl('http://cfile2.uf.tistory.com/image/2353573E529FDAAC032731');
      
        Memo1.Lines.Add('2 : ' + Format('W: %d, H: %d', [Image1.Bitmap.Width, Image1.Bitmap.Height]))
      end;
      
      procedure TForm3.Button3Click(Sender: TObject);
      begin
        Image1.Bitmap.LoadThumbnailFromURL('http://cfile2.uf.tistory.com/image/2353573E529FDAAC032731', 100, 100);
      
        Memo1.Lines.Add('3 : ' + Format('W: %d, H: %d', [Image1.Bitmap.Width, Image1.Bitmap.Height]))
      end;


      쓰레드로 처리하도록 변경해 사용법의 첫번째 사이즈 정보를 얻어오는 부분은 제외됩니다.

      (깃허브의 소스코드를 참고하세요. https://github.com/hjfactory/FMX.Devgear)

      구현부

      unit FMX.Devgear.HelperClass;
      
      interface
      
      uses
        System.Classes, FMX.Graphics;
      
      type
        TBitmapHelper = class helper for TBitmap
        public
          procedure LoadFromUrl(AUrl: string);
      
          procedure LoadThumbnailFromUrl(AUrl: string; const AFitWidth, AFitHeight: Integer);
        end;
      
      implementation
      
      uses
        System.SysUtils, System.Types, IdHttp, IdTCPClient, AnonThread;
      
      procedure TBitmapHelper.LoadFromUrl(AUrl: string);
      var
        _Thread: TAnonymousThread;
      begin
        _Thread := TAnonymousThread.Create(
          function: TMemoryStream
          var
            Http: TIdHttp;
          begin
            Result := TMemoryStream.Create;
            Http := TIdHttp.Create(nil);
            try
              try
                Http.Get(AUrl, Result);
              except
                Result.Free;
              end;
            finally
              Http.Free;
            end;
          end,
          procedure(AResult: TMemoryStream)
          begin
            if AResult.Size > 0 then
              LoadFromStream(AResult);
            AResult.Free;
          end,
          procedure(AException: Exception)
          begin
          end
        );
      end;
      
      procedure TBitmapHelper.LoadThumbnailFromUrl(AUrl: string; const AFitWidth,
        AFitHeight: Integer);
      var
        Bitmap: TBitmap;
        scale: Single;
      begin
        LoadFromUrl(AUrl);
        scale := RectF(0, 0, Width, Height).Fit(RectF(0, 0, AFitWidth, AFitHeight));
        Bitmap := CreateThumbnail(Round(Width / scale), Round(Height / scale));
        try
          Assign(Bitmap);
        finally
          Bitmap.Free;
        end;
      end;
      
      end.


      안드로이드 환경에서 8bit GIF 이미지가 표시되지 않는경우

      아래와 같이 FMX.Graphics.Android.pas 파일을 수정하세요. 결과적으로 IsGIFStream 메소드에서 GIF 여부를 파악하는 부분이 잘못되어 있어 8bit GIF 표시에 문제가 있습니다.

      class function TBitmapCodecAndroid.IsGIFStream(const Stream: TStream): Boolean;
      const
        IDCharCount = 3;
      type
        GIFByteArray = array[0..IDCharCount-1] of Byte;
      const
        GifBytes: GIFByteArray = ($47, $49, $46); // G I F
      var
        I: Integer;
        PrevPosition: Int64;
        ReadBytes: GIFByteArray;
      begin
        if (Stream = nil) or (Stream.Size < IDCharCount) then
          Exit(False);
      
        Result := True;
        PrevPosition := Stream.Position;
        try
          Stream.ReadBuffer(ReadBytes, IDCharCount);
          for I := Low(ReadBytes) to High(ReadBytes) do
            if ReadBytes[I] <> GifBytes[I] then
              Exit(False);
        finally
          Stream.Position := PrevPosition;
        end;
      end;

      저작자 표시 비영리 동일 조건 변경 허락
      신고
      크리에이티브 커먼즈 라이선스
      Creative Commons License

      험프리.김현수 험프리.김현수 Firemonkey/데이터 엑세스 class helper, Firemonkey, Image url, TImage

      1. Blog Icon
        epilogs

        빌드해보니 아래와 같은 오류가 발생하네요.
        뭔가 설치를 추가로 해야하나보군요..^^

        [dcc32 Fatal Error] Unit3.pas(27): F1026 File not found: 'C:\Users\홍길동\Downloads\BitmapUrl\FMX.Devgear.Extentsions.dcu'

      2. 폼의 유닛에서 uses에서 오타가 있었습니다.
        다시 올렸구요. uses 절에 FMX.Devgear.Extensions;로 변경하시면 됩니다^^

      3. Blog Icon
        8비트24비트

        BitmapUrlDemo 소스로 테스트해본 결과

        안드로이드 환경에서는 가져오는 이미지가 8비트 형식일때 그림이 제대로 불러와지지 않네요
        ex) http://img.naver.net/static/www/u/2013/0731/nmms_224940510.gif

        24비트(jpeg)는 잘 보여지구요. (예제로 쓰인 그림도 이 경우더군요)

        윈도우에서는 잘만되니 모바일에서는 무언가 더 설정이 필요한건가요??

      4. 본문 하단의 내용을 참고하세요.