업데이트내역
2019-11 : 컨텐트 타입 변경 - application/json >CONTENTTYPE_APPLICATION_VND_EMBARCADERO_FIREDAC_JSON

 

 

이 글에서는 데이터셋 기반으로 일괄 데이터 처리하는 REST API 엔드포인트를 구현하고, 연동하는 내용을 설명합니다.

데이터셋 기반 REST API

 

데이터셋(TDataSet)은 데이터들의 집합으로, FireDAC의 데이터 셋(TFDDataSet)은 데이터셋의 내용을 JSON 포맷으로 저장하고, 불러오는 기능을 제공합니다. 

 

이 기능을 활용해 REST API의 JSON 포맷을 손쉽게 개발할 수 있습니다.

 

데이터셋 기반 REST API의 특징(장/단점)은 다음과 같습니다.

1) (장점) 매우 신속하고, 손쉽게 REST API 서버, 클라이언트를 개발할 수 있습니다.

데이터를 JSON 포맷으로 변환하는 코드가 대단히 짧아 집니다.

 

2) (단점)JSON 포맷을 직접 설정할 수 없습니다.

FireDAC의 JSON 저장 기능을 이용하기 때문에 FireDAC의 JSON 포맷을 그대로 사용합니다.

필요한 항목을 추가, 변경하기 쉽지 않습니다.

또한, REST 아키텍처의 규칙과 일부 다르게 구현해야 합니다.(GET과 POST 메소드만 사용합니다.)

(개인적으로 델파이 클라이언트-서버 환경으로 개발하는 경우 활용하는 것이 좋다는 의견입니다.)

 

이 글에 앞서 다음 내용을 선행학습하시기 바랍니다.

 

이 글에서는 다음 내용을 다룹니다.

  • [이론] 데이터셋과 JSON
    • JSON 포맷 저장/불러오기
    • CachedUpdates와 TFDSchemaAdapter
  • [실습] 데이터셋 기반 REST API 개발하기
    • 데이터셋 기반 REST API 서버 개발하기
    • 데이터셋 기반 REST API 클라이언트 개발하기

 

이 글의 실습에서는 도서대여 프로그램 만들기에서 사용한 데이터베이스를 사용합니다. 다음 링크에서 데이터베이스 구조를 확인하고, DB 파일을 다운로드 받으시기 바랍니다.

 

데이터셋과 JSON

FireDAC의 데이터셋(TFDDataSet)은 JSON 포맷으로 데이터 저장하고, JSON 포맷의 데이터를 불러오는 기능을 제공합니다.

 

JSON 포맷 저장/불러오기

FireDAC 데이터셋(TFDDataSet)은 SaveToStream, LoadFromStream 메소드를 이용해 데이터를 저장하고 불러오는 기능을 제공합니다. 파라메터로 스트림(TStream)과 데이터포맷을 지정할 수 있으며, 데이터포맷으로는 XML, JSON, Binary 중 선택할 수 있습니다.

 

아래와 같은 코드로 데이터셋의 데이터를 JSON 포맷으로 저장하고, 불러올 수 있습니다.

var

  Stream: TMemoryStream;

begin

  Stream := TMemoryStream.Create;

  try

    FDQuery1.SaveToStream(Stream, TFDStorageFormat.sfJSON);

 

    Stream.Position := 0;

    FDQuery1.LoadFromStream(Stream, TFDStorageFormat.sfJSON);

    FDQuery1.ApplyUpdates;

  finally

    Stream.Free;

  end;

 

단, 데이터를 로드(LoadFromStream)의 경우 데이터셋의 CachedUpdates 속성이 True로 설정되어 있어야 합니다.

 

참고: 위 코드를 사용하기 위해서는 TFDStanStorageJSONLink 컴포넌트를 화면(또는 데이터모듈)에 올려놓거나, "FireDAC.Stan.StorageJSON" 유닛을 유즈절에 추가해야 합니다.

 

알아야 하는 주요 컴포넌트, 속성

캐쉬 업데이트 속성(CachedUpdates)

데이터셋의 CachedUpdates 속성을 사용하면 데이터셋의 Post 메소드 호출 시 데이터셋의 캐쉬(메모리)에 임시로 저장되고, ApplyUpdates 메소드 호출 시 캐쉬 데이터가 실제 DBMS에 변경된 데이터가 적용됩니다. 즉, 캐쉬 기반으로 데이터 조작 후 일괄 적용할 수 있습니다.

 

스키마어댑터 컴포넌트(TFDSchemaAdapter)

