Google Protocol Buffers 소개

Binary Protocol인 Google Protocol Buffers에 대해 알아보자

1. Google Protocol Buffers 소개

Protocol Buffers는 구글에서 만든 것으로 언어 중립적, 플랫폼 중립적이면서 통신 프로토콜, 데이터 저장 또는 다른 곳에서 사용을 위한 구조화된 데이터 직렬화의 확장성 있는 방법입니다.
(Protocol buffers are a language-neutral, platform-neutral, extensible way of serializing structured data for use in communications protocols, data storage, and more.)

Protocol Buffers는 바이너리 프로토콜로 XML이나 JSON 처럼 사람이 직접 보고 분석할 수 없는 메모리 기반의 자료 구조를 말한다. 이런 바이너리 프로토콜은 데이터를 전달할 때 특정 포맷으로 변환할 필요가 없고, 데이터 수신시 파싱이 필요 없기 때문에 속도가 텍스트 프로토콜(XML, JSON 또는 HTTP 등)에 비해 상당히 빠르다는 장점이 있다.

Protocol Buffers을 사용하기 위해서는 Protocol Buffer Language을 사용하여 필드들을 정의하여 데이터를 구조화 후 컴파일을 통해 개발에 필요한 언어 코드을 자동 생성한다. 여기서 '구조화'라는 의미는 각 필드의 데이터들을 크기 또는 데이터 타입으로 정의하는 것을 의미하며, XML 스키마 파일을 만드는 것과 유사하다.

Protocol Buffer Language 컴파일러는 모두 C++로 구현되어 있기 때문에 GitHub에서 다운로드 받아 빌드할 수 있으며, 이미 빌드된 바이너리 파일을 다운로드 받아 사용할 수 있다.(https://github.com/google/protobuf/releases)

2. Protocol Buffer Language

Google의 Protocol Buffers 소스는 GitHub에 공개되어 있다. 현재 C++, Java, Python, Objective-C, C#, JavaNano, JavaScript, Ruby, Go, PHP을 지원한다.

데이터를 구조화 하기 위해서는 Protocol Buffer Language을 사용한다. 현재 Protocol Buffer Language는 proto2와 proto3 두 버전이 공개 되어 있다. 자세한 내용은 아래 링크를 참고.

2.1 Proto2와 Proto3의 기본적인 차이점 

다음은 Proto2로 정의된 구조화 데이터 선언이다.
message SearchRequest {
  required string query = 1;
  optional int32 page_number = 2;
  optional int32 result_per_page = 3;
}

아래 예저는 Proto3로 정의된 구조화 데이터 선언이다.
syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}

두 버전에서 주요 차이점은 적색으로 표시한 것으로, Proto3에서는 required와 optional은 사용할 수 없으며, 현재 Protocol Buffer Language 버전을 명시해야 한다.

그리고 아래 'query' 필드처럼 필드 이름 뒤에 ' = 숫자'는 부여된 고유 번호로 해당 영역( '{'와 '}'로 정의) 에서는 고유하게 유지되어야 한다.
string query = 1;

다시 말하면 아래와 같이 각 영역에서(색상으로 표시) 고유하게 부여되어야 한다.
message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
  enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
    LOCAL = 3;
    NEWS = 4;
    PRODUCTS = 5;
    VIDEO = 6;
  }
  Corpus corpus = 4;
}

2.2 Proto3 버전으로 데이터 구조화 및 언어별 코드 생성하기

SearchRequest.proto 파일 이름으로 아래와 같이 파일을 작성한다.
SearchRequest.proto
syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}

그리고 Protocol Buffer Language 컴파일러을 사용하여 코드를 자동으로 생성한다. 이 예제는 macOS에서 테스트 하였으며, Protocol Buffer Language 컴파일러 버전은 3.1.0을 사용한다. 그리고 우리가 사용할 옵션들을 빨강색으로 표시했다.
$ ../bin/protoc --version
libprotoc 3.1.0

