Temp

[Python] 구조화된 데이터를 효율적으로 저장하고 전달하기위한 Protocol Buffers

ju_young 2023. 7. 30. 12:04
728x90

Protocol Buffers?

Protocol Buffers 는 구조화된 데이터를 Serialization 하는 도구이다. XML, JSON 과 같은 텍스트 기반의 Serialization 방식을 생각할 수 있지만 Protocol Buffers는 더 작고 빠르다고 한다.

 

다른 방법과 한계

  • Python pickling: 스키마 변경에 대해서 잘 처리하지 못하고 응용 프로그램과의 데이터 공유가 잘 이루어지지 않는다.
  • 문자열 인코딩: 아주 간단한 데이터에 대해 적합하다.
  • XML: 메모리를 많이 잡아먹는 것으로 알려져있으며 응용 프로그램의 성능을 저하시킬 수 있다. 또한 XML DOM 트리가 복잡하다.

Protocol Format 정의

Protocol Buffers 의 기본 개념은 "message" 라고 하는 구조화된 데이터를 정의하는 것이다. message 안에는 각 필드에 대한 이름, 타입, 필드 번호 등을 작성한다.

syntax = "proto2";

package tutorial;

message Person {
  optional string name = 1;
  optional int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    PHONE_TYPE_UNSPECIFIED = 0;
    PHONE_TYPE_MOBILE = 1;
    PHONE_TYPE_HOME = 2;
    PHONE_TYPE_WORK = 3;
  }

  message PhoneNumber {
    optional string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}

위에서 보는 것처럼 문법은 C++ 이나 Java 처럼 생겼다.

 

  • package
    • 다른 프로젝트들과의 name conflict가 일어나지 않도록 해준다.
  • field types
    • bool, int32, float, double, string
    • 위처럼 message 내부에 message 타입, enum 타입을 정의할 수도 있다.
  • field number
    • 필드가 바이너리 형식으로 serialization 될 때 사용된다.
    • 자주 사용되는 요소는 1-15(더 높은 숫자보다 1byte 덜 사용), 덜 사용되는 요소는 더 높은 숫자를 사용한다고 한다. (마치 HTTP 2에 적용된 허프만 코딩 압축 알고리즘과 유사)
  • modifier
    • oprional: 필드가 지정될 수도 안될 수도 있다. 지정 안된다면 default 값을 사용한다. default가 지정이 안되어있으면 숫자 타입은 0, 문자 타입은 빈 문자, boolean은 false 를 사용한다.
    • repeated: 필드가 반복될 수 있다. 동적으로 할당되는 array 를 생각하면 된다.
    • required: 필드를 반드시 지정해야한다.

 

Compiling Protocol Buffers

protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/addressbook.proto
  • SRC_DIR: compile할 *.proto 가 있는 directory 
  • DST_DIR: compile한 *.py를 저장할 directory

위와 같은 경우는 DST_DIR 에 addressbook_pb2.py 가 저장된다.

 

Tutorial

1. Defined fields

import addressbook_pb2

person = addressbook_pb2.Person()
person.id = 1234
person.name = "John Doe"
person.email = "jdoe@example.com"
phone = person.phones.add()
phone.number = "555-4321"
phone.type = addressbook_pb2.Person.PHONE_TYPE_HOME

2. Serialization

# Serialization
serialized_person = person.SerializeToString()
print(serialized_person)

"""
b'\n\x08John Doe\x10\xd2\t\x1a\x10jdoe@example.com"\x0c\n\x08555-4321\x10\x02'
"""

3. Deserialization

# Deserialization
person_message = addressbook_pb2.Person()
person_message.ParseFromString(serialized_person)
print(person_message)

"""
name: "John Doe"
id: 1234
email: "jdoe@example.com"
phones {
  number: "555-4321"
  type: PHONE_TYPE_HOME
}
"""

 

[reference]

https://protobuf.dev/getting-started/pythontutorial/

728x90