스키마어댑터(TFDSchemaAdapter) 컴포넌트는 중앙 캐쉬 업데이트를 지원합니다. 스키마어댑터 컴포넌트(TFDSchemaAdapter) 추가 후 데이터셋의 SchemaAdapter 속성에 할당하면, 스키마어댑터 컴포넌트를 통해 여러 데이터셋의 데이터 변경을 순차적으로 처리할 수 있습니다.

 

스키마어댑터 컴포넌트는 Open, Close, SaveToStream, LoadFromStream, ApplyUpdates 등의 메소드를 제공합니다. 이 메소드를 호출하면 스키마어댑터 컴포넌트와 연결된 여러개의 데이터셋의 데이터를 열기/닫기, 저장/불러오기, 일괄적용 할 수 있습니다.

 

메모리 기반 데이터셋 컴포넌트(TFDMemTable)

메모리 상에서 데이터 처리를 지원합니다. 이번 실습의 클라이언트는 메모리 기반 데이터셋 컴포넌트에 서버에서 받은 데이터를 보관하고, 조작(추가, 수정, 삭제) 후 변경된 내용을 서버에 저장 요청합니다.


EMS FireDAC 클라이언트 컴포넌트(TEMSFireDACClient)

EMS 서버에 데이터셋 기반 REST End-point와 연동하는 컴포넌트입니다. EMS 프로바이더, 리소스, 스키마어댑터 속성을 제공합니다.

  • EMS 프로바이더 : 접속할 EMS 서버 정보 설정
  • 리소스 : 연결할 리소스 설정
  • 스키마어댑터 : 데이터를 처리 위임

GetDatas, PostUpdates 메소드를 제공합니다.

  • GetDatas : 서버에서 데이터를 요청합니다.
  • PostUpdates : 클라이언트에서 변경된 데이터를 서버에 저장 요청합니다.

 

[실습] 데이터셋 기반 REST API 개발하기

이 실습에서는 도서대여 프로그램의 도서정보와 사용자정보를 제공하는 REST API 서버를 개발하고, REST API 서버에서 받은 도서 정보를 편집 후 저장하는 클라이언트 프로그램 개발을 실습합니다.

 

REST API 자료 구조는 FireDAC 데이터셋의 JSON 포맷으로 저장 기능을 사용합니다.

 

데이터셋 기반 REST API 서버 개발하기

이 실습에서는 "도서대여 프로그램 만들기"의 도서(BOOK)와 사용자(USERS) 테이블 데이터를 제공하는 REST API 서버 개발을 실습합니다.

 

다음 순서로 진행합니다.

 

1) EMS 패키지 프로젝트를 생성합니다.

2) 데이터 제공하기 위해 컴포넌트 추가 및 설정합니다.

3) GET 메소드를 구현해 데이터 제공 기능을 개발합니다.

4) POST 메소드를 구현해 데이터 저장 기능을 개발합니다.

 

EMS 패키지 프로젝트 생성

EMS 패키지 프로젝트를 생성(File > New > Other > Delphi Projects > EMS > EMS Package) 합니다.

 

리소스 이름을 "datasets"로 지정합니다.

 

 

엔드포인트는 Get과 Post를 선택합니다.

 

프로젝트와 소스코드를 저장합니다.

 

데이터 제공을 위한 컴포넌트 추가 및 설정

아래 목록을 참고해 데이터 연결을 위한 컴포넌트 추가 및 설정합니다.

  • conBookRental: TFDConnection
    • 도서대여 프로그램 데이터베이스 연결
  • scmAdtBookRental: TFDSchemaAdapter
  • qryBook: TFDQuery
    • CachedUpdates = True
    • Connection = conBookRental
    • SQL = "SELECT BOOK_SEQ, BOOK_TITLE, BOOK_AUTHOR, BOOK_ISBN, BOOK_PRICE, BOOK_LINK FROM BOOK"
    • SchemaAdapter = scmAdtBookRental
    • UpdateOptions.AutoIncFields = BOOK_SEQ
  • qryUser: TFDQuery
    • CachedUpdates = True
    • Connection = conBookRental
    • SQL = "SELECT USER_SEQ, USER_NAME, USER_BIRTH, USER_SEX, USER_PHONE, USER_MAIL FROM USERS"
    • SchemaAdapter = scmAdtBookRental
    • UpdateOptions.AutoIncFields = USER_SEQ
  • FDStanStroageJSONLink1: TFDStanStorageJSONLink

 

엔드포인트 추가

기본으로 생성한 엔드포인트에는 전체 데이터를 제공, 저장하는 용도로 사용합니다.