$ ../bin/protoc --help
Usage: ../bin/protoc [OPTION] PROTO_FILES
Parse PROTO_FILES and generate output based on the options given:
  -IPATH, --proto_path=PATH   Specify the directory in which to search for
                              imports.  May be specified multiple times;
                              directories will be searched in order.  If not
                              given, the current working directory is used.
  --version                   Show version info and exit.
  -h, --help                  Show this text and exit.
  --encode=MESSAGE_TYPE       Read a text-format message of the given type
                              from standard input and write it in binary
                              to standard output.  The message type must
                              be defined in PROTO_FILES or their imports.
  --decode=MESSAGE_TYPE       Read a binary message of the given type from
                              standard input and write it in text format
                              to standard output.  The message type must
                              be defined in PROTO_FILES or their imports.
  --decode_raw                Read an arbitrary protocol message from
                              standard input and write the raw tag/value
                              pairs in text format to standard output.  No
                              PROTO_FILES should be given when using this
                              flag.
  -oFILE,                     Writes a FileDescriptorSet (a protocol buffer,
    --descriptor_set_out=FILE defined in descriptor.proto) containing all of
                              the input files to FILE.
  --include_imports           When using --descriptor_set_out, also include
                              all dependencies of the input files in the
                              set, so that the set is self-contained.
  --include_source_info       When using --descriptor_set_out, do not strip
                              SourceCodeInfo from the FileDescriptorProto.
                              This results in vastly larger descriptors that
                              include information about the original
                              location of each decl in the source file as
                              well as surrounding comments.
  --dependency_out=FILE       Write a dependency output file in the format
                              expected by make. This writes the transitive
                              set of input file paths to FILE
  --error_format=FORMAT       Set the format in which to print errors.
                              FORMAT may be 'gcc' (the default) or 'msvs'
                              (Microsoft Visual Studio format).
  --print_free_field_numbers  Print the free field numbers of the messages
                              defined in the given proto files. Groups share
                              the same field number space with the parent 
                              message. Extension ranges are counted as 
                              occupied fields numbers.

  --plugin=EXECUTABLE         Specifies a plugin executable to use.
                              Normally, protoc searches the PATH for
                              plugins, but you may specify additional
                              executables not in the path using this flag.
                              Additionally, EXECUTABLE may be of the form
                              NAME=PATH, in which case the given plugin name
                              is mapped to the given executable even if
                              the executable's own name differs.
  --cpp_out=OUT_DIR           Generate C++ header and source.
  --csharp_out=OUT_DIR        Generate C# source file.
  --java_out=OUT_DIR          Generate Java source file.
  --javanano_out=OUT_DIR      Generate Java Nano source file.
  --js_out=OUT_DIR            Generate JavaScript source.
  --objc_out=OUT_DIR          Generate Objective C header and source.
  --php_out=OUT_DIR           Generate PHP source file.
  --python_out=OUT_DIR        Generate Python source file.
  --ruby_out=OUT_DIR          Generate Ruby source file.

아래와 같이 자동 생성 파일의 옵션을 C++, Java, JavaScript, Python으로 한번에 정의할 수도 있고,
$ ../bin/protoc --cpp_out=./output/cpp/ --java_out=./output/java/ --js_out=./output/js/ --python_out=./output/python/ SearchRequest.proto

특정 언어만 설정할 수 있다.
$ ../bin/protoc --js_out=./output/js/ SearchRequest.proto
Javascript와 C++로 자동 생성된 코드는 각각 다음과 같다.
[Javascript]


[Python]


C++ 코드는 너무 길고 복잡해 보여 아래 링크만 적어 둔다.

3. Reference

2. Source Code, GitHub: https://github.com/google/protobuf




댓글

이 블로그의 인기 게시물

C++로 프로그래밍할 때 인자 또는 리턴 값으로 std::vector 등 STL 데이터 타입 처리하는 좋은 방법

macOS가 갑자기 부팅이 되지 않을 경우 데이터 복구 또는 백업 방법

Git 저장소를 병합하는 방법(How to merge repositories in Git)