5분 만에 만드는 Delphi 웹 CRUD 페이지

· 컴포넌트
5분 만에 만드는 Delphi 웹 CRUD 페이지

모든 업무용 애플리케이션에는 테이블마다 반복해서 작성되는 똑같은 페이지가 들어 있습니다. 검색창과 페이저가 있는 목록, 생성과 편집을 위한 폼, 삭제 확인, 그리고 쓰기를 담당하는 코드입니다. 저희 자체 관리자 데모만 해도 고객 하나만을 위해 네 개의 요청 핸들러와 약 400줄의 페이지 빌더 코드가 있었고, 공급업체를 위해 또 그만큼, 인보이스를 위해 또 그만큼 있었습니다.

sgcWebSockets 2026.10은 이 페이지를 전담하는 TsgcHTMLComponent_CRUD를 추가합니다. 데모는 이제 여섯 개의 메서드로 이루어진 어댑터로 고객 영역을 구성하며, 직접 작성한 버전도 그 옆에 남겨두어 두 가지를 비교해서 볼 수 있게 했습니다.

페이지

uses
  sgcHTML_Component_CRUD;

FCRUD := TsgcHTMLComponent_CRUD.Create(Self);
FCRUD.CRUDID := 'customers';
FCRUD.RoutePrefix := '/customers';
FCRUD.Title := 'Customers';
FCRUD.KeyField := 'ID';
FCRUD.PageSize := 15;
FCRUD.Actions := [caCreate, caEdit, caDelete, caView, caExport];
FCRUD.DataSet := FDQueryCustomers;

데이터셋에 연결되면 컴포넌트가 스스로 컬럼을 읽어들이며, 그것으로 페이지 전체가 완성됩니다. 목록, 검색, 페이징, 생성과 편집 폼, 삭제 확인, 그리고 목록에 표시되는 내용의 CSV 내보내기까지입니다.

아니면 여러분만의 SQL로

데이터셋은 레코드를 저장하는 한 가지 방법일 뿐, 유일한 방법은 아닙니다. 레코드를 변경하는 모든 작업은 어댑터를 거치므로, 컴포넌트는 SQL도, 레코드도, 데이터베이스 드라이버도 직접 보지 않습니다.

type
  TCustomersAdapter = class(TsgcHTMLCRUDAdapter)
  public
    function Locate(const aKey: string): Boolean; override;
    function Read(const aKey: string; aValues: TStrings): Boolean; override;
    function List(const aSearch: string; aPage, aPageSize: Integer;
      aRows: TStrings): Integer; override;
    function Insert(aValues: TStrings; var aKey: string;
      aErrors: TStrings): Boolean; override;
    function Update(const aKey: string; aValues, aErrors: TStrings)
      : Boolean; override;
    function Delete(const aKey: string; aErrors: TStrings): Boolean; override;
  end;

FCRUD.SetAdapter(TCustomersAdapter.Create(FCRUD));

이미 가지고 있는 것 위에 얹는 여섯 개의 메서드입니다. 데모에서는 각각이 애플리케이션의 데이터베이스 풀에 대한 단 한 번의 호출입니다.

다음으로 직접 작성했을 부분들

낙관적 잠금. 버전 컬럼의 이름을 지정하면, 저장 시 브라우저가 보낸 값과 저장되어 있는 값을 비교합니다. 그래서 두 번째로 저장 버튼을 누른 사람은 첫 번째 사람의 변경 내용을 조용히 덮어쓰는 대신, 레코드가 바뀌었다는 알림을 받습니다:

FCRUD.VersionField := 'UPDATED_AT';

마스터와 디테일. 레코드의 하위 목록은 또 다른 CRUD 컴포넌트이며, 레코드가 편집되는 동안 폼 아래에 렌더링됩니다:

with FCRUD.Details.Add do
begin
  CRUD := FCRUDOrderLines;
  MasterField := 'ORDER_ID';
  Caption := 'Lines';
end;

폼이 아니라 여러분의 데이터에 관한 검증. 필수 컬럼을 확인하는 것은 컴포넌트의 몫이고, 고객에 관한 규칙이 있는 곳은 OnValidate입니다. 문제마다 한 줄씩 추가하면 쓰기가 거부되고 그 내용과 함께 폼이 되돌아옵니다.

시작하는 두 가지 방법

IDE에는 이제 마법사가 있습니다. Tools › New sgcHTML CRUD page입니다. 테이블, 키, 컬럼의 이름을 지정하면 디자인 중인 폼 위에 컴포넌트가 생성되고, 이를 연결하는 두 줄의 코드가 클립보드에 담깁니다.

IDE 밖에서는 sgcHTMLGen이라는 명령줄 도구가 있어, 페이지의 소스를 Object Pascal 또는 C#으로 작성해 줍니다:

sgcHTMLGen crud --table Customers --prefix /customers ^
  --fields "ID:key CODE:req:32 NAME:req:120 CITY:80 NOTES:noform"

페이지가 응답하는 방식

페이지의 모든 컨트롤은 같은 방식으로 값을 전송합니다. 검색창, New 버튼, 행의 Edit와 Delete, 그리고 폼 자체까지 모두 action, crud, 그리고 의미가 있는 경우에는 key라는 세 개의 숨겨진 필드를 가지고 있습니다. 호스트는 이 값들을 읽어 ProcessAction을 호출하고, 이 함수가 교체해 넣을 마크업으로 응답합니다.

소켓으로 전송되는 메시지에는 경로가 없으므로, 라우터에 도달하지 않고 라우터 자체의 검사도 받지 않습니다. 이를 인가하는 것은 애플리케이션의 몫이며, 엔진은 이제 그에 필요한 것을 제공합니다:

oSession := FHTMXEngine.MessageSession(aConnection, FAuth);
try
  if not Assigned(oSession) then
    Exit;   // a write with nobody behind it is not a write
  oPage.ProcessAction(vAction, oParams.Values['key'], oParams, aResponse);
finally
  oSession.Free;
end;

브라우저에서 전달된 키는 어떤 쓰기가 실행되기 전에 Locate로 먼저 조회되므로, 누구의 것도 아닌 키는 아무것도 쓰지 않습니다.

두 버전을 모두 보기

Demos\60.HTML\01.RunTime\02.AdminCRUD에서, /customers는 컴포넌트이고 /customers-classic은 그것이 대체한 페이지입니다. 같은 데이터베이스, 같은 데이터, 같은 화면입니다. 둘 중 하나는 400줄이 더 짧습니다.

업그레이드

이 컴포넌트는 sgcHTML의 일부이며 추가적인 기능입니다. 폼에 하나를 올리기 전까지는 기존 페이지에서 바뀌는 것이 없습니다.

다음 읽을거리

영상으로 보기

이에 관한 짧은 영상eSeGeCe 채널에 있습니다.

질문, 피드백, 마이그레이션 도움이 필요하신가요? 문의하기 — 코드를 작성한 사람들에게서 직접 답변을 받으실 수 있습니다.