파트너가 gRPC 서비스를 넘겨줄 때 함께 건네는 것은 .proto 파일이 들어 있는 폴더입니다. 그 파일들이 곧 계약이며, 그 안의 모든 메시지는 여러분의 Delphi 코드가 읽고 쓸 수 있는 무언가가 되어야 합니다. 이 작업을 손으로 하는 것은 지루하고 실수하기 쉬우며, 상대편이 계약의 새 버전을 배포할 때마다 처음부터 다시 해야 합니다.
sgcProtoBuf는 그 일을 대신 해 주는 코드 생성기입니다. .proto 파일을 지정하면 모든 메시지에 대한 클래스를 담은 Delphi 유닛을 생성하며, 바로 직렬화하고 역직렬화할 수 있습니다. proto2와 proto3를 모두 읽고, 구문은 파일 자체에서 가져옵니다. 마법사와 명령줄 도구 두 가지 형태로 제공됩니다.
파일 하나만 주면 트리 전체를 얻습니다
실제 proto 트리는 결코 파일 하나가 아닙니다. 서로를 import하고, 그 위에 Google 정의까지 import합니다. 전형적인 서비스 파일은 다음과 같이 시작합니다:
syntax = "proto3";
package acme.telemetry.v1;
import "acme/common/v1/device.proto";
import "google/protobuf/timestamp.proto";
message Reading {
string device_id = 1;
double value = 2;
google.protobuf.Timestamp taken_at = 3;
}
import 해석을 활성화하면 그 파일 하나만 선택해도 sgcProtoBuf가 import 문을 재귀적으로 따라가며 발견한 모든 의존성을 생성하고, 생성된 유닛을 uses 절에 추가합니다. proto 트리의 루트 폴더는 선택한 파일에서 자동으로 찾아내므로, 대개 설정할 것이 없습니다.
디스크에서 정의를 찾을 수 없으면 컴파일되지 않는 유닛을 조용히 만들어 내는 대신 메시지 목록에 보고합니다. 컴파일러 오류를 쫓아다니는 대신 어떤 파일이 없는지 정확히 알 수 있습니다.
유닛 이름은 package에서 옵니다
생성된 유닛의 이름은 package 문을 따르므로, package acme.telemetry.v1;은 unit acme.telemetry.v1;을 만들어 acme.telemetry.v1.pas로 저장합니다. 파일 이름은 항상 유닛 이름과 일치하므로, 피할 수 있는 컴파일 오류 부류 전체가 사라집니다.
또한 같은 package를 선언하는 여러 .proto 파일이 계약 작성자의 의도 그대로 하나의 유닛에 모인다는 뜻이기도 합니다. acme.common.v1을 선언하는 device.proto, device_service.proto, units.proto가 들어 있는 폴더는 하나의 acme.common.v1.pas가 됩니다.
Google 타입은 이미 들어 있습니다
실제 proto 파일 거의 전부가 google.protobuf에서 무언가를 import합니다. 그것들은 생성할 필요가 없습니다. sgcWebSockets가 이미 sgcProtoBuf_WellKnownTypes 유닛으로 제공하며, 생성기가 자동으로 매핑합니다: Timestamp, Duration, Any, Empty, FieldMask, Struct, 그리고 StringValue와 Int32Value 같은 래퍼 타입까지 포함합니다.
따라서 위의 google.protobuf.Timestamp 필드는 ToDateTime과 FromDateTime이 이미 구현된 TsgcProtoBufTimestamp가 됩니다. Google .proto 파일은 전혀 변환할 필요가 없습니다.
진짜 Delphi 타입으로서의 열거형
기본적으로 열거형은 상수 블록으로 생성됩니다. 열거형을 Delphi 열거 타입으로 생성을 켜면 제대로 된 타입을 얻게 되는데, 읽기에 더 짧고 관련 없는 정수와 혼동될 수 없습니다:
CountryCodeDto = (
COUNTRY_CODE_DTO_NONE = 0,
COUNTRY_CODE_DTO_NL = 1,
COUNTRY_CODE_DTO_DE = 2,
COUNTRY_CODE_DTO_CH = 3
);
알아 둘 만한 솔직한 제약이 하나 있습니다. Protocol Buffers 열거형은 Delphi 열거 타입이 할 수 없는 일을 할 수 있습니다. 음수 값을 가질 수 있고, 두 이름을 같은 숫자에 별칭으로 붙일 수 있으며, 같은 package 안의 두 열거형이 같은 값 이름을 선언할 수 있습니다. 생성기가 그런 경우를 만나면 해당 열거형 하나에 대해서만 상수 형태로 되돌아가고 그 이유를 설명하는 주석을 씁니다. 파일 안의 다른 모든 열거형은 영향을 받지 않으므로, 가능한 곳에서는 진짜 타입을 얻고 나머지 모든 곳에서는 동작하는 유닛을 얻습니다.
생성된 코드
모든 메시지 클래스는 생성된 기반 클래스를 상속하며 LoadFromBytes, LoadFromStream, ToBytes를 노출하고 각 필드마다 프로퍼티를 갖습니다. 아래 예제는 원래 proto 요소 이름 유지 옵션을 사용하는데, 이 옵션은 Tsgc 접두사를 없애고 계약에 나타난 그대로 이름을 유지합니다:
uses
acme.telemetry.v1;
var
oReading: Reading;
begin
oReading := Reading.Create;
Try
oReading.LoadFromBytes(vBytes);
ShowMessage(oReading.DeviceId);
ShowMessage(FloatToStr(oReading.Value));
ShowMessage(DateTimeToStr(oReading.TakenAt.ToDateTime));
vBytes := oReading.ToBytes;
Finally
oReading.Free;
End;
end;
명령줄에서
마법사는 처음 살펴볼 때 편리하지만, proto 트리는 변하고 이를 다시 생성하는 일은 빌드에 속합니다. 같은 생성기를 명령줄에서 실행할 수 있습니다:
sgcProtoBuf.exe --input=protos\acme\telemetry\v1\telemetry.proto --resolve-imports --output-dir=units --enum-as-type
스위치는 마법사 옵션과 동일합니다: --resolve-imports, --import-root(반복 가능하며, 루트가 둘 이상인 트리에 사용), --output-dir, --enum-as-type, --original-names, --short-enum-consts, 그리고 --no-classes, --no-services, --no-docs가 있습니다. 전체 목록은 --help로 실행해 확인하세요.
사용 방법
sgcProtoBuf는 TsgcGRPCClient 및 sgcProtoBuf 런타임 유닛과 함께 sgcWebSockets에 포함되어 제공됩니다. 최신 빌드는 sgcWebSockets 다운로드 페이지에서 받을 수 있으며, 전체 옵션 레퍼런스는 도움말의 sgcProtoBuf 항목을 참조하세요.
질문이나 의견이 있거나, 깔끔하게 생성되지 않는 proto 트리가 있으신가요? 문의해 주세요, 코드를 직접 작성한 사람들이 답변해 드립니다.