"/books/" 엔드포인트를 추가해 도서 정보만 제공, 저장하는 과정도 소개합니다.(이 엔드포인트는 클라이언트 실습에서 사용하지 않습니다.)

  published

    procedure Get(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);

    procedure Post(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);

    [ResourceSuffix('/books/')]

    procedure GetItemBooks(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);

    [ResourceSuffix('/books/')]

    procedure PostBooks(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);

  end;

 

Get / GetItemBooks

(2019-11 업데이트) 컨텐츠 타입 변경 : application/json >CONTENTTYPE_APPLICATION_VND_EMBARCADERO_FIREDAC_JSON

uses 절에 "REST.Types" 추가 필요

procedure TDatasetResource1.Get(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);

var

  Stream: TMemoryStream;

begin

  Stream := TMemoryStream.Create;

  try

    scmAdtBookRental.SaveToStream(Stream, TFDStorageFormat.sfJSON);

 

//    AResponse.Body.SetStream(Stream, 'application/json'True);

    AResponse.Body.SetStream(Stream, CONTENTTYPE_APPLICATION_VND_EMBARCADERO_FIREDAC_JSONTrue);

  except

    Stream.DisposeOf;

  end;

end;

 

procedure TDatasetResource1.GetItemBooks(const AContext: TEndpointContext;

  const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);

var

  Stream: TMemoryStream;

begin

  Stream := TMemoryStream.Create;

  try

    qryBook.SaveToStream(Stream, TFDStorageFormat.sfJSON);

 

//    AResponse.Body.SetStream(Stream, 'application/json'True);

    AResponse.Body.SetStream(Stream, CONTENTTYPE_APPLICATION_VND_EMBARCADERO_FIREDAC_JSONTrue);

  except

    Stream.DisposeOf;

  end;

end;

Get과 GetItemBooks 메소드는 데이터셋의 데이터를 스트림으로 JSON 포맷으로 저장 후 응답합니다.

 

Get은 스키마 어댑터와 연결된 데이터셋 들의 데이터(도서, 사용자)를 일괄 제공합니다.

GetItemBooks는 도서 쿼리(데이터셋)의 데이터만 제공합니다.

 

Post / PostBooks

(2019-11 업데이트) 컨텐츠 타입 변경 : application/json >CONTENTTYPE_APPLICATION_VND_EMBARCADERO_FIREDAC_JSON

uses 절에 "REST.Types" 추가 필요

 

procedure TDatasetResource1.Post(const AContext: TEndpointContext;

  const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);

var

  Stream: TStream;

begin

//  if not SameText(ARequest.Body.ContentType, 'application/json'then

  if not SameText(ARequest.Body.ContentType, CONTENTTYPE_APPLICATION_VND_EMBARCADERO_FIREDAC_JSONthen

    AResponse.RaiseBadRequest('content type');

  if not ARequest.Body.TryGetStream(Stream) then

    AResponse.RaiseBadRequest('Invailed stream');

 

  Stream.Position := 0;

  scmAdtBookRental.LoadFromStream(Stream, TFDStorageFormat.sfJSON);

  scmAdtBookRental.ApplyUpdates;

end;

 

procedure TDatasetResource1.PostBooks(const AContext: TEndpointContext;

  const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);

var

  Stream: TStream;

begin

//  if not SameText(ARequest.Body.ContentType, 'application/json'then

  if not SameText(ARequest.Body.ContentType, CONTENTTYPE_APPLICATION_VND_EMBARCADERO_FIREDAC_JSONthen

    AResponse.RaiseBadRequest('content type');

  if not ARequest.Body.TryGetStream(Stream) then

    AResponse.RaiseBadRequest('Invailed stream');

 

  Stream.Position := 0;

  qryBook.LoadFromStream(Stream, TFDStorageFormat.sfJSON);

  qryBook.ApplyUpdates;

end;

Post와 PostBooks 메소드는 요청 바디의 컨텐트타입을 확인하고, 바디의 컨텐트를 스트림으로 가져옵니다.(TryGetStream) 

 

Post 메소드는 스키마 어댑터로 데이터가 담긴 스트림의 데이터를 읽고, DB에 적용(ApplyUpdates)합니다.

PostBooks 메소드는 도서 쿼리에서 데이터가 담긴 스트림의 데이터를 읽고, DB에 적용(ApplyUpdates)합니다.

 

데이터셋 기반 REST API 클라이언트 개발하기

이 실습에서는 위에서 개발한 데이터셋 기반 REST API 서버와 연결해 데이터를 불러오고, 데이터 변경(추가, 수정, 삭제) 후 서버에 저장 요청합니다.

 

멀티-디바이스 애플리케이션(파이어몽키) 프로젝트로 진행합니다.(VCL 폼 애플리케이션에서도 같은 방법으로 개발할 수 있습니다.)

 

다음 순서로 진행합니다.

1) 프로젝트 생성 및 화면 개발

2) 데이터 연결 기능 개발

