<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>나무를 심는 프로그래머</title>
    <link>https://blog.hjf.pe.kr/</link>
    <description>오픈소스와 델파이(파이어몽키)에 관심이 많은 개발자.
나눔을 실천하는 소프트웨어 개발 및 서비스를 꿈꾸고 있습니다.</description>
    <language>ko</language>
    <pubDate>Sat, 11 Apr 2026 21:53:51 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>험프리.김현수</managingEditor>
    <image>
      <title>나무를 심는 프로그래머</title>
      <url>https://t1.daumcdn.net/cfile/tistory/2353573E529FDAAC03</url>
      <link>https://blog.hjf.pe.kr</link>
    </image>
    <item>
      <title>[튜토리얼] RAD 서버를 활용해 데이터를 REST API로 서비스하기</title>
      <link>https://blog.hjf.pe.kr/522</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;제가 진행한 &lt;a href=&quot;https://welcome.devgear.co.kr/articles/%EB%B9%84%EB%94%94%EC%98%A4-%EC%84%B8%EB%AF%B8%EB%82%98/%EB%8D%B0%EB%B8%8C%EA%B8%B0%EC%96%B4-%EC%84%B8%EB%AF%B8%EB%82%98/%EC%98%A8%EB%9D%BC%EC%9D%B8-%EC%84%B8%EB%AF%B8%EB%82%98-what%E2%80%99s-new-rad%EC%8A%A4%ED%8A%9C%EB%94%94%EC%98%A4-11-r24/&quot;&gt;온라인 세미나-WHAT&amp;rsquo;S NEW! RAD스튜디오 11&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #353c41;&quot;&gt;&amp;nbsp;중&amp;nbsp;세션 2-1의 다시보기, 자료, 따라하기입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #353c41;&quot;&gt;샘플 데이터베이스(InterBase-FishFacts)의 데이터를 REST API로 서비스하는 과정을 익히고, 여러분의 데이터에도 적용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;핵심&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TEMSDataSetResource 컴포넌트(몇가지 속성 설정만으로):&amp;nbsp;데이터베이스 데이터를 REST API로 서비스하는 리소스 추가&lt;br /&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;268&quot; data-origin-height=&quot;194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y6yJJ/btrnR5qp3JX/TKz3xggi7lypmt8n2Tihtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y6yJJ/btrnR5qp3JX/TKz3xggi7lypmt8n2Tihtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y6yJJ/btrnR5qp3JX/TKz3xggi7lypmt8n2Tihtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY6yJJ%2FbtrnR5qp3JX%2FTKz3xggi7lypmt8n2Tihtk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;268&quot; height=&quot;194&quot; data-origin-width=&quot;268&quot; data-origin-height=&quot;194&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;커스텀 엔드포인트 작성: 이미지 제공과 같은 로직이 포함된 엔드포인트&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;ready&quot; data-ke-size=&quot;size26&quot;&gt;1. 준비하기&lt;/h2&gt;
