문제: 대부분의 최신 API는 Delphi SDK가 아닌 OpenAPI 사양을 제공해요
Delphi에서 최신 REST API를 통합해 본 적이 있다면 워크플로는 아마 다음과 같았을 거예요: 문서 읽기, TIdHTTP 또는 TNetHTTPClient 주변에 래퍼 손으로 작성, 요청 본문을 문자열 리터럴에 붙여넣기, TJSONObject로 응답 파싱, TDateTime 직렬화와 싸우기, 모든 엔드포인트에 대해 반복. 작동하지만 확장되지 않아요. API에는 80개의 엔드포인트가 있고, 8개를 래핑하고, 공급업체가 다음 달에 10개를 더 추가하면 래퍼가 썩어요.
OpenAPI(이전 Swagger)의 약속은 공급업체가 기계 판독 가능한 설명 — 하나의 YAML 또는 JSON 파일 — 을 게시하고 생성기가 이를 선택한 언어로 타입이 지정된 클라이언트로 변환한다는 것이에요. 대부분의 언어에서 이것은 매우 잘 작동해요: openapi-generator와 swagger-codegen은 Python, TypeScript, Go, Java, C#, Rust 및 기타 여러 언어에 대해 관용적인 클라이언트를 생성해요.
Delphi의 경우 이야기는 역사적으로 덜 매끄러웠어요. 이 게시물은 2026년 기준 두 가지 주요 옵션 — 오랫동안 존재한 오픈 소스 swagger-codegen Delphi 생성기와 eSeGeCe의 네이티브 상용 sgcOpenAPI — 을 비교하고 동일한 입력 사양에서 각각이 생성하는 것을 보여줘요.
swagger-codegen 및 openapi-generator에 대해
이제 SmartBear 산하의 Swagger 프로젝트는 swagger-codegen을 제공해요. 2018년 커뮤니티 포크 후 병렬 프로젝트인 openapi-generator가 많은 언어의 사실상 표준이 되었어요. 두 도구 모두 Mustache 템플릿을 통해 긴 대상 언어 목록을 지원해요. Delphi 생성기(swagger-codegen의 delphi와 커뮤니티 템플릿)가 있지만 항상 가끔씩 자원 봉사자가 유지 관리하는 2계층 대상이었어요.
작성 시점에 openapi-generator의 Delphi 템플릿은 이전 Delphi 버전에서 컴파일되는 코드를 생성하지만 최신 Delphi(D11/D12/D13)에서는 알려진 문제가 있어요: nullable 필드의 잘못된 처리, 최신 RTL에 대한 {$IFDEF} 가드 누락, OpenAPI 3.1 지원 없음, 스트리밍 응답 지원 없음, 특정 HTTP 클라이언트 라이브러리 버전에 의존하는 런타임. Delphi 생성기에 대한 여러 오래된 GitHub 이슈가 수년간 열려 있어요. 사양에 따라 결과가 달라질 거예요.
sgcOpenAPI에 대해
sgcOpenAPI는 eSeGeCe의 네이티브 Delphi 도구예요. OpenAPI 3.0(이제 3.1) 사양을 파싱하고 sgc 명명 규칙을 따르는 Delphi 유닛을 내보내요: 클라이언트의 경우 Tsgc[Api]Client, 각 스키마 객체의 경우 Tsgc[Api]_[Model], 그리고 작업당 하나의 메서드. 생성된 코드는 sgcWebSockets의 TsgcHTTPComponentClient를 전송으로 사용하므로 인증, 재시도, TLS 및 HTTP/2가 무료로 상속돼요.
생성기 자체가 Delphi로 작성되었기 때문에 Delphi 도구에 임베드하거나, 명령줄에서 실행하거나, 빌드 스크립트에서 호출할 수 있어요. 출력은 sgcWebSockets 외에는 런타임 종속성이 없는 일반 .pas 파일이에요 — JVM 없음, Node 없음, Python 없음, 템플릿 엔진 없음.
기능 비교
| 기능 | swagger-codegen / openapi-generator (Delphi 대상) | sgcOpenAPI |
|---|---|---|
| OpenAPI 2.0 (Swagger) | 예 | 예 |
| OpenAPI 3.0 | 예 (openapi-generator) | 예 |
| OpenAPI 3.1 | 작성 시점 제한적 | 예 |
| 런타임 요구 사항 | 생성기 실행을 위한 Java 11+ | 없음 (네이티브 Delphi exe) |
| 대상 Delphi 버전 | D10.x에서 최선의 노력, 작성 시점 D11+에서 종종 깨짐 | D7부터 D13까지 |
| HTTP 백엔드 | 현재 Delphi 템플릿에서 기본적으로 Indy | sgcHTTPComponentClient (Indy / ICS / SChannel, HTTP/2 포함) |
| 비동기 / 스트리밍 응답 | 제한적 | 예 |
| OAuth2 / API 키 / 베어러 인증 | 부분적 | 예, 네이티브 컴포넌트 |
다형성 (oneOf, allOf, discriminator) | 제한적 | 예 |
| Nullable 필드 | 일관성 없음 | 예 (TsgcNullable<T>) |
| 파일 업로드 / multipart | 부분적 | 예 |
| 서버 스텁 생성 | 예 (대부분 다른 언어) | 예 (sgcWebSocketHTTPServer가 있는 Delphi 서버 스텁) |
| 라이선스 | 오픈 소스 (Apache 2.0) | 상용 |
워크플로 비교
swagger-codegen 워크플로
- Java 11 이상 설치.
openapi-generator-cliJAR 다운로드.java -jar openapi-generator-cli.jar generate -i spec.yaml -g delphi -o ./out실행.- IDE에서 생성된
.pas파일 열기. Indy 런타임 경로 추가. Delphi 버전에 대한 컴파일 문제 수정. Nullable 처리 패치. 모든 사양 변경 후 다시 실행. - 생성된 클라이언트는 템플릿이 제공하는 별도의 런타임 지원 유닛에 의존해요. 그 유닛을 프로젝트와 함께 배포해요.
sgcOpenAPI 워크플로
- sgcOpenAPI IDE를 열거나 CLI를 호출:
sgcOpenAPI.exe -i spec.yaml -o ./out. - IDE에서 생성된 유닛 열기. sgcWebSockets를 사용한다면 이미 가지고 있는
TsgcHTTPComponentClient를 사용해요. - 생성된 클라이언트 컴포넌트를 폼에 드롭하고, 기본 URL과 자격 증명을 설정하고, 타입이 지정된 메서드를 호출하세요.
나란히 비교: 생성된 GET /users/{id} 메서드
동일한 OpenAPI 조각, 두 도구, 동일한 작업. 헬퍼의 이름과 정확한 형식은 명확성을 위해 약간 양식화되었지만 접근 방식의 차이는 실재해요.
swagger-codegen (Delphi 대상, 단순화)
function TUsersApi.GetUserById(const Id: Int64): TUser;
var
HttpRequest: TIdHTTP;
Path, Response: string;
JsonValue: TJSONValue;
begin
Path := StringReplace(BasePath + '/users/{id}', '{id}', IntToStr(Id), [rfReplaceAll]);
HttpRequest := TIdHTTP.Create(nil);
try
HttpRequest.Request.CustomHeaders.AddValue('Authorization', 'Bearer ' + FApiKey);
Response := HttpRequest.Get(Path);
finally
HttpRequest.Free;
end;
JsonValue := TJSONObject.ParseJSONValue(Response);
try
Result := TUser.Create;
Result.Id := (JsonValue as TJSONObject).GetValue<Int64>('id');
Result.Name := (JsonValue as TJSONObject).GetValue<string>('name');
// ... nullable fields handled inconsistently ...
finally
JsonValue.Free;
end;
end;
sgcOpenAPI
function TsgcUsersApiClient.GetUserById(const aId: Int64): TsgcUser;
var
vResponse: TsgcAPIResponse;
begin
Result := nil;
vResponse := DoGet(Format('/users/%d', [aId]));
Try
if vResponse.StatusCode = 200 then
Result := TsgcUser.FromJSON(vResponse.Content);
Finally
vResponse.Free;
End;
end;
// TsgcUser is generated with typed nullable fields:
// property Id: Int64 read FId write FId;
// property Name: string read FName write FName;
// property Email: TsgcNullable<string> read FEmail write FEmail;
// property CreatedAt: TDateTime read FCreatedAt write FCreatedAt;
//
// Authentication, retry, HTTP/2, TLS are configured on the
// underlying TsgcHTTPComponentClient once, not per-method.
주목할 만한 차이점:
- sgcOpenAPI는 전송(컴포넌트 수준 설정)을 작업(타입이 지정된 메서드)에서 분리하므로 인증, 재시도 및 TLS가 한 번 구성돼요.
- Nullable 필드는 OpenAPI 3.x 의미와 일치하는 “없음”, “null” 및 “값”을 구별하는 타입이 지정된 래퍼
TsgcNullable<T>를 사용해요. - 날짜/시간 필드는 나중에 파싱하는 문자열이 아니라 올바른 ISO 8601 파서가 있는 실제
TDateTime을 사용해요. - 생성된 클라이언트는 sgcWebSockets HTTP 컴포넌트를 사용하며, 서버가 ALPN을 통해 광고하면 HTTP/2를 투명하게 지원해요.
서버 스텁
두 도구 모두 서버 측 코드를 생성할 수 있지만 openapi-generator의 Delphi 서버 템플릿은 작성 시점 최소한이에요. sgcOpenAPI는 작업당 하나의 가상 메서드, 요청 검증, 응답 형성 및 내장 OpenAPI 탐색기 엔드포인트가 있는 TsgcWebSocketHTTPServer 기반 핸들러를 생성해요. 단일 Delphi 프로젝트가 API를 노출하고 Delphi 클라이언트가 소비하는 내부 서비스의 경우 왕복이 매우 짧아요.
swagger-codegen이 여전히 올바른 선택인 경우
- 이미 여러 언어에 걸쳐
openapi-generator로 표준화했으며 CI 파이프라인에 하나의 도구를 원해요. - 사양이 작고 안정적이며 OpenAPI 3.1 기능이 필요하지 않아요.
- 대상 버전에 맞게 생성된 Delphi를 패치하는 것에 익숙해요.
- 라이선스 이유로 상용 소프트웨어를 사용할 수 없어요.
sgcOpenAPI가 실제 시간을 절약하는 경우
- swagger-codegen Delphi 템플릿이 작성 시점 삐걱거리는 현재 Delphi 버전(D11 / D12 / D13)에 있어요.
- 사양이 OpenAPI 3.1,
oneOf/discriminator, nullable 필드, 파일 업로드 또는 비동기 스트리밍을 사용해요. - 이미 sgcWebSockets를 사용하며 일관된 컴포넌트 모델과 단일 HTTP 스택을 원해요.
- 생성된 클라이언트를 다시 작성하지 않고 HTTP/2, TLS, OAuth2 및 재시도가 “그냥 작동”해야 해요.
맺음말
OpenAPI에서의 코드 생성은 프로젝트의 수명 동안 누적되는 생산성 승리 중 하나예요 — 생성기가 사양과 Delphi 버전에 발맞춰 진행되는 경우. swagger-codegen / openapi-generator는 우수한 다중 언어 도구이지만 Delphi 대상은 역사적으로 최선의 노력으로 취급되었어요. sgcOpenAPI는 Java 종속성 없이 그리고 수동 패치 주기 없이 컴파일 가능하고 관용적이며 전체 기능 OpenAPI 3.0 / 3.1 클라이언트(및 서버 스텁)를 제공하는 집중적인 네이티브 Delphi 대안이에요. 이미 REST API를 통합하는 대부분의 Delphi 팀의 경우 공급업체가 사양을 업데이트하는 첫 번째 시점에 그 비용을 회수해요.