3) UI 컨트롤과 데이터 연결

4) 각 버튼 이벤트 개발

 

프로젝트 생성 및 화면 개발

멀티-디바이스 애플리케이션 프로젝트를 생성(File > New > Multi-Device Application)합니다.

(앞에서 만든 REST API 서버와 프로젝트 그룹으로 묶어 놓으면 편리합니다. Project Group > Add New Project)

 

아래 그림을 참고해 화면을 개발합니다.

 

에디트(TEdit)의 이름을 각각 "edtTitle, edtAuthor, edtISBN, edtPrice, edtLink"로 변경합니다.

버튼(TButton)의 이름을 각각 "btnSearch, btnDelete, btnAppend, btnSave, btnCancel"로 변경합니다.

 

데이터 연결 기능 구현

프로젝트에 데이터 모듈 추가 후 EMS 서버와 연결할 컴포넌트를 추가하겠습니다.

 

프로젝트에 데이터 모듈을 추가(File > New > Other > Delphi Files > Data Module)합니다.

데이터 모듈의 이름을 변경하고, 파일을 저장합니다.(저는 이름은 dmData, 파일이름은 DataAccessModule.pas로 했습니다.)

 

 

아래 그림과 표를 참고해 컴포넌트를 추가합니다.

 

 상위 오브젝트

 오브젝트

 속성

 값(또는 설명)

 dmData

 (데이터 모듈)

 EMSProvider1

 URLHost

 localhost(EMS 서버 주소)

 URLPort  8080

 FDSchemaAdapter1

 

 

 EMSFireDACClient1  Provider

 EMSProvider1

 Resource

 datasets

 SchemaAdapter  FDSchemaAdapter1

 tblAdtBook

 (TFDTableAdapter)

 DatSTableName

 qryBook

 Name

 tblAdtBook

 SchemaAdapter  FDSchemaAdapter1

 qryBook
 (TFDMemTable)

 Adapter

 tblAdtBook

 CachedUpdates  True

 tblAdtUser

 (TFDTableAdapter)

 DatSTableName

 qryUser

 Name  tblAdtUser
 SchemaAdapter  FDSchemaAdapter1

 qryUser

 (TFDMemTable)

 Adapter

 tblAdtUser

 CachedUpdates

 True

 FDGUIxWaitCursor1

   

 

 

UI 컨트롤과 데이터 연결

라이브 바인딩 기술을 이용해 UI 컨트롤에 데이터를 표현합니다.

 

 

메모리 테이블에 필드 정보 추가하기

에디트(TEdit) 등의 UI 컨트롤과 데이터 연결 시 데이터셋의 필드 정보를 이용합니다. 메모리 테이블의 경우 자체적으로 필드 정보를 생성할 수 없어 아래 절차를 통해 메모리 테이블에 필드 정보를 추가합니다.

 

1) REST API 서버 프로젝트를 열고, 쿼리 컴포넌트(TFDQuery)의 Field Editor 메뉴를 선택합니다.

 

2) Field Editor 창의 팝업 메뉴에서 Add all fields 메뉴를 선택해 필드 정보를 추가합니다.

 

3) 추가된 필드 정보를 모두 선택 후 (클립보드로)복사 합니다.

 

4) 현재 프로젝트의 데이터 모듈로 돌아와 메모리 테이블 팝업 메뉴에서 Field Editor 메뉴를 선택합니다.

 

5) Field Editor 창에 붙여넣기 합니다.

 

위 과정을 메모리 테이블 별로 진행합니다.

 

라이브 바인딩으로 연결

폼 화면 유닛의 유즈(uses) 절에 데이터 모듈 유닛을 추가합니다.

 

폼 화면에서 라이브 바인딩 디자이너를 표시(View > Tool Windows > LiveBindings Designer)합니다.

 

아래 그림을 참고해 UI 컨트롤과 데이터를 연결(Linking)합니다.

 

버튼 기능 구현

각 버튼의 클릭 이벤트를 아래 코드를 참조해 입력합니다.

procedure TForm1.btnSearchClick(Sender: TObject);

begin

  dmData.EMSFireDACClient1.GetData;

end;

 

procedure TForm1.btnDeleteClick(Sender: TObject);

begin

  dmData.qryBook.Delete;

end;

 

procedure TForm1.btnAppendClick(Sender: TObject);

begin

  dmData.qryBook.Append;

end;

 

