sgcOpenAPI 2026.6 — 독립형 OpenAPI 서버, Spec-First 또는 Code-First

· 릴리스
sgcOpenAPI 2026.6 — 독립형 OpenAPI 서버, Spec-First 또는 Code-First | eSeGeCe 블로그

sgcOpenAPI의 차기 릴리스, 6월 출시 예정인 2026.6 버전은 sgcWebSockets를 설치하지 않고도 호스팅할 수 있는 OpenAPI 3.0 서버를 제공합니다. 이를 위한 두 컴포넌트가 있습니다: Indy 기반 HTTP 서버인 TsgcHTTPServer와, 여기에 연결하는 API 컴포넌트인 TsgcWSAPIServer_OpenAPI입니다. API 컴포넌트에 스펙을 지정하고(또는 RTTI 속성으로 Delphi 클래스에서 생성), 그 Server 속성을 대입한 뒤 HTTP 서버를 시작하면 자동으로 제공되는 Swagger UI가 포함된 문서화된 REST API를 갖게 됩니다.

핵심 변경 사항은 sgcOpenAPI가 더 이상 HTTP 서버를 호스팅하기 위해 sgcWebSockets를 필요로 하지 않는다는 것입니다. 두 컴포넌트 모두 전적으로 sgcOpenAPI에서 제공, 패키징 및 설치됩니다. 이미 sgcWebSockets를 사용하고 있다면, 익숙한 TsgcWSServer_API_OpenAPI 컴포넌트는 변경 없이 계속 작동합니다. 같은 클래스이기 때문입니다. 두 제품 모두 동일한 엔진으로 이를 빌드합니다.

얻을 수 있는 것

이 한 쌍은 세 가지를 제공합니다:

빠른 시작 — 최소 예제

Swagger UI가 포함된 작동하는 OpenAPI 서버를 호스팅하는 데 필요한 모든 것입니다. API 컴포넌트에는 자체 Active 속성이 없다는 점에 유의하세요. Server를 대입하면 연결되고, nil을 대입하면 HTTP 서버가 계속 실행되는 동안 분리됩니다.

uses
  sgcHTTP_Server, sgcWebSocket_Server_API_OpenAPI;

var
  oServer: TsgcHTTPServer;
  oOpenAPI: TsgcWSAPIServer_OpenAPI;
begin
  oServer := TsgcHTTPServer.Create(nil);
  oOpenAPI := TsgcWSAPIServer_OpenAPI.Create(nil);
  try
    oServer.Bindings.Add.Port := 8080;
    oOpenAPI.LoadFromFile('petstore.json');
    oOpenAPI.OnRequest := MyOnRequest;
    oOpenAPI.Server := oServer;
    oServer.Active := True;
    Readln;
  finally
    oOpenAPI.Free;
    oServer.Free;
  end;
end;

Swagger UI는 http://localhost:8080/docs로, 스펙은 http://localhost:8080/openapi.json로 접속하세요. 스펙에 정의된 모든 작업은 해결된 operationId와 완전히 구성된 요청 컨텍스트와 함께 MyOnRequest 핸들러로 라우팅됩니다.

Spec-First — 기존 OpenAPI 3.0 파일 로드

이미 OpenAPI 3.0 JSON 파일(Petstore, 내부 API 계약, 모킹하려는 공용 스키마)이 있다면 spec-first가 이를 제공하는 가장 빠른 방법입니다. LoadFromFile은 스펙을 읽고 파싱하며, paths 섹션에서 라우트 테이블을 구성하고, 들어오는 모든 요청을 이에 대해 매칭합니다. 서버는 JSON만 읽으므로, YAML 계약이라면 먼저 예를 들어 sgcOpenAPI.exe로 변환하세요.

각 라우트의 operationId는 디스패치 키입니다. OnRequest 내에서 각 작업을 차례로 처리합니다:

uses
  sgcHTTP_Server, sgcHTTP_OpenAPI_Server,
  sgcHTTP_OpenAPI_Server_Engine, sgcWebSocket_Server_API_OpenAPI;

procedure TForm1.OnOpenAPIRequest(Sender: TObject;
  const aOperationId: string; const aContext: TsgcOpenAPIServerContext;
  var Handled: Boolean);
begin
  Handled := True;
  if aOperationId = 'listPets' then
    HandleListPets(aContext)
  else if aOperationId = 'getPetById' then
    HandleGetPetById(aContext)
  else if aOperationId = 'createPet' then
    HandleCreatePet(aContext)
  else
    Handled := False;