&lt;h3 id=&quot;RAD+++++++++++++++++&quot; data-ke-size=&quot;size23&quot;&gt;RAD 서버 환경설정(최초 사용 시)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 따라하기는 RAD 서버를&lt;span&gt;&amp;nbsp;&lt;/span&gt;이용해 REST API를 제공합니다. RAD 서버를 처음 사용하는 경우 &quot;RAD 서버 개발환경 설정하기(준비 중)&quot;를 참고해 설정합니다.&lt;/p&gt;
&lt;h3 id=&quot;++++++++++++++&quot; data-ke-size=&quot;size23&quot;&gt;샘플 데이터 파일 다운로드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 따라하기는 인터베이스 용 &quot;Fish facts(어류도감) 데이터&quot;를 샘플 데이터로 사용합니다. 다음 링크에서 [Download] 버튼을 눌러 적절한 경로에 다운로드 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/devgear/FishFactsRSX/blob/main/Data/BIOLIFE.IB&quot;&gt;https://github.com/devgear/FishFactsRSX/blob/main/Data/BIOLIFE.IB&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;283&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Wh0la/btrnR5xb4UM/AKa6bYVZDvjVWVb4Cfd7bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Wh0la/btrnR5xb4UM/AKa6bYVZDvjVWVb4Cfd7bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Wh0la/btrnR5xb4UM/AKa6bYVZDvjVWVb4Cfd7bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWh0la%2FbtrnR5xb4UM%2FAKa6bYVZDvjVWVb4Cfd7bk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;926&quot; height=&quot;283&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;283&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&quot;step2&quot; data-ke-size=&quot;size26&quot;&gt;2. RAD 서버 패키지 프로젝트 생성하기&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IDE를 열고, File &amp;gt; New &amp;gt; Other 메뉴 클릭&lt;/li&gt;
&lt;li&gt;Delphi &amp;gt; RAD Server &amp;gt; RAD Server Package 선택 &amp;gt; [OK] 버튼 클릭&lt;br /&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ds2FgC/btrnN2WabCQ/T5Wku99rZ2Y1YIc7ijx1s1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ds2FgC/btrnN2WabCQ/T5Wku99rZ2Y1YIc7ijx1s1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ds2FgC/btrnN2WabCQ/T5Wku99rZ2Y1YIc7ijx1s1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fds2FgC%2FbtrnN2WabCQ%2FT5Wku99rZ2Y1YIc7ijx1s1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;316&quot; height=&quot;241&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;리소스를 포함한 패키지 생성&lt;br /&gt;&quot;Create package with resource&quot; 선택 &amp;gt; [Next &amp;gt;&amp;gt;] 버튼 클릭&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;리소스 이름 입력 및 파일 타입을 데이터 모듈로 선택(논비주얼 컴포넌트 사용에 필요)&lt;br /&gt;Resource name &quot;fishfacts&quot; 입력 &amp;gt; File type &quot;Data Module&quot; 선택 &amp;gt; [Next &amp;gt;&amp;gt;] 버튼 클릭&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;샘플 엔드포인트에서 이미지 제공에 사용할 GetItem만 선택&lt;br /&gt;GetItem만 선택 &amp;gt; [Finish] 버튼 클릭&lt;br /&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;523&quot; data-origin-height=&quot;524&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxnvMK/btrnJATpLwS/FOkZq8mLZspW6kkI9xYDB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxnvMK/btrnJATpLwS/FOkZq8mLZspW6kkI9xYDB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxnvMK/btrnJATpLwS/FOkZq8mLZspW6kkI9xYDB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxnvMK%2FbtrnJATpLwS%2FFOkZq8mLZspW6kkI9xYDB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;301&quot; data-origin-width=&quot;523&quot; data-origin-height=&quot;524&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Sample Endpoints: 프로젝트 생성 시 선택한 커스텀 엔드포인트 메소드 코드 자동 생성&lt;/li&gt;
&lt;li&gt;Database Endpoints: 사전 정의된 데이터베이스 연결의 테이블을 선택 해 데이터를 제공하는 리소스 자동 생성&lt;br /&gt;(이 따라하기에서는 이 과정을 수작업으로 진행)&lt;/li&gt;
&lt;li&gt;API Documentation: 코드를 이용해 Swagger API 문서 제공&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;프로젝트 생성 완료&lt;/li&gt;
&lt;li&gt;(선택사항) 프로젝트 저장
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트 파일: FishfactsPackage.dproj&lt;/li&gt;
&lt;li&gt;유닛 파일: BiolifeResource.pas&lt;/li&gt;
&lt;li&gt;(다른이름(및 형식)의 파일명으로 저장해도 문제 없음)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;step3&quot; data-ke-size=&quot;size26&quot;&gt;3. 데이터베이스 연결하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;1, 사전 준비하기&quot; 단계에서 다운로드 받은 인터베이스 데이터베이스와 연결합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&amp;gt; 이 따라하기에서는 인터베이스 데이터베이스와 연결하지만, 다른 DBMS와 연결해도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&amp;gt; 이 따라하기에서는 FireDAC을 이용해 데이터베이스와 연결하지만, 다른 데이터 엑세스 컴포넌트를 사용해도 됩니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터베이스 연결
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TFDConnection 컴포넌트를 데이터 모듈에 추가&lt;/li&gt;
&lt;li&gt;추가된 TFDConnection 컴포넌트를 더블클릭해 &quot;FireDAC Connection Editor&quot; 표시&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;데이터 연결 속성 설정&lt;br /&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;664&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8KPpo/btrnORmoJEa/nPVKXZoRMEsRQ0OSwiy041/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8KPpo/btrnORmoJEa/nPVKXZoRMEsRQ0OSwiy041/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8KPpo/btrnORmoJEa/nPVKXZoRMEsRQ0OSwiy041/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8KPpo%2FbtrnORmoJEa%2FnPVKXZoRMEsRQ0OSwiy041%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;307&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;664&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Driver ID: IB 선택&lt;/li&gt;
&lt;li&gt;Database: 다운로드 받은 &quot;BIOLIFE.IB&quot; 파일 선택&lt;/li&gt;
&lt;li&gt;User_Name: sysdba 입력&lt;/li&gt;
&lt;li&gt;Password: masterkey 입력&lt;/li&gt;
&lt;li&gt;[Test] 버튼 클릭해 연결 확인&lt;/li&gt;
&lt;li&gt;[OK]버튼 클릭&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Object Inspector에서 TFDConnection의 LoginPrompt 속성을 &quot;False&quot;로 변경&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;쿼리 설정
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TFDQuery 컴포넌트를 데이터 모듈에 추가&lt;/li&gt;
&lt;li&gt;추가된 TFDQuery 컴포넌트를 더블클릭해 &quot;FireDAC Query Editor&quot; 표시&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;SQL 조회 문구 입력&lt;br /&gt;SELECT * FROM BIOLIFE&lt;/li&gt;
&lt;li&gt;[Execute] 버튼 클릭 후 데이터 확인&lt;/li&gt;
&lt;li&gt;[OK] 버튼 클릭&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;step4&quot; data-ke-size=&quot;size26&quot;&gt;4. 데이터를 REST API 리소스로 제공하도록 설정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 추가한 데이터셋의 데이터를 HTTP 기반 REST API로 제공하도록 설정하고, 이미지 제공을 위한 커스텀 엔드포인트를 설정합니다.&lt;/p&gt;
&lt;h3 id=&quot;+++++++++TEMSDataSetResource++++++++&quot; data-ke-size=&quot;size23&quot;&gt;데이터 제공 용 TEMSDataSetResource 컴포넌트 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TEMSDataSetResource 컴포넌트(10.3 리오에서 추가)를 이용해 리소스를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TEMSDataSetResource 컴포넌트는&amp;nbsp;DataSet 속성에 설정된 데이터셋의 데이터를 REST API로 제공합니다.&lt;br /&gt;리소스에 HTTP 메소드(Get, Post, Put, Delete) 요청 시 데이터 제공(조회) 및 처리(등록, 수정, 삭제)를 자동화 해줍니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TEMSDataSetResource 컴포넌트를 데이터 모듈에 추가&lt;/li&gt;
&lt;li&gt;(Object Inspector에서)AllowAcctions 속성 모두 선택&lt;br /&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;251&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/69Bdl/btrnPKglnJl/HTYRzuPejKXciU9zCu0NvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/69Bdl/btrnPKglnJl/HTYRzuPejKXciU9zCu0NvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/69Bdl/btrnPKglnJl/HTYRzuPejKXciU9zCu0NvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F69Bdl%2FbtrnPKglnJl%2FHTYRzuPejKXciU9zCu0NvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;251&quot; height=&quot;172&quot; data-origin-width=&quot;251&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;List: 데이터셋 목록 제공&lt;/li&gt;
&lt;li&gt;Get: 특정 항목 제공&lt;/li&gt;
&lt;li&gt;Post: 신규 항목 생성&lt;/li&gt;
&lt;li&gt;Put: 특정 항목 수정&lt;/li&gt;
&lt;li&gt;Delete: 특정 항목 제거&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터셋 설정&lt;br /&gt;DataSet 속성에 앞에서 추가한 TFDQuery 컴포넌트 선택&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;키필드 설정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;KeyFIelds 속성 더블클릭&lt;/li&gt;
&lt;li&gt;SPECIES_NO 선택 후 [&amp;gt;] 버튼을 클릭해 &quot;Included fields:&quot;로 이동&lt;/li&gt;
&lt;li&gt;[OK] 버튼 클릭&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;API로 제공할 필드 선택
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ValueFIelds 속성 더블클릭&lt;/li&gt;
&lt;li&gt;용량이 큰 GRAPHIC(Blob Field) 제외한&amp;nbsp;모든 항목을 &quot;Included fields:&quot;로 이동&lt;br /&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;359&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bI3zJr/btrnPLzzlLD/DLpm0XjvxSipnpNviuz4v1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bI3zJr/btrnPLzzlLD/DLpm0XjvxSipnpNviuz4v1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bI3zJr/btrnPLzzlLD/DLpm0XjvxSipnpNviuz4v1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbI3zJr%2FbtrnPLzzlLD%2FDLpm0XjvxSipnpNviuz4v1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;359&quot; height=&quot;351&quot; data-origin-width=&quot;359&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;[OK] 버튼 클릭&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;리소스 이름 지정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 에디터로 변경(F12)&lt;/li&gt;
&lt;li&gt;TEMSDataSetResource 선언부 위에 리소스 이름 특성 추가
&lt;pre id=&quot;ips_uid_3751_15&quot; class=&quot;brush:delphi prolog&quot;&gt;    [ResourceName('biolifes')]
    EMSDataSetResource1: TEMSDataSetResource;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;+++++++++++++++++++&quot; data-ke-size=&quot;size23&quot;&gt;이미지 제공 커스텀 엔드포인트 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 제공 등 큰 용량의 데이터 제공은 별도의 엔드포인트로 제공해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;샘플 엔드포인트로 생성한 GetItem에 Blob 필드에 저장된 이미지를 제공하도록 구현합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그래픽 조회 용 쿼리 컴포넌트 추가
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TFDQuery 컴포넌트를 데이터 모듈에 추가(FDQuery2)&lt;/li&gt;
&lt;li&gt;추가된 TFDQuery 컴포넌트를 더블클릭해 &quot;FireDAC Query Editor&quot; 표시&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;SQL 조회 문구 입력&lt;br /&gt;SELECT GRAPHIC FROM BIOLIFE WHERE SPECIES_NO = :ITEM&lt;/li&gt;
&lt;li&gt;[OK] 버튼 추가&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;커스텀 엔드포인트 수정
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GetItem 선언부 위에 리소스 접미사 변경
&lt;pre id=&quot;ips_uid_3751_17&quot; class=&quot;brush:delphi&quot;&gt;    [ResourceSuffix('bio/lifes/{item}/photo/')]
    procedure GetItem(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;엔드포인트 호출 시 이미지 제공하도록 구현부 변경&lt;/li&gt;
&lt;/ol&gt;
&lt;/ol&gt;
&lt;pre id=&quot;ips_uid_3751_19&quot; class=&quot;brush:delphi&quot;&gt;procedure TFishfactsResource1.GetItem(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
var
  item: string;
  Stream: TMemoryStream;
begin
  item := ARequest.Params.Values['item'];
  Stream := TMemoryStream.Create;
  try
    FDQuery2.Close;
    FDQuery2.ParamByName('item').AsString := item;
    FDQuery2.Open;

    if FDQuery2.RecordCount = 0 then
      AResponse.RaiseNotFound('Not found', '''' + item + ''' is not found');

    TBlobField(FDQuery2.FieldByName('GRAPHIC')).SaveToStream(Stream);

    if Stream.Size = 0 then
      AResponse.RaiseNotFound('Not found', '''' + item + ''' is not found');

    Stream.Position := 0;
    AResponse.Body.SetStream(Stream, 'image/jpeg', True);
  except
    Stream.Free;
    raise;
  end;
end;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;step5&quot; data-ke-size=&quot;size26&quot;&gt;5. RAD 서버 실행 및 결과 확인&lt;/h2&gt;
&lt;h3 id=&quot;RAD++++++&quot; data-ke-size=&quot;size23&quot;&gt;RAD 서버 실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAD 서버는 패키지를 개발해 RAD 서버 엔진을 이용해 패키지를 로드하는 방식입니다.&lt;br /&gt;(개발한 프로젝트의 확장자는 *.bpl(Boload Package Library))&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 시점에는 RAD Development Server를 실행해 결과 확인과 테스트를 진행합니다.&lt;br /&gt;운영서버에서는 &quot;Microsoft IIS&quot; 및 &quot;Apache Server&quot; 엔진을 이용하는 것이 좋습니다.(&lt;a href=&quot;https://docwiki.embarcadero.com/RADStudio/en/Installing_the_RAD_Server_or_the_RAD_Server_Console_on_a_Production_Environment_on_Windows&quot;&gt;운영환경에서 RAD 서버 엔진 설치(영문)&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;참조)&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트 실행(F9)&lt;/li&gt;
&lt;li&gt;(최초 1회) 필요 패키지 추가&lt;br /&gt;아래와 같은 대화상자 표시된 경우 [OK] 버튼 클릭&lt;br /&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;601&quot; data-origin-height=&quot;386&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TRTAP/btrnPMeb6eb/aMU5MzsnmqKc2DvPPzD1vK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TRTAP/btrnPMeb6eb/aMU5MzsnmqKc2DvPPzD1vK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TRTAP/btrnPMeb6eb/aMU5MzsnmqKc2DvPPzD1vK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTRTAP%2FbtrnPMeb6eb%2FaMU5MzsnmqKc2DvPPzD1vK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;279&quot; height=&quot;179&quot; data-origin-width=&quot;601&quot; data-origin-height=&quot;386&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FireDAC에서 사용하는 패키지 추가&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;RAD Development Server 실행 및 로그 확인&lt;br /&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;614&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnVTIs/btrnDo6UVY2/31rv4Je6LseHKoVrI1yA80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnVTIs/btrnDo6UVY2/31rv4Je6LseHKoVrI1yA80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnVTIs/btrnDo6UVY2/31rv4Je6LseHKoVrI1yA80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnVTIs%2FbtrnDo6UVY2%2F31rv4Je6LseHKoVrI1yA80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;303&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;614&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빨간색 로그
&lt;pre id=&quot;ips_uid_3751_25&quot; class=&quot;brush:js json&quot;&gt;{&quot;Thread&quot;:1812,&quot;Loading&quot;:{&quot;Filename&quot;:&quot;C:\Users\Public\Documents\Embarcadero\Studio\22.0\Bpl\FishfactsPackage.bpl&quot;}}&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;우리가 생성한 RAD 서버 패키지가 로드 된 것을 확인&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;주황색 로그
&lt;pre id=&quot;ips_uid_3751_27&quot; class=&quot;brush:js json&quot;&gt;{&quot;Thread&quot;:1812,&quot;RegResource&quot;:{&quot;name&quot;:&quot;fishfacts&quot;,&quot;endpoints&quot;:[
  {&quot;name&quot;:&quot;GetItem&quot;,&quot;method&quot;:&quot;Get&quot;,&quot;path&quot;:&quot;fishfacts/bio/lifes/{item}/photo/&quot;},
  {&quot;name&quot;:&quot;biolifes.List&quot;,&quot;method&quot;:&quot;Get&quot;,&quot;path&quot;:&quot;fishfacts/biolifes/&quot;,&quot;produce&quot;:&quot;application/json, *;q=0.9&quot;},
  {&quot;name&quot;:&quot;biolifes.Get&quot;,&quot;method&quot;:&quot;Get&quot;,&quot;path&quot;:&quot;fishfacts/biolifes/{id}&quot;,&quot;produce&quot;:&quot;application/json, *;q=0.9&quot;},
  {&quot;name&quot;:&quot;biolifes.Put&quot;,&quot;method&quot;:&quot;Put&quot;,&quot;path&quot;:&quot;fishfacts/biolifes/{id}&quot;,&quot;consume&quot;:&quot;application/json, *;q=0.9&quot;},
  {&quot;name&quot;:&quot;biolifes.Post&quot;,&quot;method&quot;:&quot;Post&quot;,&quot;path&quot;:&quot;fishfacts/biolifes/&quot;,&quot;consume&quot;:&quot;application/json, *;q=0.9&quot;},
  {&quot;name&quot;:&quot;biolifes.Delete&quot;,&quot;method&quot;:&quot;Delete&quot;,&quot;path&quot;:&quot;fishfacts/biolifes/{id}&quot;}
]}}&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GetItem: 이미지 제공을 위한 커스텀 엔드포인트 제공한 것을 확인&lt;/li&gt;
&lt;li&gt;&lt;span&gt;biolifes.{Actions}: TEMSDataSetResource에서 제공하는 액션 제공한 것을 확인&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;++++++++++++&quot; data-ke-size=&quot;size23&quot;&gt;웹브라우저에서 결과확인&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;웹브라우저에서 List 액션 결과 확인&lt;/span&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;웹브라우저 실행&lt;/span&gt;&amp;nbsp;후&amp;nbsp;&lt;span&gt;주소에 다음 URL 입력 후 실행&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;http://localhost:8080/fishfacts/biolifes/&quot;&gt;http://localhost:8080/fishfacts/biolifes/&lt;/a&gt;&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;결과 확인&lt;/span&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crGDs3/btrnHRA5Btr/K0aApfsDrrhAFssJcUZEFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crGDs3/btrnHRA5Btr/K0aApfsDrrhAFssJcUZEFK/img.png&quot; data-alt=&quot;(FireFox에서 확인 시)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crGDs3/btrnHRA5Btr/K0aApfsDrrhAFssJcUZEFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrGDs3%2FbtrnHRA5Btr%2FK0aApfsDrrhAFssJcUZEFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;347&quot; height=&quot;256&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;(FireFox에서 확인 시)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;웹브라우저에서 이미지 제공 결과 확인&lt;/span&gt;&lt;/span&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;웹브라우저 실행 후 주소에 다음 URL 입력 후 실행&lt;br /&gt;&lt;a href=&quot;http://localhost:8080/fishfacts/biolifes/90020/photo/&quot;&gt;http://localhost:8080/fishfacts/biolifes/90020/photo/&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;결과 확인&lt;/span&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1uYKE/btrnJdpZqok/ztfWVe0JOlOszlk58oxGek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1uYKE/btrnJdpZqok/ztfWVe0JOlOszlk58oxGek/img.png&quot; data-alt=&quot;(FireFox에서 확인 시)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1uYKE/btrnJdpZqok/ztfWVe0JOlOszlk58oxGek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1uYKE%2FbtrnJdpZqok%2FztfWVe0JOlOszlk58oxGek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;476&quot; height=&quot;225&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;(FireFox에서 확인 시)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Delphi/C++Builder</category>
      <author>험프리.김현수</author>
      <guid isPermaLink="true">https://blog.hjf.pe.kr/522</guid>
      <comments>https://blog.hjf.pe.kr/522#entry522comment</comments>
      <pubDate>Tue, 14 Dec 2021 09:30:12 +0900</pubDate>
    </item>
    <item>
      <title>[번역] RAD Studio 11의 새로운 RAD 서버 라이트(RSLite)</title>
      <link>https://blog.hjf.pe.kr/521</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;마르코 칸투(Marco Cantu)의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://blogs.embarcadero.com/the-new-rad-server-lite-rslite-in-rad-studio-11/&quot;&gt;The New RAD Server Lite (RSLite) in RAD Studio 11&lt;/a&gt;을 번역했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAD 스튜디오 11 알렉산드리아 출시와 함께,&amp;nbsp;RAD 서버 배포/라이선스에 라이트(Lite) 버전이 새로 추가되었다. 이것은 REST&amp;nbsp;요청이 많지 않은, 배포/설치가 보다 간소화된 RAD 서버이다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RAD 서버는 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세부 사항에 앞서 RAD 서버가 무엇인지 살펴 보자. RAD 서버는&amp;nbsp;REST 서버 엔진이다. 개발자는 REST 엔드포인트 (주로 JSON을 제공)를 빠르게 개발할 수 있다. 개발 도구는 델파이 또는 C++빌더를 사용하며 FireDAC(또는 다른 데이터 엑세스 레이어)를 사용하여 데이터에 연결한다. 정리하면, 개발자가 애드온 패키지(BPL)를 만들고, 이것에 URL 엔드포인트를 등록하는 방식으로 원하는 서비스를 구현/제공한다. RAD 서버에는 이처럼 개발자가 직접 구현할 수 있는 능력 뿐만 아니라 일반적인 서비스들이 바로 사용할 수 있도록 미리 구현되어&amp;nbsp;있다 (자세한 내용:&amp;nbsp;&lt;a href=&quot;https://www.embarcadero.com/products/rad-server&quot;&gt;https://www.embarcadero.com/products/rad-server&lt;/a&gt;).&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RAD 서버 라이트(Lite)가 왜 추가 되었는가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAD 서버는 서버 데이터를 관리하는 데이터베이스(InterBase)가 백엔드에서 작동한다. 그리고, RAD 서버는 웹서버 DLL 모듈 형태로 IIS 또는 Apache에 배포되는 것이 일반적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 표준 배포를 하려면, 아래 사항이 필요하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 서버를 작동하고, 그 웹 서버에 &quot;RAD 서버 모듈&quot;을 구성&lt;/li&gt;
&lt;li&gt;RAD 서버를 배포하고 구성&lt;/li&gt;
&lt;li&gt;InterBase를 설치하고 RAD 서버 전용 InterBase 라이선스를 등록 (RAD서버를 배포되는 장비에 이 라이선스를 등록해야 사용할 수 있다)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발용 독립 실행 버전인, &quot;RAD 서버 개발자 에디션&quot;이 제공된 지는 이미 꽤 오래되었다. RAD 서버 개발자 에디션은&amp;nbsp;Indy HTTP 서버 기반이므로 성능에 제한이 있다. 하지만 배포가 훨씬&amp;nbsp;쉽고, 디버거 안에서도 실행될 수 있다 (덕분에 개발자는 RAD 서버 모듈의 코드를 디버깅할 수 있다). 개발자 버전이라고 해서 배포를 허용하지 않는 것은 아니다. 다만, 사용자 수에 제한이 있으며, 데이터베이스로는 (RAD 스튜디오 라이선스에 포함되어 있는) &quot;InterBase&amp;nbsp;개발자 에디션&quot;이 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAD 서버 라이트(일명 RSLite)는 배포 모델이 더 간단하며, 용도는 테스트 서버 또는 처리량이 많지 않은 시나리오에 적합하다. RSLite에서 사용되는 InterBase&amp;nbsp;데이터베이스는 모든 기능을 갖춘 서버 버전이 아니라 임베디드 엔진인&amp;nbsp;IBToGo가 사용되며, 라이선스 모델도 더 단순하다.&amp;nbsp;RSLite는&amp;nbsp;&quot;RAD 서버 개발자 에디션(RAD 스튜디오와 함께 제공됨)&quot;과 동일한 바이너리, IBToGo 바이너리, 라이선스 슬립파일을 사용하므로, 한번에 솔루션과 함께&amp;nbsp;배포할 수 있다 (즉 배포할 컴퓨터에서 따로 등록할 필요가 없다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임베디드 데이터베이스와 IndyHTTP 서버 컴포넌트를 사용하기 때문에, 일반적인 RAD 서버에 비해 초당 요청 처리 능력이 떨어지고, 프론트 엔드에 RAD 서버를 여러대 추가하는 방식으로 확장하지 못한다는 단점이 있다. 우리가 별도 작업을 통해 기능을 제약하지는 않았지만, 기반 아키텍처 상&amp;nbsp;RSLite의 확장성은 매우 제한적이다. 하지만, 단순한 시나리오에서 사용하기에는 충분할 것이다. 단, 처리 능력은 RAD 서버 모듈에서 실행되는 코드 즉 개발자가 구현한 코드에 따라 달라진다는 점을 명심해야 한다. 외부에 공개되는 시스템에 배포할 때에는&amp;nbsp;RSLite HTTP 서버를 외부로 직접 노출하지 말고, 프록시 통해서 접근하도록 구성하기를 강력히 권장한다. 이렇게 (Apache, IIS와 같은) 웹서버를 앞에 배치함으로써, 인바운드 HTTPS 호출을 RSLite로 포워딩하기 전에&amp;nbsp;보안 컨텍스트를 제공할 수 있기 때문이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;라이선스 요청/확보 하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 RSLite를 실제로 어떻게 하면 되는 지를 알아보자. 가장 먼저, 라이선스를 확보해야 한다. 개발 도구인 RAD 스튜디오 11(델파이 11 및 C++빌더 11 포함)의 엔터프라이즈 또는 아키텍트 라이선스를 가지고 아래 페이지의 안내를 따라 진행하면 RSLite 라이선스를&amp;nbsp;받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://reg.embarcadero.com/srs6/promotion.jsp?promoId=572&quot;&gt;https://reg.embarcadero.com/srs6/promotion.jsp?promoId=572&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 도구의 등록 키(시리얼번호)와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://welcome.devgear.co.kr/guide-embarcadero/install/edn-%EA%B3%84%EC%A0%95%EA%B3%BC-%EC%9D%B4%EC%9A%A9-r11/#3&quot;&gt;EDN 계정&lt;/a&gt;을 미리 준비하고 진행하자. 이 절차를 모두 마치고 나면 RSLite 라이선스 키와 해당 슬립파일 (라이선스가 기록된 .TXT)을 받게 된다. 슬립 파일은 RAD 서버를 설치할 때 함께 배포하기 위한 라이선스 파일이다. 이 라이선스는 설치 횟수 제한이 없다 (그러나 동일한 컴퓨터에 인스턴스를 2개를 실행할 수는 없다). &amp;nbsp;라이선스 파일은 특정 하위폴더에 배치해야 한다(아래에 설명을 참고할 것,&amp;nbsp;라이선스를 확보한 웹페이지에 있는 설명과는 다르니 주의 바람).&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RAD 서버 라이트 프로젝트 배포하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 라이선스를 확보했으니, RSLite를 배포하자. 다음 두가지를 알아 두어야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫번째로, RSLIte의 배포 구성, 필요한 런타임 패키지, IBToGo 배포를 만들어야 한다(아래에서 절차 설명)&lt;/li&gt;
&lt;li&gt;두번째로, 데이터베이스 파일을 만들 때에는 운영 시 사용할 IBToGo 라이선스와 호환되는 파일을 만들어야 한다. &amp;mdash; RAD 서버 개발자 에디션에서 생성한 로컬 데이터베이스는 호환되지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배포할 파일&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RSLite 솔루션을 배포하기 위해 필요한 파일 목록 (실제로는, 당신이 만든&amp;nbsp;애플리케이션 패키지와 해당 의존 파일들을 아래 파일들과 함께 배포하게 된다)&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RSLite 실행 파일 (RAD 서버 개발자 에디션과 동일한 파일):&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;EMSDevServer.exe&lt;/b&gt;는 RAD 스튜디오 bin 폴더에 들어 있다(또는 비슷한 64-비트 버전)&lt;/li&gt;
&lt;li&gt;필수 RAD 스튜디오 런타임 패키지(최소 설치에 필요한 패키지로써 RAD Studio win32 또는 win64 redist 폴더에 들어 있다),&lt;br /&gt;그리고 당신이 작성한 RAD 서버 모듈 코드에서 참조하는 런타임 패키지:&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;bindengine280.bpl&lt;/li&gt;
&lt;li&gt;dbrtl280.bpl&lt;/li&gt;
&lt;li&gt;emsclientfiredac280.bpl&lt;/li&gt;
&lt;li&gt;emsserverapi280.bpl&lt;/li&gt;
&lt;li&gt;FireDAC280.bpl&lt;/li&gt;
&lt;li&gt;FireDACCommon280.bpl&lt;/li&gt;
&lt;li&gt;FireDACCommonDriver280.bpl&lt;/li&gt;
&lt;li&gt;FireDACIBDriver280.bpl&lt;/li&gt;
&lt;li&gt;rtl280.bpl&lt;/li&gt;
&lt;li&gt;vcl280.bpl&lt;/li&gt;
&lt;li&gt;vcldb280.bpl&lt;/li&gt;
&lt;li&gt;vclFireDAC280.bpl&lt;/li&gt;
&lt;li&gt;vclimg280.bpl&lt;/li&gt;
&lt;li&gt;vclwinx280.bpl&lt;/li&gt;
&lt;li&gt;vclx280.bpl&lt;/li&gt;
&lt;li&gt;Xmlrtl280.bpl&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;InterBase ToGo 배포 파일들, 공용문서의 InterBase redist 폴더(예, C:/Users/Public/Documents/Embarcadero/Interbase/redist/InterBase2020) 아래의 win32_togo 또는 win64_togo 폴더 안에 있다 &amp;mdash; 리눅스 배포용 파일은 알맞은 InterBase redist 폴더에 있는 libibtogo.so 파일이다.&lt;/li&gt;
&lt;li&gt;앞에서 확보한 라이선스 파일을 interbase/license 폴더 안에 넣는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;MSVC 런타임&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 IBToGo (RSLite는 IBToGo를 사용한다)가 작동하려면 배포할 윈도우 시스템에&amp;nbsp;Visual C++ 2013 런타임 라이브러리가 설치되어 있어야 한다. RAD 스튜디오가 설치된 개발자 컴퓨터라면 이미 설치되어 있을 가능성이 크다. 그러나 일반적인 시스템이라면 아마도&amp;nbsp;&lt;a href=&quot;https://docs.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-160#visual-studio-2013-vc-120&quot;&gt;Microsoft에서 다운로드&lt;/a&gt;하여&amp;nbsp;설치해야 할 것이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;운영 환경에서 사용할 데이터베이스 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 명시된 서버 구성을 모두 마치고 나면,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;EMSDevServer.exe&lt;/b&gt;&amp;nbsp;애플리케이션을 실행하여 RSLite를 시작하면 된다. 이때 대상 컴퓨터에 InterBase 클라이언트가 설치되어 있으면 이것이 IBLite보다 더 우선하여 선택된다. 그리고 그 InterBase 클라이언트가 RAD 스튜디오와 함께 제공된 &quot;InterBase 개발자 버전&quot;인 경우라면, 모든 동작이 잘 되지만 RSLite가 아니라&amp;nbsp;표준 RAD 서버의 개발자 에디션으로 작동하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이점은 쉽게 파악할 수 있다. RAD 서버가 시작되면 로그에 &amp;ldquo;RSLite&amp;rdquo; 구성인지 아닌 지를 기록하기 때문이다. 처음 몇줄은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;{&amp;ldquo;Thread&amp;rdquo;:19124,&amp;rdquo;ConfigLoaded&amp;rdquo;:{&amp;ldquo;Filename&amp;rdquo;:&amp;rdquo;[folder]emsserver.ini&amp;rdquo;,&amp;rdquo;Exists&amp;rdquo;:true}}&lt;br /&gt;{&amp;ldquo;Thread&amp;rdquo;:19124,&amp;rdquo;Licensing&amp;rdquo;:{&lt;b&gt;&amp;ldquo;Lite&amp;rdquo;:true&lt;/b&gt;,&amp;rdquo;Licensed&amp;rdquo;:true,&amp;rdquo;LicensedMaxUsers&amp;rdquo;:2}}&lt;br /&gt;{&amp;ldquo;Thread&amp;rdquo;:19124,&amp;rdquo;DBConnection&amp;rdquo;:{&amp;ldquo;InstanceName&amp;rdquo;:&amp;rdquo;&amp;rdquo;,&amp;rdquo;Filename&amp;rdquo;:&amp;rdquo;[folder]emsserver.ib&amp;rdquo;}}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 로그에 &amp;ldquo;Lite&amp;rdquo;가 false라고 기록되어 있다면, InteBase 클라이언트 라이브러리인 gds32.dll이 로딩되지 않도록 하는 조치를 직접해야 한다. 이 파일은 일반적으로 C:\Windows\SysWOW64 폴더에 들어 있다. (RSLite는 표준 RAD 서버와 마찬가지로 기동하면서, 이 폴더에서 InteBase 클라이언트 라이브러리를 찾으려고 한다. 따라서 이 폴더에&amp;nbsp;gds32.dll 파일을 없어야만, RSLite 안에서 ibtogo.dll를 찾아서 로딩한다. 이와 관련된 보강은 다음 릴리즈에 반영할 예정이다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구성도 잘 마쳤고 RSLite도 잘 기동했는데, &amp;nbsp;&lt;b&gt;emsserver.ini&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;파일과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;emsserver.ib&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;데이터베이스 파일을 새로 만들라는 메시지가 표시되고, &quot;RAD Server Setup Wizard&quot;가 실행되어 이 파일들을 만들 수 있다. 하지만&amp;nbsp;이&amp;nbsp;마법사를&amp;nbsp;실행하려면&amp;nbsp;RAD&amp;nbsp;스튜디오&amp;nbsp;Object&amp;nbsp;Repository&amp;nbsp;폴더의&amp;nbsp;설정&amp;nbsp;파일들이&amp;nbsp;필요하다.&amp;nbsp;개발자&amp;nbsp;PC의&amp;nbsp;(RAD&amp;nbsp;스튜디오&amp;nbsp;설치&amp;nbsp;경로)/ObjRepos/en/EMS&amp;nbsp;폴더의&amp;nbsp;파일들을&amp;nbsp;배포&amp;nbsp;환경의&amp;nbsp;EMSDevServer.exe&amp;nbsp;기준&amp;nbsp;../ObjRepos/en/EMS&amp;nbsp;폴더로&amp;nbsp;복사해야&amp;nbsp;한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;633&quot; data-origin-height=&quot;227&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccAvc2/btrnPj4mA32/XiYDbMJe1q8Dgo6HYK34z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccAvc2/btrnPj4mA32/XiYDbMJe1q8Dgo6HYK34z1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccAvc2/btrnPj4mA32/XiYDbMJe1q8Dgo6HYK34z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccAvc2%2FbtrnPj4mA32%2FXiYDbMJe1q8Dgo6HYK34z1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;633&quot; height=&quot;227&quot; data-origin-width=&quot;633&quot; data-origin-height=&quot;227&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(역자 주: 만약,&amp;nbsp;D:\RSLite\bin\EMSDevServer.exe가 설치되어 있다면&lt;br /&gt;개발자 PC의 RAD 스튜디오 Object Repository 폴더 하위의 en/EMS 폴더의 파일들을&amp;nbsp;D:\RSLite\ObjRepos\en\EMS 폴더에 복사해야 합니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: 위 작업은 매번 RSLite 배포마다 진행하지 않아도 된다. 최초 1회 프로덕션 데이터베이스 생성을 위해 위 작업을 진행하고, 다음 배포시에는 생성한 프로덕션 데이터베이스를 복사해 사용할 수 있다. 개발환경에서&amp;nbsp;InterBase 개발자 에디션 기반으로 생성한 데이터베이스는 RSLite와 호환되지 않기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마법사에서 emsserver.ini 파일과 emsserver.ib 데이터베이스 파일 생성 시 RSLite와 동일한 폴더를 지정하는 것을 권장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 RSLite, 설정파일, 런타임 패키지 및 라이선스, IBToGo를 모두 백업 받는다. 이제 윈도우 컴퓨터에 배포할 모든 파일들이 준비되었다.(역자 주: 다음 배포 시에는 백업 받은 파일들을 그대로 배포하면 됩니다.)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프록시 구성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 언급했듯 RSLite는 보호 및 암호화 측면에서 제한이 있기 때문에 공개 웹 애플리케이션으로 직접 노출하지 않는 것이 좋다. RSLite 앞단에 프록시 레이어 전용 서비스를 사용하거나, 인기 있는 웹 서비스를 배치하는 것을 권장한다. 예를 들어 Apache에서 가상 호스트 구성 시 HTTPS를 활성화하고, 다음과 같이 구성해 트래픽을 RSLite로 리다이렉션한다.&lt;/p&gt;
&lt;pre id=&quot;ips_uid_4240_8&quot; class=&quot;groovy&quot;&gt;&lt;code&gt;ProxyPass / http://localhost:8088
ProxyPassReverse / http://localhost:8088
ProxyPreserveHost On&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리눅스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스의 경우 위와 비슷한 절차를 진행할 수 있으며, 대부분 예상대로 작동할 것이다. 또한 전체 RAD 서버를 설치하고 나서, IBToGo를 추가도 가능하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RAD 서버 폴더에 있는 ems_install.sh를 사용해 RAD 서버 설치 (&lt;a href=&quot;https://docwiki.embarcadero.com/RADStudio/en/Configuring_Your_RAD_Server_Engine_or_RAD_Server_Console_on_Linux&quot;&gt;https://docwiki.embarcadero.com/RADStudio/en/Configuring_Your_RAD_Server_Engine_or_RAD_Server_Console_on_Linux&lt;/a&gt;)(역자 주: 이 도움말의 내용 중 개발 도구가 설치된 폴더 위치는 버전 별로 다를 수 있다. 예를 들어 19.0 이라는 폴더가 없으면, 22.0 등 내가 설치한 버전에 해당하는 폴더라고 이애하면 된다)&lt;/li&gt;
&lt;li&gt;InterBase &quot;redist&quot; 폴더(예. C:\Users\Public\Documents\Embarcadero\InterBase\redist\InterBase2020\linux64_togo)의 IBToGo 파일들을 Linux의 EMS 폴더(/usr/lib/ems)로 복사&lt;/li&gt;
&lt;li&gt;EMSDevServerCommand를 실행하고 마법사를 따라 EMS 데이터베이스 및 구성 파일 생성 (애플리케이션을 실행할 권한이 없다면, 슈퍼 유저로서 실행하기 위해&amp;nbsp;명령어 앞에 'sudo'를 붙여야 할 수도 있음)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 기능이 포함된 RAD 서버로 업그레이드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 RSLite는 감당할 수 있는 사용자 요청 트래픽에 한계가 있다는 점을 상기한다. RSLite 보다&amp;nbsp;더 넓은 대역 폭과 처리량이 필요하다면, 일반 RAD 서버를 배포하는 것이 좋다. RAD 스튜디오, 델파이 또는 C++빌더의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://welcome.devgear.co.kr/guide-embarcadero/install/%EC%8B%A0%EA%B7%9C-%EA%B3%A0%EA%B0%9D-%E2%80%9C%ED%95%84%EB%8F%85%E2%80%9D-%EC%82%AC%ED%95%AD-r15/#esd_enterprise&quot;&gt;엔터프라이즈 에디션을 구입하고 받는 ESD 라이선스 이메일&lt;/a&gt;에는 서버 한 대에 배포할 수 있는 일반 RAD 서버 &amp;nbsp;라이선스가 들어있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://welcome.devgear.co.kr/guide-embarcadero/install/%EC%8B%A0%EA%B7%9C-%EA%B3%A0%EA%B0%9D-%E2%80%9C%ED%95%84%EB%8F%85%E2%80%9D-%EC%82%AC%ED%95%AD-r15/#esd_architect&quot;&gt;아키텍트 에디션의 ESD 라이선스 이메일&lt;/a&gt;에는 RAD를 서버 여러대에 배포할 수 있는 라이선스가 들어있다. 배포한 RAD 서버가 작동하려면 장비 별로 라이선스를 활성화해야 한다. 당사 영업팀에 문의하여 VAR(Value Added Reseller) 계약을 체결하면 라이선스 슬립파일을 사용하여 별도의 등록 과정 없이도&amp;nbsp;모든 기능이 있는 일반 RAD 서버를 쉽게 배포할 수도 있다.&lt;/p&gt;</description>
      <category>RAD Studio 정보</category>
      <author>험프리.김현수</author>
      <guid isPermaLink="true">https://blog.hjf.pe.kr/521</guid>
      <comments>https://blog.hjf.pe.kr/521#entry521comment</comments>
      <pubDate>Tue, 14 Dec 2021 09:18:02 +0900</pubDate>
    </item>
    <item>
      <title>[RX.11] REST 클라이언트 Post 전송 시 Get으로 파라미터 전송 이슈 해결방안</title>
      <link>https://blog.hjf.pe.kr/520</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;데브기어 포럼에 등록된 이슈 공유합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://welcome.devgear.co.kr/topic/227-delphi-11-rest-client-동작-오류-문의드립니다/#comment-401&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://welcome.devgear.co.kr/topic/227-delphi-11-rest-client-동작-오류-문의드립니다/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;질문 요약&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;11.0에서 REST 클라이언트로 post로 요청시, 서버단에서 post로 못받고 get으로 인지를 하는 문제입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개요&amp;nbsp;:&amp;nbsp;10.4.2&amp;nbsp;에서&amp;nbsp;잘&amp;nbsp;되던&amp;nbsp;앱이&amp;nbsp;11&amp;nbsp;버전&amp;nbsp;RestClient&amp;nbsp;POST&amp;nbsp;방식에서&amp;nbsp;문제가&amp;nbsp;되어&amp;nbsp;여러가지&amp;nbsp;테스트&amp;nbsp;해본&amp;nbsp;결과&amp;nbsp;POST로&amp;nbsp;파라미터를&amp;nbsp;요청을&amp;nbsp;하면&amp;nbsp;서버쪽에서&amp;nbsp;POST로&amp;nbsp;파라미터&amp;nbsp;값을&amp;nbsp;못&amp;nbsp;받는&amp;nbsp;현상입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;답변&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;질문하신 내용을 요약하면 &quot;델파이 11에서 POST로 요청 시 GET으로 메소드를 호출되는 이슈가 있다.로 이해됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;질문의 답변에 앞서,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요구사항을&amp;nbsp;확인하면&amp;nbsp;&quot;이미지&amp;nbsp;데이터를&amp;nbsp;서버로&amp;nbsp;전달&quot;하는&amp;nbsp;것으로&amp;nbsp;보여&amp;nbsp;다음&amp;nbsp;2가지&amp;nbsp;답변을&amp;nbsp;드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) POST 요청 시 GET으로 호출하는 원인 확인 및 조치사항(질문에 대한 답변)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) REST 클라이언트로 이미지를 업로드하는 방안(요구사항 솔루션)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) POST 요청 시 GET으로 호출하는 원인 확인 및 조치사항&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 현상과 원인 파악을 위해 PHP 페이지를 다음과 같이 작성 후 다시 시도해보시기 바랍니다.(정확히 어떤 메소드로 호출하는지 확인할 수 있습니다.)&lt;/p&gt;
&lt;pre class=&quot;brush:php xml&quot;&gt;&lt;code&gt;&amp;lt;?php
$GetId = $_GET['id'];
$PostId = $_POST['id'];

echo $_SERVER[&quot;REQUEST_METHOD&quot;].&quot;&amp;lt;br /&amp;gt;\n&quot;; // HTTP 메소드(GET / POST)

echo &quot;Get : &quot;.$GetId.&quot;&amp;lt;br /&amp;gt;\n&quot;;
echo &quot;Post : &quot;.$PostId.&quot;&amp;lt;br /&amp;gt;\n&quot;;

echo &quot;body: &quot;.$HTTP_RAW_POST_DATA.&quot;&amp;lt;br /&amp;gt;\n&quot;;
echo &quot;body: &quot;.file_get_contents(&quot;php://input&quot;).&quot;&amp;lt;br /&amp;gt;\n&quot;;
?&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가&lt;span&gt;&amp;nbsp;&lt;/span&gt;테스트한 결과는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;588&quot; data-origin-height=&quot;632&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bs7eyv/btrnR2ABT5R/24cFspVWyxj79b9zKm8Ax0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bs7eyv/btrnR2ABT5R/24cFspVWyxj79b9zKm8Ax0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bs7eyv/btrnR2ABT5R/24cFspVWyxj79b9zKm8Ax0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbs7eyv%2FbtrnR2ABT5R%2F24cFspVWyxj79b9zKm8Ax0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;331&quot; height=&quot;356&quot; data-origin-width=&quot;588&quot; data-origin-height=&quot;632&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두가지 버전 모두 POST로 요청함을 확인했습니다.&lt;br /&gt;(응답 Body의 첫번째 항목은 HTTP 메소드 종류이며, 둘다 POST로 출력되었습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 전송한 요청 파라미터(GETorPOST 파라미터)는, 10.4.2에서는 POST($_POST) 데이터로 11.0에서는 GET($_GET)으로 전달됩니다.&lt;br /&gt;결과적으로, 두 버전간 전달하는 방식에 차이가 있는 것을 확인했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;두 버전이 다른 결과가 나온 내용을 소스코드에서 분석한 내용을 설명합니다.(간단히 설명하니 참고만 하고, 아래의 결론의 내용을 적용하시기 바랍니다.)&lt;/b&gt;&lt;br /&gt;11.0에서 REST 클라이언트의 주요 변경사항 중&lt;span&gt;&amp;nbsp;&lt;/span&gt;ContentType을 문자열로 처리하도록 개선되었습니다.(상당히 많은 양의 코드가 변경되었습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히, 파라메터 전송 방식을 판단하는 IsQueryParam은 아래와 같이 변경되었습니다.(좌: 10.4.2, 우: 11.0)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfDD6X/btrnR2tRWty/hdUrTIZI9lKG3kFRsGkwBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfDD6X/btrnR2tRWty/hdUrTIZI9lKG3kFRsGkwBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfDD6X/btrnR2tRWty/hdUrTIZI9lKG3kFRsGkwBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfDD6X%2FbtrnR2tRWty%2FhdUrTIZI9lKG3kFRsGkwBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;140&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;140&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빨간&lt;span&gt;&amp;nbsp;&lt;/span&gt;박스를 보면 파라메터의 컨텐트타입(APram.ContentType)이 ctNone(공백)인 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;쿼리 파라메터로 인식되어 요청시 Get 형식의 파라메터가 전송&lt;/b&gt;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 로직을 피하기 위해서는 다음과 같은 조치를 취해야 합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TRESTRequest.Params 속성 선택&lt;/li&gt;
&lt;li&gt;POST 파라메터로 전달하려는 파라메터의 ContentTypeStr을 &quot;multipart/form-data&quot; 지정&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;159&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/79BhB/btrnQj3TMHS/Vkk5K8PvTKMKPeZOg57Z5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/79BhB/btrnQj3TMHS/Vkk5K8PvTKMKPeZOg57Z5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/79BhB/btrnQj3TMHS/Vkk5K8PvTKMKPeZOg57Z5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F79BhB%2FbtrnQj3TMHS%2FVkk5K8PvTKMKPeZOg57Z5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;643&quot; height=&quot;159&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;159&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 지정 후 REQUEST 실행하면 다음과 같이 10.4.2와 동일한 방식으로 POST 파라메터로 전송되는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;265&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QNWR1/btrnLHLuUMv/yCuO5pouUgNepvgun6ZwoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QNWR1/btrnLHLuUMv/yCuO5pouUgNepvgun6ZwoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QNWR1/btrnLHLuUMv/yCuO5pouUgNepvgun6ZwoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQNWR1%2FbtrnLHLuUMv%2FyCuO5pouUgNepvgun6ZwoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;363&quot; height=&quot;265&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;265&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 조치는 REST Debugger에서는 진행할 수 없으니, 소스코드 상에서 조치해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결론:&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #c0392b;&quot;&gt;델파이 11.0에서 POST 요청 시 파라메터를 POST 파라미터로 전달하려면, 파라메터의 ContentTypeStr을 &quot;multipart/form-data&quot;로 지정해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) REST 클라이언트로 이미지를 업로드하는 방안&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지를 Base64로 인코딩해 문자열로 전송하셨습니다.&amp;nbsp; 멀티파트 폼데이터(multi-part/formdata)로 전송하는 방식도 검토해 보시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티파트 폼데이터로 전송 시 Stream을 그대로 파라미터로 설정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 클라이언트에서 멀티파트-폼데이터로 전송하는 샘플 코드입니다.&lt;br /&gt;(파라미터에 파일(pkFILE)이 포함된 경우 multi-part/formdata로 전송됩니다.)&lt;/p&gt;
&lt;pre id=&quot;code_1639440645032&quot; class=&quot;brush:delphi oxygene&quot;&gt;&lt;code&gt;var
  Stream: TMemoryStream;
  Item: TRESTRequestParameter;
begin
  if not OpenDialog1.Execute then
    Exit;

  Stream :=  TMemoryStream.Create;
  Stream.LoadFromFile(OpenDialog1.FileName);

// 파라메터는 디자인타임에 생성되었습니다.
//  Item := RESTRequest1.Params.AddItem;
//  Item.Name := 'img';
//  Item.Kind := pkFILE;
  RESTRequest1.Params.ParameterByName('img').SetStream(Stream);

  RESTRequest1.Method := rmPOST;
  RESTRequest1.Execute;
end;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티파트 폼데이터로 전송 시 웹서버에서 파일 전송과 동일한 방식으로 받아 처리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAD 서버 11.0에서는 다음 코드를 이용해 멀티파트 폼데이터를 처리할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1639440661647&quot; class=&quot;brush:delphi&quot;&gt;&lt;code&gt;procedure TImgResource1.Post(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
var
  Stream: TStream;
begin
  Stream := ARequest.Body.GetPart('img', '').GetStream;

  if Stream.Size &amp;lt;= 0 then
    AResponse.RaiseBadRequest('no stream');

  TMemoryStream(Stream).SaveToFile('D:\Temp\test.jpg');
end;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #353c41;&quot;&gt;위 샘플코드 프로젝트입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/c0JI4U/btrnJA6XXMC/7Ti7VgtMX8skTKHfLUFK8K/RSX_FileUpload.zip?attach=1&amp;amp;knm=tfile.zip&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;RSX_FileUpload.zip&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.01MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결론:&amp;nbsp;&lt;/b&gt;&lt;br /&gt;이미지 등의 파일 업로드 구현 시 멀티파티 폼데이터로 전송할 수도 있습니다.&lt;br /&gt;(델파이의 일반적인 데이터구조인 Stream을 바로 전송할 수 있어 별도 인코딩 없이 간단히 구현할 수 있습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두가지 방식을 안내드렸습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 제안하는 방식은,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업로드 방식을 변경할 수 있다면 2번항목을 참고해 업로드 방식을 변경하시길 권장드리며,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버의 인터페이스를 변경하지 못하는 경우 1번항목을 참고해 적용하시기 바랍니다.&lt;/p&gt;</description>
      <category>Delphi/C++Builder</category>
      <author>험프리.김현수</author>
      <guid isPermaLink="true">https://blog.hjf.pe.kr/520</guid>
      <comments>https://blog.hjf.pe.kr/520#entry520comment</comments>
      <pubDate>Tue, 14 Dec 2021 09:12:53 +0900</pubDate>
    </item>
    <item>
      <title>[REST API] REST 기반 파일 업로드와 다운로드 구현하기</title>
      <link>https://blog.hjf.pe.kr/518</link>
      <description>&lt;p&gt;이 글에서는 REST API 기반 파일 업로드와 다운로드 구현방안을 설명합니다.&lt;/p&gt;&lt;p&gt;REST 서버와 REST 클라이언트를 이용해 기능을 구현했습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;/p&gt;&lt;h1&gt;REST 기반 파일 업로드와 다운로드 구현&lt;/h1&gt;&lt;p&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 292px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99BD33485F4C573129&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99BD33485F4C573129&quot; width=&quot;292&quot; height=&quot;261&quot; filename=&quot;RESTAPI_Updown.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;REST API 구현 시 파일을 제공해야하는 경우가 있습니다. 파일 업로드 시 기존의 데이터와 함께 파일을 업로드할 수도 있고, 별도의 파일 전용 엔드포인트를 추가해 구현할 수 있습니다. 이 두가지&amp;nbsp;방법 모두에 대해 설명합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 글에 앞서 다음 내용을 이해하고 있어야 합니다. 미리 선행 학습이 필요합니다.&lt;/p&gt;&lt;ul style=&quot;margin: 0px; padding: 0px 0px 0px 25px; border: 0px; line-height: 26px; font-family: Verdana, &amp;quot;Apple SD Gothic Neo&amp;quot;, &amp;quot;Malgun Gothic&amp;quot;, dotum;&quot;&gt;&lt;li style=&quot;margin: 0px 0px 2px; padding: 0px; border: 0px; line-height: 2; list-style-position: inside;&quot;&gt;&lt;a href=&quot;http://blog.hjf.pe.kr/462&quot; target=&quot;_blank&quot; style=&quot;margin: 0px; padding: 0px; color: rgb(71, 153, 0);&quot;&gt;[REST API] REST API 이해하기&lt;/a&gt;&lt;/li&gt;&lt;li style=&quot;margin: 0px 0px 2px; padding: 0px; border: 0px; line-height: 2; list-style-position: inside;&quot;&gt;&lt;a href=&quot;http://blog.hjf.pe.kr/463&quot; target=&quot;_blank&quot; style=&quot;margin: 0px; padding: 0px; color: rgb(71, 153, 0);&quot;&gt;[REST API][실습] REST API 서버 개발하기(엔드포인트 구현, RAD 서버 이용)&lt;/a&gt;&lt;/li&gt;&lt;li style=&quot;margin: 0px 0px 2px; padding: 0px; border: 0px; line-height: 2; list-style-position: inside;&quot;&gt;&lt;a href=&quot;http://blog.hjf.pe.kr/464&quot; target=&quot;_blank&quot; style=&quot;margin: 0px; padding: 0px; color: rgb(71, 153, 0);&quot;&gt;[REST API][실습] REST API 클라이언트 개발하기(REST Client 이용)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p style=&quot;margin-right: 0px; padding-right: 0px; padding-left: 0px; padding-top: 0px !important; padding-bottom: 0px !important;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;이 글에서는 다음 내용을 다룹니다.&lt;br /&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;파일 엔드포인트 추가 구성&lt;/li&gt;&lt;li&gt;파일 업로드 구현&amp;nbsp;방안&lt;/li&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;서버 측 구현&lt;/li&gt;&lt;li&gt;클라이언트 측 구현&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;파일 다운로드 구현 방안&lt;/li&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;서버 측 구현&lt;/li&gt;&lt;li&gt;클라이언트 측 구현&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h1&gt;파일 엔드포인트 추가 구성&lt;/h1&gt;&lt;p&gt;파일을 제공하는 기능을 추가하기 위해서는, 1) 기존 엔드포인트에서 파일 항목을 추가하는 방법과 2) 별도의 엔드포인트를 추가하는 방법으로 구현할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 글에서는 별도의 엔드포인트를 추가해 파일 업로드와 다운로드 기능을 구현하는 방법을 설명합니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;저는 images라는 리소스이름으로 RAD 서버 패키지 프로젝트를 생성했습니다.&lt;/p&gt;&lt;p&gt;1, File &amp;gt; New &amp;gt; Other&lt;/p&gt;&lt;p&gt;2, RAD Server &amp;gt; RAD Server Package&lt;/p&gt;&lt;p&gt;3, Create package with resource &amp;gt; Next&lt;/p&gt;&lt;p&gt;4, Resource name: images, File type: Data Module &amp;gt; Next&lt;/p&gt;&lt;p&gt;5, 모든 항목 선택 해제 &amp;gt; Finish&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;다음과 같이 엔드포인트를 추가합니다.(선언부에 추가 후 Ctrl + Shift + C를 눌러 구현부를 자동 생성할 수 있습니다.)&lt;/p&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;type
  [ResourceName('images')]
  TImagesResource1 = class(TDataModule)
  published
    [ResourceSuffix('{item}/photo')]
    procedure GetItemPhoto(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
    [ResourceSuffix('{item}/photo')]
    procedure PostItemPhoto(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
  end;&lt;/pre&gt;
&lt;p&gt;엔드포인트는 ResourceSuffix(리소스 접미사)와 메소드로 구성됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;메소드는 Get, Post, Put, Delete 접두사로 시작해야 합니다. 리소스 호출 시 HTTP 메소드와 접두사가 매핑되어 메소드가 호출 됩니다.&lt;/p&gt;&lt;p&gt;즉, GET으로 요청된 경우 Get*으로 시작된 메소드가, POST로 요청한 경우 Post*로 시작된 메소드가 실행됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;ResourceSuffix에&amp;nbsp;지정된 항목과 매핑된 요청이 온 경우 메소드가 실행됩니다.&lt;/p&gt;&lt;p&gt;중괄호({})로 감싼 부분은 파라미터화된 항목으로 구현부에서&amp;nbsp;ARequest.Params.Values['item'] 등의 코드로 그부분의 내용을 취득할 수 있습니다.&lt;/p&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;예를 들어&lt;/p&gt;&lt;p&gt;GET http://localhost:8080/images/1/photo 호출 시 GetItemPhoto 메소드가 호출되고&lt;/p&gt;&lt;p&gt;POST http://localhost:8080/images/1/photo 호출 시 PostItemPhoto 메소드가 호출됩니다.&lt;/p&gt;&lt;p&gt;ARequest.Params.Values['item']은 1을 반환합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h1&gt;파일 업로드 구현 방안&lt;/h1&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;서버(RAS Server) 측 구현&lt;/h2&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;파일 업로드는 PostItemPhoto 메소드에서 구현합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;주의할 점은, 파일 전송을 위해서는 multipart/form-data 인코딩 타입으로 데이터가 전달됩니다.&lt;/p&gt;&lt;p&gt;현재(2020년 08월)에 RAD 서버에서 multipart/form-data 타입의 데이터를 처리하는 기능이 구현되어있지 않은 것으로 파악되어, 직접 데이터를 분석해 필요한 데이터를 사용해야 합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;저는 다음과 같은 DecodeParamsAndStream 함수를 이용해 데이터를 분석했습니다.&lt;/p&gt;&lt;p&gt;(Indy에서 재공하는 TIdMessageDecoderMIME 객체를 이용했습니다.)&lt;/p&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;type
  TStreamParams = class(TDictionary&lt;string, tstream=&quot;&quot;&gt;)
  private
    function GetStream(const Name: string): TStream;
    procedure SetStream(const Name: string; const Value: TStream);
  public
    property Streams[const Name: string]: TStream read GetStream write SetStream;
    destructor Destroy; override;
  end;

procedure DecodeParamsAndStream(AStream: TStream; AContentType: string;
  Params: TStrings; StreamParams: TStreamParams);&lt;/string,&gt;&lt;/pre&gt;
&lt;p&gt;파라메터로는&amp;nbsp;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;AStream : Body의 전체 스트림&lt;/li&gt;&lt;li&gt;AContentType : 요청의 컨텐트 타입, boundary 포함&lt;/li&gt;&lt;li&gt;Params : 문자열 형식의 데이터(파라미터)&lt;/li&gt;&lt;li&gt;StreamParams : Stream 형식의 데이터(파라미터), TStreamParams 객체는 &amp;lt;string, TStream&amp;gt; 쌍의 딕셔너리 사용
&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;컨텐트 타입은 다음과 같이 데이터의 인코딩 타입과 boundary값이 포함됩니다.&lt;/div&gt;&lt;div&gt;multipart/form-data; boundary=---------Embt-Boundary--493D921E3683D69B&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;REST Client에서 파일 업로드 요청한 데이터(Body의 스트림)의 내용은 아래 그림과 같습니다.&lt;/div&gt;&lt;div&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 588px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99B8A04B5F4C8E760E&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99B8A04B5F4C8E760E&quot; width=&quot;588&quot; height=&quot;237&quot; filename=&quot;rawdata.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;파라미터 데이터들은 컨텐트타입의 boundary 값을 앞뒤에 두어 구분합니다.&lt;/p&gt;&lt;p&gt;주의할 점은 문자열 형식의 파라미터 데이터의 경우 Content-Type이 누락되어 있습니다.(분석 시 누락된 경우에 대해 예외처리가 필요합니다.)&lt;/p&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;위 데이터를 분석한 내용은 다음과 같습니다.&lt;/div&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;uses
  System.IOUtils,
  IdGlobalProtocols, IdMessageCoder, IdMessageCoderMIME;

procedure DecodeParamsAndStream(AStream: TStream; AContentType: string;
  Params: TStrings; StreamParams: TStreamParams);
var
  Boundary: string;
  Decoder, NewDecoder: TIdMessageDecoderMIME;
  MsgEnd: Boolean;

  FieldName: string;
  StringStream: TStringStream;
  MemoryStream: TMemoryStream;
begin
  Boundary := ExtractHeaderSubItem(AContentType, 'boundary', QuoteHTTP);

  Decoder := TIdMessageDecoderMIME.Create(nil);
  try
    MsgEnd := False;
    repeat
      Decoder.MIMEBoundary := Boundary;
      Decoder.SourceStream := AStream;
      Decoder.FreeSourceStream := False;

      Decoder.ReadHeader;
      { Content-Type이 없는 경우 mcptAttachment로 인식해 기본값 설정
        RESTClient에서 MultiPart 전송 시 일반 파라메터의 경우 Content-Type 누락해 전송 함}
      if Decoder.Headers.Values['Content-Type'] = '' then
      begin
        Decoder.Headers.Values['Content-Type'] := 'text/plain';
        Decoder.CheckAndSetType(Decoder.Headers.Values['Content-Type'], Decoder.Headers.Values['Content-Disposition']);
      end;
      case Decoder.PartType of
        mcptText:
          begin
            FieldName := ExtractHeaderSubItem(Decoder.Headers.Values['Content-Disposition'], 'name', QuoteMIME);
            StringStream := TStringStream.Create;
            try
              NewDecoder := Decoder.ReadBody(StringStream, MsgEnd) as TIdMessageDecoderMIME;
              try
                Params.Values[FieldName] := StringStream.DataString.Trim;
              finally
                Decoder.Free;
                Decoder := NewDecoder;
              end;
            finally
              StringStream.Free;
            end;
          end;
        mcptAttachment:
          begin
            var HL: string := Decoder.Headers.Values['Content-Disposition'];
            FieldName := ExtractHeaderSubItem(Decoder.Headers.Values['Content-Disposition'], 'name', QuoteMIME);
            MemoryStream := TMemoryStream.Create;
            NewDecoder := Decoder.ReadBody(MemoryStream, MsgEnd) as TIdMessageDecoderMIME;
            try
              StreamParams.Streams[FieldName] := MemoryStream;
            finally
              Decoder.Free;
              Decoder := NewDecoder;
            end;
          end;
        mcptIgnore:
          begin
            FreeAndNil(Decoder);
            Decoder := TIdMessageDecoderMIME.Create(nil);
            TIdMessageDecoderMIME(Decoder).MIMEBoundary := Boundary;
          end;
        mcptEOF:
          begin
            FreeAndNil(Decoder);
            MsgEnd := True;
          end;
      end;

    until (Decoder = nil) or MsgEnd;
  finally
    Decoder.Free;
  end;
end;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;위의 코드가 좀 길고 생소할 수 있습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;중요한 부분은 디코더로 분석한 데이터의&amp;nbsp;Decoder.PartType이&amp;nbsp;mcptText(텍스트)인 경우 Params 항목에 데이터를 추가하고,&amp;nbsp;mcptAttachment(첨부 파일)인 경우 StreamParams 항목에 데이터를 추가했습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;위 함수를 사용한 PostItemPhoto 메소드의 코드는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;const
  ROOT_PATH = 'D:\Projects\DelphiDemos\OpenAPI\RESTUpload\Server';

procedure TImagesResource1.PostItemPhoto(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
var
  LItem: string;

  Stream: TStream;
  ContentType: string;

  Params: TStringList;
  StreamParams: TStreamParams;
  Path, RawPath: string;
begin
  LItem := ARequest.Params.Values['item'];

  Params := TStringList.Create;
  StreamParams := TStreamParams.Create;

  Stream := ARequest.Body.GetStream;
  ContentType := ARequest.Headers.GetValue('Content-Type');
    // e.g. multipart/form-data; boundary=--------070120105641002

  Path := TPath.Combine(ROOT_PATH, 'images');
  RawPath := TPath.Combine(Path, 'raw_' + LItem + '.jpg');
  Path := TPath.Combine(Path, LItem + '.jpg');

  // Save raw data
  TMemoryStream(Stream).SaveToFile(RawPath);

  // Decode parameters and streams from body stream.
  DecodeParamsAndStream(Stream, ContentType, Params, StreamParams);

  // Using parameters as a below
  if Params.Values['user_id'] = '123' then
  begin
  end;

  // Save photo parameter to a file.
  if Assigned(StreamParams.Streams['photo']) then
    TMemoryStream(StreamParams.Streams['photo']).SaveToFile(Path);

  StreamParams.Free;
  Params.Free;
end;&lt;/pre&gt;
&lt;p&gt;참고로, 파일 저장 방식은 파일로 저장하는 방식과 Blob 필드에 저장하는 방식이 있으며, 이 예제에서는 지정경로(ROOT_PATH) 하위 images 디렉토리에 {item}항목 이름으로 저장했습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;파일로 저장시의 주의점은 RAD 서버의 로컬 디스크로 저장 시 서버를 병렬화(여러대 구성) 시 접근이 제한됩니다. 네트워크 경로 또는 공유 파일 시스템을 이용해야 합니다.(또는 파일 공유 솔루션 등을 이용할 수도 있습니다.)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Blob 필드로 저장 시 주의점은 데이터와 별도의 테이블에 Blob 필드를 구성하는 것을 추천드립니다. 성능 측면과 향후 데이터 관리(백업 등) 시 유리할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;클라이언트(REST Client) 측 구현&lt;/h2&gt;&lt;p&gt;REST 클라이언트를 이용해 파일 업로드 구현은 데이터 전송과 크게 차이나지 않습니다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;procedure TForm1.Button1Click(Sender: TObject);
var
  Filepath: string;
  Stream: TFileStream;
begin
  if not OpenDialog1.Execute then
    Exit;

  Filepath := OpenDialog1.FileName;
  Stream := TFileStream.Create(Filepath, fmOpenRead);

  RESTClient1.BaseURL := 'http://localhost:8080';
  RESTRequest2.Method := rmPOST;
  RESTRequest2.Resource := 'images/{item}/photo';
  RESTRequest2.Params.ParameterByName('item').Value := Edit1.Text;

  RESTRequest2.Params.AddItem('user_id', '123');
  RESTRequest2.Params.AddItem('photo', Stream, pkFILE, [], ctAPPLICATION_OCTET_STREAM);
  RESTRequest2.Execute;

  Stream.Free;
end;&lt;/pre&gt;
&lt;p&gt;파일을 추가할 경우, TStream 이용하므로, TStream을 상속받는 객체들(TFileStream, TMemoryStream 등)을 이용할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;스트림을 파라미터로 추가하는 코드는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;RESTRequest2.Params.AddItem('photo', Stream, pkFILE, [], ctAPPLICATION_OCTET_STREAM);&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;파라미터 종류를 pkFILE로 지정시 내부적으로 multipart/form-data 인코딩 타입으로 데이터가 전송되며, 컨텐트타입을&amp;nbsp;ctAPPLICATION_OCTET_STREAM으로 지정해야 합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h1&gt;파일 다운로드 구현 방안&lt;/h1&gt;&lt;div&gt;&lt;h2&gt;서버(RAD Server) 측 구현&lt;/h2&gt;&lt;div&gt;파일 다운로드는 GetItemPhoto 메소드에서 구현합니다.&lt;/div&gt;&lt;div&gt;&lt;pre class=&quot;brush:delphi&quot;&gt;procedure TImagesResource1.GetItemPhoto(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
var
  LItem: string;

  Path: string;
  Stream: TStream;
begin
  LItem := ARequest.Params.Values['item'];

  Path := TPath.Combine(ROOT_PATH, 'images');
  Path := TPath.Combine(Path, LItem + '.jpg');

  if not TFile.Exists(Path) then
    AResponse.RaiseNotFound('Not found', '''' + LItem + ''' is not found');

  Stream := TFileStream.Create(Path, fmOpenRead);
  AResponse.Body.SetStream(Stream, 'image/jpeg', True);
end;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;요청한 {item} 항목의 파일을 스트림(TFileStream)으로 읽어 그대로 추력합니다.&lt;/div&gt;&lt;div&gt;주의할 점은 마지막 줄의 SetStream의 마지막 파라미터(AOwnerValue)를 True로 지정해야 Stream 객체가 해제됩니다.(직접 해제 시 메모리 참조 오류가 발생합니다.) False로 지정 시 객체를 복사하므로, 직접 해제해도 됩니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2&gt;클라이언트(REST Client) 측 구현&lt;/h2&gt;&lt;div&gt;VCL Form Application에서 REST Client로 이미지를 다운로드 후 이미지(TImage)에 표시하는&amp;nbsp;코드는 다음과 같습니다.&lt;/div&gt;&lt;div&gt;&lt;pre class=&quot;brush:delphi&quot;&gt;procedure TForm1.Button2Click(Sender: TObject);
var
  WICImage: TWICImage;
  Stream: TMemoryStream;
begin
  RESTClient1.BaseURL := 'http://localhost:8080';
  RESTRequest1.Method := rmGET;
  RESTRequest1.Resource := 'images/{item}/photo';
  RESTRequest1.Params.ParameterByName('item').Value := Edit1.Text;
  RESTRequest1.ExecuteAsync(procedure
    begin
      if RESTResponse1.StatusCode = 404 then
        Exit;
      Stream := TMemoryStream.Create;
      Stream.WriteData(RESTResponse1.RawBytes, RESTResponse1.ContentLength);
      WICImage := TWICImage.Create;
      WICImage.LoadFromStream(Stream);
      Image1.Picture.Assign(WICImage);
      WICImage.Free;
      Stream.Free;
    end);
end;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;이미지 등의 용량이 큰 요청의 경우 비동기로 요청하는 것이 좋습니다.&lt;/div&gt;&lt;div&gt;다양한 이미지 포맷을 지원하기 위해 TWICImage 컴포넌트를 사용했습니다.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;완성된 프로젝트 샘플코드&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block;   height: auto; max-width: 100%;&quot;&gt;&lt;a href=&quot;https://t1.daumcdn.net/cfile/tistory/99714C3A5F4C962F12&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;https://i1.daumcdn.net/cfs.tistory/v/0/blog/image/extension/zip.gif&quot; style=&quot;vertical-align: middle;&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;RESTUpload.zip&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/b&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Delphi/C++Builder</category>
      <category>RAD Server</category>
      <category>REST</category>
      <category>Rest Client</category>
      <author>험프리.김현수</author>
      <guid isPermaLink="true">https://blog.hjf.pe.kr/518</guid>
      <comments>https://blog.hjf.pe.kr/518#entry518comment</comments>
      <pubDate>Mon, 31 Aug 2020 15:19:30 +0900</pubDate>
    </item>
    <item>
      <title>OAuth 2.0 연동 - 네이버 API 연동(네이버 아이디로 로그인)</title>
      <link>https://blog.hjf.pe.kr/517</link>
      <description>&lt;p style=&quot;margin-right: 0px; padding-right: 0px; padding-left: 0px; padding-top: 0px !important; padding-bottom: 0px !important;&quot;&gt;이 글에서는 OAuth 2.0을 이용 네이버 API와 연동하는 방법을 알아봅니다.&lt;/p&gt;&lt;p style=&quot;margin-right: 0px; margin-left: 0px; padding-right: 0px; padding-left: 0px; padding-top: 0px !important; padding-bottom: 0px !important;&quot;&gt;이 글에서는 네이버 아이디로 로그인 후 회원 프로필 조회 API와 연동해 프로필 정보를 불러오는 델파이 애플리케이션을 작성할 수 있습니다.&lt;/p&gt;&lt;p style=&quot;margin-right: 0px; margin-left: 0px; padding-right: 0px; padding-left: 0px; padding-top: 0px !important; padding-bottom: 0px !important;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 400px; width: 400px; height: 319px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9946AC3C5F0E68F826&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9946AC3C5F0E68F826&quot; width=&quot;400&quot; height=&quot;319&quot; filename=&quot;naver_result.png&quot; filemime=&quot;image/png&quot; style=&quot;width: 400px; height: 319px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-right: 0px; margin-left: 0px; padding-right: 0px; padding-left: 0px; padding-top: 0px !important; padding-bottom: 0px !important;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-right: 0px; margin-left: 0px; padding-right: 0px; padding-left: 0px; padding-top: 0px !important; padding-bottom: 0px !important;&quot;&gt;다음 글을 통해&amp;nbsp;OAuth 2.0을 이용&amp;nbsp;카카오 API와 연동하는 내용을 다뤘습니다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://blog.hjf.pe.kr/483&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;OAuth 2.0 연동 - 카카오 API(카카오톡 프로필)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p style=&quot;margin-right: 0px; margin-left: 0px; padding-right: 0px; padding-left: 0px; padding-top: 0px !important; padding-bottom: 0px !important;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-right: 0px; margin-left: 0px; padding-right: 0px; padding-left: 0px; padding-top: 0px !important; padding-bottom: 0px !important;&quot;&gt;OAuth2.0 연동하는 절차는 카카오, 네이버 API 뿐아니라 대부분의 서비스들이 비슷합니다.&lt;/p&gt;&lt;p style=&quot;margin-right: 0px; margin-left: 0px; padding-right: 0px; padding-left: 0px; padding-top: 0px !important; padding-bottom: 0px !important;&quot;&gt;이 글에서는&amp;nbsp;카카오 API와 네이버 API의 차이점에 대해서만 간략히 설명합니다. 이글을 읽기 전 위 링크의 내용을 먼저 숙지하시기 바랍니다.&lt;/p&gt;&lt;p style=&quot;margin-right: 0px; margin-left: 0px; padding-right: 0px; padding-left: 0px; padding-top: 0px !important; padding-bottom: 0px !important;&quot;&gt;(네이버와 카카오 OAuth 2.0의 차이점은 앱 등록하는 과정, API의 엔드포인트(URI)와 일부 파라미터 종류 및 이름 뿐입니다.)&lt;/p&gt;&lt;p style=&quot;margin-right: 0px; margin-left: 0px; padding-right: 0px; padding-left: 0px; padding-top: 0px !important; padding-bottom: 0px !important;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-right: 0px; margin-left: 0px; padding-right: 0px; padding-left: 0px; padding-top: 0px !important; padding-bottom: 0px !important;&quot;&gt;&lt;/p&gt;&lt;h2&gt;[준비] 준비사항,&amp;nbsp;앱 등록&lt;/h2&gt;&lt;div&gt;네이버 아이디로 로그인 API의 명세는 다음 링크로 확인할 수 있습니다.&lt;/div&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://developers.naver.com/docs/login/api/&quot;&gt;https://developers.naver.com/docs/login/api/&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;사전 준비사항으로 다음 링크에서 애플리케이션을&amp;nbsp;등록해야 합니다.&lt;/div&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://developers.naver.com/apps/#/register?api=nvlogin&quot;&gt;https://developers.naver.com/apps/#/register?api=nvlogin&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;등록 후 애플리케이션 정보에서 Client ID와 Client Secret을 확인합니다.(구현 시 사용)&lt;/div&gt;&lt;div&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 605px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99CBA73B5F0E610F23&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99CBA73B5F0E610F23&quot; width=&quot;605&quot; height=&quot;202&quot; filename=&quot;naver_clientid.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;API 설정에서 &quot;사용 API&quot; 항목에서 &quot;네아로(네이버 아이디로 로그인)&quot;항목을 선택하고, 제공 정보를 선택합니다.&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 605px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99DF77455F0E619321&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99DF77455F0E619321&quot; width=&quot;605&quot; height=&quot;280&quot; filename=&quot;naver_nailo.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;API 설정에서 &quot;로그인 오픈 API 서비스 환경&quot; 항목에서 &quot;PC웹&quot; 환경을 추가하고, 서비스 URL과 Callback URL을 등록합니다.(테스트 시에는 제 OAuth 페이지를 이용해도 좋습니다.&amp;nbsp;http://hjf.pe.kr/oauth)&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 605px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99A4F03F5F0E619322&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99A4F03F5F0E619322&quot; width=&quot;605&quot; height=&quot;436&quot; filename=&quot;naver_loginenv.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;네이버 아이디로 로그인 시 사용하는 API는 다음과 같습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 605px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/994A5E375F0E62B422&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F994A5E375F0E62B422&quot; width=&quot;605&quot; height=&quot;189&quot; filename=&quot;naver_apibasic.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;h2&gt;[1단계] 로그인 및 인증코드 취득
&lt;/h2&gt;&lt;div&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&quot;네이버 아이디로 로그인 인증 요청&quot; 페이지의 요청 변수는 다음과 같습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 605px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99ED094A5F0E62F91F&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99ED094A5F0E62F91F&quot; width=&quot;605&quot; height=&quot;417&quot; filename=&quot;naver_api1.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;대부분 카카오 API와 같지만, 새로운 state 값이 있습니다. 해당 값은 사이트 간 요청 위조 공격을 방지하기 위한 값으로, 요청 시 입의의 값을 전달하면 응답시 동일한 값을 반환해 내가 작성한 요청에 대한 응답인지를 확인하기 위한 목적입니다. state는 임의의 값을 사용해도 됩니다. 샘플에서는 &quot;abcdef&quot; 값을 사용했습니다.(랜덤으로 문자열을 생성해서 사용해도 됩니다.)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;작성된 코드는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;procedure TForm3.Button1Click(Sender: TObject);
var
  URL, Param: string;
begin
  URL := 'https://nid.naver.com';
  Param := '/oauth2.0/authorize?response_type=code&amp;amp;client_id={client_id}&amp;amp;redirect_uri={redirect_uri}&amp;amp;state={state}';
  Param := Param.Replace('{client_id}', CLIENT_ID);
  Param := Param.Replace('{redirect_uri}', REDIRECT_URI);
  Param := Param.Replace('{state}', 'abcdef');

  WebBrowser1.URL := URL + Param;
  WebBrowser1.Navigate;
end;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;위 코드를 이용해 실행 후 로그인 및 동의 화면 후 다음과 같은 웹페이지로 리다이렉션 됩니다.&lt;/div&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;http://hjf.pe.kr/oauth/?&lt;b&gt;&lt;span style=&quot;color: rgb(255, 0, 0);&quot;&gt;code=7geMnuaVmQDsDbpoUl&lt;/span&gt;&lt;/b&gt;&amp;amp;state=abcdef&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;이동된 페이지 URL에서 code 정보를 취득 후 엑세스 토큰 취득시 사용합니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;h2&gt;[2단계] 엑세스 토큰 취득&lt;/h2&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;엑세스 토큰 취득 API의 요청 변수는 다음과 같습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 605px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/994DE7375F0E663A24&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F994DE7375F0E663A24&quot; width=&quot;605&quot; height=&quot;419&quot; filename=&quot;naver_api2.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;1단계에서 취득한 code를 포함해 요청합니다.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;작성된 코드는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  token: string;
begin
  RESTClient1.BaseURL := 'https://nid.naver.com/';

  RESTRequest1.Resource := 'oauth2.0/token';
  RESTRequest1.Method := rmPOST;
  RESTRequest1.Params.Clear;
  RESTRequest1.Params.AddItem('grant_type',     'authorization_code');
  RESTRequest1.Params.AddItem('client_id',      CLIENT_ID);
  RESTRequest1.Params.AddItem('client_secret',  CLIENT_SECRET);
  RESTRequest1.Params.AddItem('code',           Edit1.Text);
  RESTRequest1.Params.AddItem('state',          'abcdef');

  RESTRequest1.Execute;

  Memo1.Lines.Text := RESTResponse1.Content;

  if RESTResponse1.JSONValue.TryGetValue&amp;lt;string&amp;gt;('access_token', token) then
    Edit2.Text := token;
end;&lt;/pre&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;다음과 같은 응답을 받으면, 필요한 access_token을 가져옵니다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 557px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99F3CA335F0E673324&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99F3CA335F0E673324&quot; width=&quot;557&quot; height=&quot;137&quot; filename=&quot;naver_accesstoken_json.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;h2&gt;[3단계] 서비스에 접근(엑세스 토큰 이용)&lt;/h2&gt;&lt;p&gt;엑세스 토큰으로 다양한 네이버 API에 접근할 수 있습니다.(단, 애플리케이션 등록 시 설정한 앱에 한함)&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;네이버 회원 프로필 조회 API는 다음과 같습니다.&lt;/div&gt;&lt;div&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 605px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99C110425F0E680B24&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99C110425F0E680B24&quot; width=&quot;605&quot; height=&quot;437&quot; filename=&quot;naver_profile_api.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;정상적인 요청에 대한 응답은 다음과 같습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 605px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/991F7E505F0E683627&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F991F7E505F0E683627&quot; width=&quot;605&quot; height=&quot;266&quot; filename=&quot;naver_profile_json.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;작성된 코드는 다음과 같습니다.&lt;br /&gt;&lt;/p&gt;&lt;pre class=&quot;brush:delphi&quot;&gt;procedure TForm3.Button3Click(Sender: TObject);
var
  nickname, name, profile_url: string;
  Stream: TMemoryStream;
begin
  OAuth2Authenticator1.AccessToken := Edit2.Text;

  RESTClient2.BaseURL := 'https://openapi.naver.com/';
  RESTClient2.Authenticator := OAuth2Authenticator1;

  RESTRequest2.Client := RESTClient2;
  RESTRequest2.Response := RESTResponse2;

  RESTRequest2.Resource := 'v1/nid/me';
  RESTRequest2.Execute;

  Memo1.Lines.Text := RESTResponse2.Content;

  name := RESTResponse2.JSONValue.GetValue&amp;lt;string&amp;gt;('response.name');
  nickname := RESTResponse2.JSONValue.GetValue&amp;lt;string&amp;gt;('response.nickname');
  Edit3.Text := name;
  Edit4.Text := nickname;

  profile_url := RESTResponse2.JSONValue.GetValue&amp;lt;string&amp;gt;('response.profile_image');

  RESTClient3.BaseURL := profile_url;
  RESTRequest3.Execute;
  RESTRequest3.ExecuteAsync(procedure
    begin
      Stream := TMemoryStream.Create;
      try
        Stream.WriteData(RESTResponse3.RawBytes, RESTResponse3.ContentLength);
        ImageControl1.Bitmap.LoadFromStream(Stream);
      finally
        Stream.Free;
      end;
    end
  );
end;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;완료 및 테스트&lt;/h2&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;완성된 화면의 테스트 결과는&amp;nbsp;다음과 같습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 605px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9946AC3C5F0E68F826&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9946AC3C5F0E68F826&quot; width=&quot;605&quot; height=&quot;482&quot; filename=&quot;naver_result.png&quot; filemime=&quot;image/png&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;완성된 프로젝트 파일&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block;   height: auto; max-width: 100%;&quot;&gt;&lt;a href=&quot;https://t1.daumcdn.net/cfile/tistory/9934C73A5F0E694D29&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;https://i1.daumcdn.net/cfs.tistory/v/0/blog/image/extension/zip.gif&quot; style=&quot;vertical-align: middle;&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;NaverOAuth20.zip&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;div&gt;&lt;h1&gt;추가 학습할 내용&lt;/h1&gt;&lt;div&gt;이 글은 다음 링크의 OAuth2.0 카카오 API 연동하는 내용과의 차이점을 기술합니다. OAuth 2.0의 프로세스와 카카오 API 연동의 자세한 내용은 다음 링크에서 확인할 수 있습니다.&lt;/div&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://blog.hjf.pe.kr/483&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;OAuth 2.0 연동 - 카카오 API(카카오톡 프로필)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;네이버, 카카오 등의&amp;nbsp;서비스 인증(OAuth 2.0) 및 API 연동은 REST API 기반으로 구현됩니다. 다음 글들을 통해 REST API를 이해하고 실습할 수 있습니다.&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://blog.hjf.pe.kr/462&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;REST API 이해하기&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://blog.hjf.pe.kr/463&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;REST API 서버 개발하기(엔드포인트 구현, RAD 서버 이용)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://blog.hjf.pe.kr/464&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;REST API 클라이언트 개발하기(REST Client 이용)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://blog.hjf.pe.kr/465&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;데이터셋 기반 REST API 개발하기&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;관련/참고 링크&lt;/h3&gt;&lt;div&gt;&lt;hr class=&quot;tx-hr-border-3&quot; style=&quot;border-color: black;&quot;&gt;&lt;ul&gt;&lt;li&gt;OAuth 2.0 공식 페이지&amp;nbsp;-&amp;nbsp;&lt;a href=&quot;https://oauth.net/2/&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://oauth.net/2/&lt;/a&gt;&lt;/li&gt;&lt;li&gt;네이버 API&amp;nbsp;관련 링크&lt;/li&gt;&lt;ul&gt;&lt;li&gt;네이버 아이디로 로그인 API 명세 -&lt;a href=&quot;https://developers.naver.com/docs/login/api/&quot;&gt;https://developers.naver.com/docs/login/api/&lt;/a&gt;&lt;/li&gt;&lt;li&gt;네이버 회원 프로필 조회 API 명세 -&amp;nbsp;&lt;a href=&quot;https://developers.naver.com/docs/login/profile/&quot;&gt;https://developers.naver.com/docs/login/profile/&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;</description>
      <category>파이어몽키</category>
      <author>험프리.김현수</author>
      <guid isPermaLink="true">https://blog.hjf.pe.kr/517</guid>
      <comments>https://blog.hjf.pe.kr/517#entry517comment</comments>
      <pubDate>Wed, 15 Jul 2020 11:30:58 +0900</pubDate>
    </item>
    <item>
      <title>3회차 &amp;quot;커뮤니케이션 데이 - 마이그레이션&amp;quot; 회고</title>
      <link>https://blog.hjf.pe.kr/516</link>
      <description>&lt;p&gt;데브기어에서는 매월 첫번째 화요일 커뮤니케이션 데이를 진행합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;b&gt;델파이/C++빌더 커뮤니케이션 데이는?&amp;nbsp;&lt;/b&gt;&lt;/p&gt;&lt;p&gt;커뮤니케이션 데이는 특정 주제로 델파이/C++빌더 개발자 분들이 모여 함께 토의하고, 정보를 공유하고, 전문가의 조언을 들을 수 있는 오프라인 모임입니다. 매월 첫째주 화요일 데브기어 라운지에서 진행됩니다.&amp;nbsp;&lt;br /&gt;다음 신청 페이지에서 신청할 수 있습니다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://docs.google.com/forms/d/e/1FAIpQLScX3K-GsrqVLXOmwtyVQ2hc22CmImmszxOS8yY9t73gDUZUrA/viewform&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;델파이/C++빌더&amp;nbsp;커뮤니케이션 DAY 신청 페이지&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2월은 마이그레이션 주제로 진행되었습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;다음 참석대상자 분들은 많은 도움을 받을 수 있을것입니다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;마이그레이션을 계획 중이며, &lt;b&gt;효과적인 마이그레이션 프로세스를 조언&lt;/b&gt;받고 싶은 분들&lt;/li&gt;&lt;li&gt;마이그레이션 중 자체적으로 해결하기 &lt;b&gt;어려운 이슈에 대한 해결 방향&lt;/b&gt;에 대한 조언&lt;/li&gt;&lt;li&gt;써드파티 &lt;b&gt;컴포넌트 대체 및 컴포넌트 통합&lt;/b&gt;에 대한 구체적인 방안&lt;/li&gt;&lt;li&gt;마이그레이션 &lt;b&gt;방향에 대한 전반적인 의견&lt;/b&gt;을 듣고 싶은 분들&lt;/li&gt;&lt;li&gt;마이그레이션 프로젝트/컨설팅의&amp;nbsp;&lt;b&gt;경험 및 노하우&lt;/b&gt;가 듣고 싶은 분들&lt;/li&gt;&lt;li&gt;외부 사용자 증가 등으로 &lt;b&gt;아키텍처 변경(예&amp;gt; 2티어 &amp;gt; 멀티티어)이 필요&lt;/b&gt;하신 분들&lt;/li&gt;&lt;li&gt;기타&lt;b&gt;&amp;nbsp;주제와는 상관 없는&amp;nbsp;개발 이슈&lt;/b&gt;에 대해 이야기하고 싶은 분들&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h1&gt;3회차 마이그레이션 데이&lt;/h1&gt;&lt;p&gt;어제(2.4)는 3회차 마이그레이션 데이가 진행되었고, 신청자 4분 중 2분이 참석해 주셨고, 순차적으로 방문하셔서 개별 면담 형식으로 진행했습니다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 499px; width: 499px; height: 374px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/996E4C3B5E3A238738&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F996E4C3B5E3A238738&quot; width=&quot;499&quot; height=&quot;374&quot; filename=&quot;200204_3회차.JPG&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 499px; height: 374px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;미리 준비한 질문지에 현장&amp;nbsp;질문을 기록하고 답변 및 참고할 내용을 기록해 참석하신 분들에게&amp;nbsp;제공했습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 605px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/997876465E3A59120B&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F997876465E3A59120B&quot; width=&quot;605&quot; height=&quot;404&quot; filename=&quot;200204_질문지.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;다음 진행 시에는 좀더 구체적인 질문을 받아서 미리 답변을 준비하도록 개선하는 것이 좋을 것 같습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;어제 진행된 주요 문의 내용은 다음과 같습니다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;&lt;b&gt;가스제어 장보 모니터링 소프트웨어 개발사&lt;/b&gt;&lt;/li&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;프로젝트 방향과 개발방향 문의 목적으로 참가&lt;/li&gt;&lt;li&gt;C++빌더로 개발된 프로젝트를 델파이로 전환하는 방안 문의&lt;/li&gt;&lt;li&gt;외주업체에서 받은 C++빌더 프로젝트를 내무 개발자가 유지보수하기 어렵다 판단해 변경을 원함&lt;/li&gt;&lt;li&gt;상담결과, 굳이 델파이로 전환할 필요 없다 결정(C++ 학습 및 교육 진행)&lt;/li&gt;&lt;li&gt;기타, DBMS 변경 및 연결 기술등의 개발 방향에 대해 안내&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;&lt;b&gt;반도체 유통 ERP 자체 개발 및 운영&lt;/b&gt;&lt;/li&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;자체적으로&amp;nbsp;마이그레이션 진행이 필요하며, 마이그레이션 이슈 해결 및 방향 문의목적으로 참가&lt;/li&gt;&lt;li&gt;써드파티 컴포넌트(리얼그리드, NumberEdit, FlatControl) 사용 중&lt;/li&gt;&lt;li&gt;리얼그리드 전환의 어려움 &amp;gt; 기존 컨설팅 사례를 통해 컴포넌트 전환 자동화 방안 안내&lt;/li&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://tech.devgear.co.kr/delphi_news/459559&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;데브기어 컴포넌트 컨버터 안내&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;데브기어 마이그레이션 컨설팅 및 워크샵 과정 안내&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;많은 분들이 참석하지 않아 조촐히 진행했습니다. 하지만 참석자 분들의 적극적인 질문으로 오후시간을 모두 사용했습니다. 덕분에 현업의 많은 이야기를 들을 수 있는 의미있는 시간이었습니다. 참석자 분들도 의미있는 시간이었기를&amp;nbsp;바랍니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;다음 4회차 커뮤니케이션 데이에도 &quot;마이그레이션&quot; 주제로 새로운 분들을 만나뵙고 많은 이야기를 나누고 싶습니다. 메인 주제는 마이그레이션이지만 다른 기술적인 내용이나 평상시 잘 풀리지 않거나 궁금한 기술적인 내용들, 방향등을 이야기하고 싶다면 다음 링크에서 3월 커뮤니티 데이 참석을 신청해 주세요.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://docs.google.com/forms/d/e/1FAIpQLScX3K-GsrqVLXOmwtyVQ2hc22CmImmszxOS8yY9t73gDUZUrA/viewform&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;델파이/C++빌더&amp;nbsp;커뮤니케이션 DAY 신청 페이지&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>마이그레이션</category>
      <author>험프리.김현수</author>
      <guid isPermaLink="true">https://blog.hjf.pe.kr/516</guid>
      <comments>https://blog.hjf.pe.kr/516#entry516comment</comments>
      <pubDate>Wed, 5 Feb 2020 11:46:55 +0900</pubDate>
    </item>
    <item>
      <title>데브기어 컴포넌트 컨버터 소개(오픈소스)</title>
      <link>https://blog.hjf.pe.kr/514</link>
      <description>&lt;p&gt;&lt;/p&gt;&lt;h1&gt;데브기어 컴포넌트 컨버터&lt;/h1&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 500px; width: 500px; height: 309px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99D6514B5E28FB5034&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99D6514B5E28FB5034&quot; width=&quot;500&quot; height=&quot;309&quot; filename=&quot;CompConverter.png&quot; filemime=&quot;image/png&quot; style=&quot;width: 500px; height: 309px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;데브기어 컴포넌트 컨버터는 델파이 소스파일을 분석해 컴포넌트와 소스코드를 변경해주는 오픈소스 기반 마이그레이션 도구입니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;데브기어 컴포넌트 컨버터는 컴포넌트 컨버터와 소스코드 컨버터 두개의&amp;nbsp;애플리케이션으로 구성됩니다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;컴포넌트 컨버터는 델파이 폼파일(*.dfm)과 소스파일(*.pas)에서 컴포넌트 정보를 변경합니다.&lt;/li&gt;&lt;li&gt;소스코드 컴버터는 델파이 소스파일(*.pas)에서 컴포넌트를 사용한 코드를 찾아 변환합니다.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2&gt;데브기어 컴포넌트 컨버터의 특징&lt;/h2&gt;&lt;div&gt;엡바카데로는 reFind라는 정규표현식 치환 도구를 통해 마이그레이션 자동화 작업을 지원합니다.&lt;/div&gt;&lt;div&gt;reFind는 정규표현식을 이용해 소스파일에서 컴포넌트 종류와 속성등을 전환하는 작업을 할 수 있습니다.&lt;/div&gt;&lt;div&gt;하지만, reFind는 컴포넌트와 속성 등을 일대일로 치환하기 때문에 하나의 컴포넌트를 여러개의 컴포넌트로 나누거나, 속성, 이벤트 등을 추가하는 작업에 제약이 있습니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;데브기어 컴포넌트 컨버터는 컴포넌트 코드를 분석해 컴포넌트 정보를 취득 후, 그 정보로 새로운 컴포넌트로 조합하는 방식으로 구현되었습니다.&lt;/div&gt;&lt;div&gt;이 방식을 이용해 하나의 컴포넌트를 여러개의 컴포넌트로 나누거나, 여러개의 컴포넌트 정보로 하나의 컴포넌트로 합치는 등의 작업이 가능합니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;컴포넌트 정보를 분석하거나 조합하는 과정은 컨버터 클래스에 구현합니다.&lt;/div&gt;&lt;div&gt;이 컨버터 클래스는 컴포넌트 별로 작성해야 하며, 본인의 코딩 스타일에 따라 다르게 구현해야 합니다.&lt;/div&gt;&lt;div&gt;데브기어 컴포넌트 컨버터는 오픈소스로 제공하며, 소스코드를 다운로드 받아 본인의 소스코드에 맞게 직접 제작하는 과정이 필요합니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;h2&gt;데브기어 컴포넌트 컨버터의 기능&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;데브기어 컴포넌트 컨버터의 주요 기능은 다음과 같습니다.&lt;/div&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;소스파일과 폼파일에서 컴포넌트 정보를 변경합니다.&lt;/li&gt;&lt;li&gt;소스파일에서 컴포넌트 사용 코드를 변경합니다.&lt;/li&gt;&lt;li&gt;지정한 디렉토리 하위 파일을 선택해 일괄 변환 가능합니다.&lt;/li&gt;&lt;li&gt;변환작업은 컨버터에 구현되며, 직접 컨버터를 구현해 변환 작업을 추가할 수 있습니다.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;세부적인 특징은 다음과 같습니다.&lt;/div&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;하나의 컴포넌트를 여러개의 컴포넌트로 나누어 변경할&amp;nbsp;수 있습니다.(예&amp;gt; TRealGrid -&amp;gt; TcxGrid, TcxLevel, TcxTableView)&lt;/li&gt;&lt;li&gt;하나의 속성을 여러개의 속성으로 나누어 변경할&amp;nbsp;수 있습니다.&lt;/li&gt;&lt;li&gt;이벤트 핸들러와 매개변수를 변경할 수 있습니다.&lt;/li&gt;&lt;li&gt;uses 절의 유닛을 추가, 제거할 수 있습니다.&lt;/li&gt;&lt;li&gt;컴포넌트를 제거할 수 있습니다.&lt;/li&gt;&lt;li&gt;컴포넌트 속성을 추가할 수 있습니다.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;위 기능들을 이용해 대규모의 컴포넌트와 컴포넌트를 사용하는 소스코드를 일괄 변경할 수 있습니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;제작 계기&lt;/h2&gt;&lt;div&gt;&lt;p&gt;이 도구를 제작한 계기는 리얼그리드(TRealGrid)를 퀀텀그리도(TcxGrid)로 전환하는 작업을 진행하기 위해서입니다.&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;엠바카데로에서는 이미 reFind라는 마이그레이션 도구를 제공합니다. reFind로 컴포넌트 또는 속성을 변경할 수 있습니다. 하지만 reFind는 일대일로 컴포넌트와 속성을 변경합니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;리얼그리드를 퀀텀그리드로 변경하기 위해서는 하나의 리얼그리드 컴포넌트를 여러개의 퀀텀그리드 컴포넌트로 나눠야하고, 두 컴포넌트의 속성이 상이해 재구성해야 했습니다. 또한 이벤트 핸들러의 구조도 상이해 이벤트 핸들러 구조를 변경해 다시 연결해야 합니다. 이와 같이 컴포넌트와 구조가 변경되면 소스코드도 대폭 변경되어야 합니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;만약, 컴포넌트를 사용한 코드가 몇군데라면 한명이 수작업으로 변경하는 것이 좋습니다.&lt;/div&gt;&lt;div&gt;하지만 사용하는 코드가 수십~수백개라면 여려명이 오랜시간 변경작업을 진행해야 할 것입니다. 이 경우 시간도 오래걸리고, 각자의 개발 스타일에 따라 다르게 구현하게 되면 향후 유지보수도 어려울 수 있습니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;위 작업을 자동화 하면, 시간과 인력이 절약되고 동일한 방향으로 변경되어 유지보수에도 유리합니다. 또한 재작업에 대한 부담도 줄게됩니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;사용방법&lt;/h2&gt;&lt;p&gt;데브기어 컴포넌트 컨버터는 오픈소스로 제공됩니다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/devgear/ComponentConverter&quot;&gt;https://github.com/devgear/ComponentConverter&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 도구는 완성된 패키지형 프로그램이 아닙니다. 변환작업을 수행할 컨버터를 직접 구현해야합니다.&lt;/p&gt;&lt;p&gt;다음 링크를 통해 데브기어 컴포넌트 컨버터의 구조와 구현방법을 설명합니다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;데브기어 컴포넌트 컨버터 구조와&amp;nbsp;원리 - 준비 중&lt;/li&gt;&lt;li&gt;데브기어 컴포넌트 컨버터 사용 샘플 - 준비 중&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;사용하며 필요한 기능이나 궁금한 내용은 의견 부탁드립니다.&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>마이그레이션</category>
      <author>험프리.김현수</author>
      <guid isPermaLink="true">https://blog.hjf.pe.kr/514</guid>
      <comments>https://blog.hjf.pe.kr/514#entry514comment</comments>
      <pubDate>Wed, 22 Jan 2020 17:06:21 +0900</pubDate>
    </item>
    <item>
      <title>[오픈소스] TGPuttyLib 소개  - PuTTY 기반 SFTP 클라이언트</title>
      <link>https://blog.hjf.pe.kr/512</link>
      <description>&lt;p&gt;오픈소스 기반 SFTP 클라이언트 라이브러리를 소개합니다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h1&gt;TGPuttyLib&lt;/h1&gt;&lt;p&gt;TGPuttyLib는 독일 델파이 개발자 Tobias Giesen이 운영하는 오픈소스로, PuTTY 기반 SFTP 클라이언트 라이브러리를 제공합니다. 주요 특징으로 알려진 다른 라이브러리 보다 높은 전송속도를 제공한다고 합니다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/superflexible/TGPuttyLib&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://github.com/superflexible/TGPuttyLib&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 500px; width: 500px; height: 328px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9956AA415E0E8B8C28&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9956AA415E0E8B8C28&quot; width=&quot;500&quot; height=&quot;328&quot; filename=&quot;TGPuttyLib_Github.png&quot; filemime=&quot;image/png&quot; style=&quot;width: 500px; height: 328px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;설명에 따르면 다음의 특징이 있습니다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;PuTTY 제품군에서 psftp 프로그램을 DLL로 변환한 것&lt;/li&gt;&lt;li&gt;개발자는 가장 높은 전송 속도(100 MB/Sec 이상)로 파일 전송 가능(알려진 다른 라이브러리 보다 높은 속도)&lt;/li&gt;&lt;li&gt;C++, Delphi, Free Pascal 에서 즉시 사용 가능한 클래스 제공&lt;/li&gt;&lt;li&gt;PuTTY Release 0.73 기반&lt;/li&gt;&lt;li&gt;2020년 1분기 MacOS 및 리눅스 지원 계획&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;컴파일된 데모와 자세한 내용은 프로젝트 웹사이트를 통해 확인할 수 있습니다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://www.syncovery.com/tgputtylib&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://www.syncovery.com/tgputtylib&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h1&gt;컴파일된 데모&lt;/h1&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 400px; width: 400px; height: 311px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99F3023D5E0E91091E&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99F3023D5E0E91091E&quot; width=&quot;400&quot; height=&quot;311&quot; filename=&quot;TGPuttyLib_Demo.png&quot; filemime=&quot;image/png&quot; style=&quot;width: 400px; height: 311px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;컴파일된 데모(DelphiVCLDemo)를 실행해본 결과, SFTP의 기본기능이 전반적으로 구현되어 완성도 높아 보였습니다.&lt;/p&gt;&lt;p&gt;데모의 코드는 클래스 기반으로 라이브러리를 다룹니다. 다양한 데모 코드를 제공해 손쉽게 원하는 기능 구현 가능할 것으로 평가됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;개발자 웹사이트를 확인하니, 파일 동기화 및 백업 소프트웨어 솔루션 개발이 주업으로 보이며, 그 중 일부를 오픈소스로 공개한 것으로 보입니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;기존에 개발한 FTP 작업의 속도가 느리거나, 더 빠른 속도로 FTP 작업이 필요한 경우 또하나의 선택지가 될 수 있을 것 같습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Delphi/C++Builder</category>
      <author>험프리.김현수</author>
      <guid isPermaLink="true">https://blog.hjf.pe.kr/512</guid>
      <comments>https://blog.hjf.pe.kr/512#entry512comment</comments>
      <pubDate>Fri, 3 Jan 2020 10:07:29 +0900</pubDate>
    </item>
    <item>
      <title>델파이에서 아이콘 폰트 사용하기</title>
      <link>https://blog.hjf.pe.kr/510</link>
      <description>&lt;p&gt;아이콘 폰트는 폰트파일에 문자 대신 아이콘을 추가해 아이콘을 사용할 수 있는 폰트파일입니다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 298px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99744C405DF1E6CF28&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99744C405DF1E6CF28&quot; width=&quot;298&quot; height=&quot;128&quot; filename=&quot;iconfonts.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;아이콘 폰트를 사용하면 다양한 아이콘을 손쉽게 그리고 통일되게&amp;nbsp;사용할 수 있습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;대표적인 아이콘 폰트는 다음과 같습니다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://fontawesome.com/?from=io&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;Font Awesome&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://material.io/resources/icons/&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;Google Meterial Icons&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://bootstrapk.com/components/&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;Bootstrap Glyphicons&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://xpressengine.github.io/XEIcon/index.html&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;XEICON&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ionicons.com/&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;Ionicons&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;위 링크에서 아이콘 폰트&amp;nbsp;설치 후 문자표(Characters map) 프로그램등으로 다음과 같이 글꼴을 확인할 수 있습니다. 문자 선택 시 하단에 코드(U+F087)가 표시됩니다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 300px; width: 300px; height: 339px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9949C8445DF1DE9204&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9949C8445DF1DE9204&quot; width=&quot;300&quot; height=&quot;339&quot; filename=&quot;char_table.png&quot; filemime=&quot;image/png&quot; style=&quot;width: 300px; height: 339px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이&amp;nbsp;글에서는 아이콘 폰트를 델파이에서 사용할 수 있도록하는 오픈소스들 소개합니다.&lt;/p&gt;&lt;p&gt;(소개하는 3가지 방식 모두 VCL 기반으로만 동작합니다.)&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;IconFontsImageList&lt;/li&gt;&lt;li&gt;FontIconEditor&lt;/li&gt;&lt;li&gt;Symbols&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;h1&gt;IconFontsImageList&lt;/h1&gt;&lt;p&gt;IconFontsImageList는 TImageList 컴포넌트를 상속받은 TIconFontsImageList 컴포넌트를 이용해 아이콘 폰트를 사용할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 500px; width: 500px; height: 349px;; height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/996A973C5DF1CA8B33&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F996A973C5DF1CA8B33&quot; width=&quot;500&quot; height=&quot;349&quot; filename=&quot;IconFontsImageList.jpg&quot; filemime=&quot;image/jpeg&quot; style=&quot;width: 500px; height: 349px;&quot; original=&quot;yes&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/EtheaDev/IconFontsImageList&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://github.com/EtheaDev/IconFontsImageList&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p style=&quot;clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;clear: none; float: none;&quot;&gt;위 링크에서 컴포넌트 다운로드 후 설치(라이브러리 패스 추가 필요) 후에 사용할 수 있습니다.&lt;/p&gt;&lt;p style=&quot;clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;clear: none; float: none;&quot;&gt;TIconFontsImageList 컴포넌트 추가 후 컴포넌트를 더블클릭하면 다음과 같은 에디터가 표시됩니다.&lt;/p&gt;&lt;p style=&quot;clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 605px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/991CC03F5DF1E12425&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F991CC03F5DF1E12425&quot; width=&quot;605&quot; height=&quot;280&quot; filename=&quot;ifil.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Properties of ImageList에서 사용할 아이콘 폰트(FontName)와 이미지 크기(Size), 색상(FontColor) 등을 선택합니다.&lt;/p&gt;&lt;p&gt;Properties of Selected Icon에서 [Add] 버튼을 눌러 이미지를 추가합니다.&lt;/p&gt;&lt;p&gt;폰트 아이콘 코드를 입력합니다.(문자표 등에서 확인 가능: [Show Char Map...] 버튼 이용)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이후 기존 이미지리스트와 동일하게 사용할 수 있습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h1&gt;FontIconEditor&lt;/h1&gt;&lt;div&gt;&lt;div&gt;FontIconEditor 프로젝트는 기존 이미지 리스트(TImageList)에 아이콘 폰트 추가하는 기능을 확장하는 컴포넌트입니다.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/lminuti/FontIconEditor&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://github.com/lminuti/FontIconEditor&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;div&gt;&lt;p style=&quot;clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;해당 컴포넌트를 설치하면 이미지리스트 팝업 메뉴에&amp;nbsp;&quot;Add font icons...&quot; 메뉴가 추가됩니다.&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 331px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99A6FE395DF1E2B719&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99A6FE395DF1E2B719&quot; width=&quot;331&quot; height=&quot;104&quot; filename=&quot;fie_01.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;해당 메뉴 클릭 시 아래와 같이 등록화면이 표시됩니다.&lt;/div&gt;&lt;div&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 307px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9975D74B5DF1E2B711&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9975D74B5DF1E2B711&quot; width=&quot;307&quot; height=&quot;439&quot; filename=&quot;fie_02.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;아이콘 폰트의 아이콘을 선택 해 이미지 리스트에 추가할 수 있습니다.&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 592px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9937BD405DF1E2B71B&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9937BD405DF1E2B71B&quot; width=&quot;592&quot; height=&quot;338&quot; filename=&quot;fie_03.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h1&gt;Symbols&lt;/h1&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;한국 델파이 구루이신 안영제 님께서 공개한 내용입니다. 샘플 프로젝트입니다.&lt;/p&gt;&lt;div&gt;&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 522px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9962F73A5DF1E51E28&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9962F73A5DF1E51E28&quot; width=&quot;522&quot; height=&quot;502&quot; filename=&quot;symbols.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;&lt;b&gt;&lt;br class=&quot;Apple-interchange-newline&quot;&gt;Symbols 저장소&lt;/b&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/civilian7/Symbols&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://github.com/civilian7/Symbols&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;clear: none; float: none;&quot;&gt;Segoe MDL2 Assets 폰트를 이용했지만, 다른 아이콘 폰트를 사용해도 됩니다.&lt;/p&gt;&lt;p style=&quot;clear: none; float: none;&quot;&gt;(단, 개발PC 뿐 아니라, 사용자PC에도 해당 폰트가 설치되어 있어야 합니다.)&lt;/p&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Delphi/C++Builder</category>
      <author>험프리.김현수</author>
      <guid isPermaLink="true">https://blog.hjf.pe.kr/510</guid>
      <comments>https://blog.hjf.pe.kr/510#entry510comment</comments>
      <pubDate>Thu, 12 Dec 2019 16:08:42 +0900</pubDate>
    </item>
    <item>
      <title>첫번째 &amp;quot;델파이 마이그레이션 DAY&amp;quot; 회고</title>
      <link>https://blog.hjf.pe.kr/509</link>
      <description>&lt;p&gt;지난 화요일(12월 3일) 데브기어 라운지에서 첫번째 &quot;델파이 마이그레이션 DAY&quot;를 진행했습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;b&gt;마이그레이션 데이는?&lt;/b&gt;&lt;/p&gt;&lt;p&gt;최근 윈도우 10 지원과 애플리케이션 현대화를 위한 마이그레이션 및 업그레이드를 시작하는 개발자들의 고민과 질문을 함께 고민하는 자리입니다. 해당 행사는 &lt;b&gt;정기적으로 매월 첫번째 화요일에 데브기어 라운지에서 오프라인으로 진행&lt;/b&gt;합니다. 단순히 세미나 형식으로 정보를 받는것이 아닌 서로 자유롭게 정보와 의견을 나누는 커뮤니케이션 시간입니다.&lt;/p&gt;&lt;p&gt;소스코드를 직접 가져오시면 더욱 좋습니다. 전문가들의 의견을 들을 수 있습니다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://tech.devgear.co.kr/board_in1/458009&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;델파이 마이그레이션 DAY 신청 페이지&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;b&gt;첫번째 마이그레이션 데이&lt;/b&gt;&lt;/p&gt;&lt;p&gt;첫번째 마이그레이션 데이는 5팀이 신청해 주셨고, 적은 인원으로 아주 가깝게&amp;nbsp;진행되었습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;각 업체에서는 당면한 기술적인 이슈에 대한 주제와 마이그레이션 시 컴포넌트 전환에 대한 주제로 자유롭게 의견을 나누었고, 저도 제 경험을 토대로 가이드를 드렸습니다.(아쉽게도 사전동의를 얻지 못해 자세한 내용 공유하지 못합니다.&amp;nbsp;다음 회고에서 남기도록 하겠습니다.)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;b&gt;앞으로의 계획은&amp;nbsp;&lt;/b&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;마이그레이션 뿐 아니라 다양한 주제로 많은 개발자 분들과 소통하고 싶습니다.&lt;/li&gt;&lt;li&gt;데브기어 주관이지만 개발 전문가분들의 소통의 장이 되었으면 합니다.&lt;/li&gt;&lt;li&gt;적극적인 홍보로 많은 분들에게 알리도록 하겠습니다.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;다음 마이그레이션 데이(2020년 1월 7일)에서 뵈요!!&lt;/p&gt;</description>
      <category>교육, 세미나</category>
      <author>험프리.김현수</author>
      <guid isPermaLink="true">https://blog.hjf.pe.kr/509</guid>
      <comments>https://blog.hjf.pe.kr/509#entry509comment</comments>
      <pubDate>Thu, 5 Dec 2019 16:37:56 +0900</pubDate>
    </item>
  </channel>
</rss>