procedure TForm1.btnSaveClick(Sender: TObject);

begin

  dmData.EMSFireDACClient1.PostUpdates;

end;

 

procedure TForm1.btnCancelClick(Sender: TObject);

begin

  dmData.qryBook.Cancel;

end;

조회와 저장은 서버의 데이터를 다뤄야 하기 때문에 EMSFireDACClient를

추가, 삭제, 취소는 로컬의 메모리 테이블(qryBook)을 다루도록 구현했습니다.

 

테스트

REST API 서버와 클라이언트 구현이 완료되었습니다. 간단한(?) 작업으로 REST API 서버를 제작하고, 클라이언트에서 데이터 조회, 추가, 편집, 삭제 기능을 개발할 수 있었습니다.

 

테스트는 다음 단계로 진행합니다.

1) REST API 서버 실행(EMS 패키지 프로젝트를 실행합니다.)

2) REST API 클라이언트 실행

3) [조회] 버튼으로 데이터 출력

4) 데이터 추가, 수정 후 [저장] 버튼 클릭

 

 

참고/관련 자료


번호 제목 글쓴이 날짜 조회 수
공지 [DelphiCon 요약] 코드사이트 로깅 실전 활용 기법 (Real-world CodeSite Logging Techniques) 관리자 2021.01.19 22591
공지 [UX Summit 요약] 오른쪽 클릭은 옳다 (Right Click is Right) 관리자 2020.11.16 21024
공지 [10.4 시드니] What's NEW! 신기능 자세히 보기 관리자 2020.05.27 23082
공지 RAD스튜디오(델파이,C++빌더) - 고객 사례 목록 관리자 2018.10.23 28881
공지 [데브기어 컨설팅] 모바일 앱 & 업그레이드 마이그레이션 [1] 관리자 2017.02.06 30050
공지 [전체 목록] 이 달의 기술자료 & 기술레터 관리자 2017.02.06 25397
공지 RAD스튜디오(델파이, C++빌더) - 시작하기 [1] 관리자 2015.06.30 46348
공지 RAD스튜디오(델파이,C++빌더) - 모바일 앱 개발 사례 (2020년 11월 업데이트 됨) 험프리 2014.01.16 182316
763 안드로이드에서 Firebase SDK 용 Google 로그인 사용하기 험프리 2017.08.04 698
762 첨부파일을 포함한 이메일 전송하기(iOS, 안드로이드, 윈도우) file 험프리 2017.08.04 738
761 이 달의 기술자료 - 2017년 08월 험프리 2017.07.28 562
760 세일즈포스(Salesforce) 데이터, RAD스튜디오 데이터 익스플로러로 연동하기 file 관리자 2017.07.26 880
759 Tools API를 사용해 IDE를 확장할 수 있습니다. file 험프리 2017.07.20 579
758 파이어몽키(FireMonkey) vs. VCL 관리자 2017.07.18 2922
757 엔터프라이즈 커넥터로 '트위터' 연동하기 file 관리자 2017.07.17 976
756 엔터프라이즈 커넥터 - 엠바카데로의 새로운 솔루션 [1] file 관리자 2017.07.12 2037
755 겟잇 패키지 매니저를 사용하면 협업을 위한 프로젝트 공유와 전환이 간편해집니다. 험프리 2017.07.07 507
754 이 달의 기술자료 - 2017년 07월 험프리 2017.07.03 547
753 [업데이트][핫픽스][10.2 도쿄] FireMonkey의 Android 호환성 패치 험프리 2017.07.03 741
752 [발표자료] 20170623 최신OS와 멀티플랫폼 개발 전략 with RAD Studio [2] 관리자 2017.06.26 418
751 [고객 사례- 쇼핑, 모바일앱, 델파이] 매일 최저가 상품을 보여주는 'Daily Offer' 관리자 2017.06.21 743
750 RAD서버로 개발은 확장하면서도 비용을 절감하는 방법 (RAD서버 라이선스 유형별 정리) file 관리자 2017.06.20 1700
» [REST API][실습] 데이터셋 기반 REST API 개발하기 험프리 2017.06.13 2309
748 이 달의 기술자료 - 2017년 06월 file 험프리 2017.05.30 585
747 [발표자료] REST API 웹서비스 연동 관리자 2017.05.29 1515
746 [REST API][실습] REST API 클라이언트 개발하기(REST Client 이용) [2] 험프리 2017.05.23 7383
745 [REST API][실습] REST API 서버 개발하기(엔드포인트 구현, RAD 서버 이용) [5] 험프리 2017.05.23 4864
744 [REST API] REST API 이해하기 험프리 2017.05.23 15458