end;

procedure TForm1.HandleGetPetById(const aContext: TsgcOpenAPIServerContext);
var
  vId, vPetJSON: string;
begin
  vId := aContext.PathParamAsString('petId');
  vPetJSON := FPets.Values[vId];
  if vPetJSON <> '' then
    aContext.RespondJSON(200, vPetJSON)
  else
    aContext.RespondError(404, 'Not Found', 'Pet ' + vId + ' not found');
end;

TsgcOpenAPIServerContext는 요청의 모든 것에 대한 타입 지정된 접근자를 제공합니다: 템플릿화된 세그먼트를 위한 PathParamAsString / PathParamAsInteger, 기본값이 있는 QueryParamAsString / QueryParamAsInteger / QueryParamAsBoolean, 요청 본문을 위한 BodyAsString / BodyAsJSON, 그리고 들어오는 헤더를 위한 HeaderValue. 응답하려면 헬퍼인 RespondJSON(code, content)RespondError(code, title, detail)를 사용하거나, 완전한 제어를 위해 Response.Code, Response.ContentType, Response.Content를 직접 설정하세요.

Code-First — Delphi 클래스에서 스펙 생성

API 계약을 Delphi로 작성하고 스펙을 생성하도록 하려면, RTTI 속성으로 클래스를 데코레이트하세요. TsgcOpenAPICodeFirstScanner가 클래스를 순회하여 완전한 OpenAPI 3.0 JSON 문서를 구성하며, LoadFromString으로 이를 서버에 로드합니다. 이 기능은 Delphi XE7 이상이 필요합니다(확장 RTTI용).

uses
  sgcHTTP_OpenAPI_Server_CodeFirst;

type
  [sgcServiceContract('Task Manager API',
    'A simple task management demo', '1.0.0')]
  [sgcRoute('/api/v1')]
  TTaskManagerService = class
  public
    [sgcHttpGet]
    [sgcRoute('/tasks')]
    [sgcSummary('List all tasks')]
    [sgcTag('Tasks')]
    [sgcResponse(200, 'A list of tasks')]
    procedure ListTasks([sgcFromQuery] const status: string); virtual;

    [sgcHttpPost]
    [sgcRoute('/tasks')]
    [sgcSummary('Create a new task')]
    [sgcTag('Tasks')]
    [sgcResponse(201, 'Task created successfully')]
    procedure CreateTask([sgcFromBody] const body: string); virtual;

    [sgcHttpGet]
    [sgcRoute('/tasks/{taskId}')]
    [sgcSummary('Get a task by ID')]
    [sgcTag('Tasks')]
    [sgcResponse(200, 'The requested task')]
    [sgcResponse(404, 'Task not found')]
    procedure GetTask([sgcFromPath][sgcRequired]
      const taskId: Integer); virtual;
  end;

메서드 본문은 스텁입니다 — 컴파일러가 이들에 대해 RTTI를 방출하도록 하기 위해서만 존재합니다. 실제 작업은 OnRequest에서 일어나며, 스캐너가 각 메서드 이름(ListTasks, CreateTask, GetTask…)에서 파생한 operationId에 의해 디스패치됩니다.

시작 시 클래스를 스캐너에 전달하고 생성된 스펙을 서버에 로드합니다:

uses
  sgcHTTP_Server, sgcHTTP_OpenAPI_Server_CodeFirst,
  sgcWebSocket_Server_API_OpenAPI;

var
  oScanner: TsgcOpenAPICodeFirstScanner;
  oServer: TsgcHTTPServer;
  oOpenAPI: TsgcWSAPIServer_OpenAPI;
  vSpec: string;
begin
  oScanner := TsgcOpenAPICodeFirstScanner.Create;
  try
    vSpec := oScanner.GenerateSpec(TTaskManagerService);
  finally
    oScanner.Free;
  end;

  oServer := TsgcHTTPServer.Create(nil);
  oServer.Bindings.Add.Port := 8081;

  oOpenAPI := TsgcWSAPIServer_OpenAPI.Create(nil);
  oOpenAPI.LoadFromString(vSpec);
  oOpenAPI.OnRequest := MyOnRequest;
  oOpenAPI.Server := oServer;

  oServer.Active := True;
end;

속성은 일반적인 메타데이터를 다룹니다: sgcServiceContract는 OpenAPI info 블록을 채우고, sgcRoute는 클래스 또는 메서드 수준에서 경로를 설정하며, sgcHttpGet / Post / Put / Delete / Patch / Head / Options는 동사를 선택하고, sgcSummarysgcDescription은 작업을 문서화하며, sgcTag는 Swagger UI에서 그룹화하고, sgcResponse(code, description)는 각 응답을 선언하며, sgcFromPath / FromQuery / FromBody / FromHeadersgcRequired와 함께 각 매개변수를 설명합니다.

구성 — OpenAPIOptions

모든 서버측 구성은 API 컴포넌트의 OpenAPIOptions 아래에 있으며, 다섯 가지 하위 옵션으로 그룹화됩니다. 이 중 세 가지가 일상적인 설정을 담당합니다:

oServer.OpenAPIOptions.Endpoint.BasePath        := '/api';
oServer.OpenAPIOptions.Endpoint.ServeSpec       := True;   // /openapi.json
oServer.OpenAPIOptions.Endpoint.ServeSwaggerUI  := True;   // /docs

oServer.OpenAPIOptions.CORS.Enabled             := True;
oServer.OpenAPIOptions.CORS.AllowOrigins        := '*';
oServer.OpenAPIOptions.CORS.AllowHeaders        := 'Content-Type, Authorization';
oServer.OpenAPIOptions.CORS.AllowMethods        := 'GET, POST, PUT, DELETE, PATCH, OPTIONS';

oServer.OpenAPIOptions.Validation.ValidateRequest     := True;
oServer.OpenAPIOptions.Validation.ValidateRequestBody := True;
oServer.OpenAPIOptions.Validation.ValidateQueryParams := True;
oServer.OpenAPIOptions.Validation.ValidatePathParams  := True;
oServer.OpenAPIOptions.Validation.ValidateRequired    := True;

검증이 활성화되면, 들어오는 모든 요청은 핸들러에 도달하기 전에 스펙에 선언된 JSON Schema에 대해 확인됩니다 — 필수 필드, 타입, 형식, 열거형, 범위. 실패하면 오류 목록과 요청을 수락하거나 거부하는 플래그와 함께 OnValidationError 이벤트가 발생합니다.

이벤트

여섯 가지 이벤트가 요청 수명 주기를 다룹니다:

OnBeforeRequest: 디스패치 전에 발생합니다. Accept := False를 설정하면 403 Forbidden으로 거부합니다. 속도 제한, 로깅 또는 라우트별 게이트에 유용합니다.

OnAuthenticate: 메인 핸들러 전에 발생합니다. Authenticated := False를 설정하면 401 Unauthorized로 거부합니다. 헤더, 쿠키 또는 쿼리 매개변수를 검사하여 결정합니다.

OnValidationError: 검증이 실패하면 발생하며 오류 목록을 받습니다. Continue := False를 설정하면 400 Bad Request로 거부합니다.

OnRequest: 메인 디스패치 이벤트. aOperationId를 보고, aContext.Response에 응답을 작성하고, Handled := True를 설정합니다.

OnAfterRequest: 핸들러가 반환된 후 발생합니다 — 메트릭이나 감사 로깅에 이상적입니다.

OnException: 처리되지 않은 예외가 핸들러에서 발생하면 호출됩니다. 500 Internal Server Error 외의 다른 것을 원하면 aResponseCode를 조정하세요.

나머지 두 하위 옵션도 각자의 역할이 있습니다: Security는 스펙이 선언한 securitySchemes를 위한 OnValidateAPIKey, OnValidateBasic, OnValidateBearer를 구동하고, Mock은 스펙 자체의 예제로부터 핸들러가 없는 작업에 응답합니다.

데모

sgcOpenAPI 2026.6에는 두 개의 완전한 데모가 함께 제공되며, 둘 다 독립형 한 쌍으로 호스팅되므로 sgcWebSockets 설치가 필요하지 않습니다:

업그레이드

현재 sgcWebSockets와 함께 TsgcWSServer_API_OpenAPI를 사용하고 있다면 아무것도 변경되지 않습니다. 클래스, 속성, 이벤트가 모두 보존되며, 구현은 공유 엔진에 위임됩니다. TsgcWSAPIServer_OpenAPI는 바로 그 클래스의 공개된 자손이므로, sgcOpenAPI가 바꾸는 것은 패키지가 어디서 오는지뿐입니다.

sgcOpenAPI 2026.6은 6월에 다운로드 페이지에서 제공될 예정입니다.

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