yoongrammer

Protocol Buffer 란? 본문

분산컴퓨팅

Protocol Buffer 란?

yoongrammer 2020. 11. 9. 11:26
728x90

목차

    Protocol Buffer 란?


    구글에서 오픈소스로 공개한 언어, 구조화(structured)된 데이터를 직렬화(serialization) 하는 방식입니다. 줄여서 protobuf, 더 줄여서 pb라고 부릅니다.

    protobuf는 여러 프로그램 언어를 지원합니다.

     

    Protocol Buffer 원리

    Person 이라는 객체를 JSON으로 표현하면 아래와 같습니다.

    {
        "userName": "Martin",
        "favouriteNumber": 1337,
        "interests": ["daydreaming", "hacking"]
    }

    데이터의 크기는 공백을 제거하면 82 바이트가 사용됩니다.

     

    Person 객체에 대한 프로토콜 버퍼 스키마는 다음과 같습니다.

    message Person {
        required string user_name        = 1;
        optional int64  favourite_number = 2;
        repeated string interests        = 3;
    }

    이 스키마를 사용하여 위의 데이터를 인코딩하면 33바이트로 표현이 가능합니다.

    출처: https://martin.kleppmann.com/2012/12/05/schema-evolution-in-avro-protocol-buffers-thrift.html

    위 그림은 프로토콜 버퍼가 어떻게 데이터를 직렬화하는지에 대한 내용입니다.

    user_name 같은 속성 값을 field tag로 대체하여 데이터를 줄이는 게 핵심입니다.

    속성 값과 TYPE을 조합하여 1바이트 메타정보로 표현할 수 있습니다.

     

    protobuf는 다른 IDL(XML, JSON..)에 비해 더 적은 용량으로 데이터를 저장할 수 있기 때문에 압축률이 좋고 처리 속도가 빠릅니다.

    하지만, 바이너리 데이터로 표현하기 때문에 사람이 확인하기 어렵다는 단점이 있습니다.

     

    728x90

     

    Protocol Buffer 사용 방법

    출처: https://www.researchgate.net/figure/To-use-Protocol-Buffers-it-is-necessary-to-generate-code-for-each-message-that-needs_fig17_285578991

    protocol buffer 사용 흐름을 보면 아래와 같습니다.

    1. .proto 파일에서 메시지 형식을 작성합니다.
    2. protoc 컴파일러를 통해 원하는 언어로 컴파일 합니다.
    3. 프로토콜 버퍼를 사용하는 전체 목적은 데이터를 다른 곳에서 구문 분석 할 수 있도록 직렬화하는 것입니다. 
      직렬화를 위해 인코딩(=마샬링)합니다.
    4. 직렬화된 데이터를 사용하기 위해 디코딩(=언마샬링)합니다.

    간단하게 Go 언어로 사용해 보도록 하겠습니다.

    다른 언어에 대한 내용은 아래 링크를 참고하시기 바랍니다.

    https://developers.google.com/protocol-buffers/docs/gotutorial#why-use-protocol-buffers

    1. .proto 파일에서 메시지 형식 작성

    person.proto 파일을 만들고 아래 코드를 적습니다.

    syntax = "proto3";
    package person;
    
    option go_package = "./personpb";
    
    message Person {
      string name = 1;
      int32 id = 2;
    }

    2. protoc 컴파일러 설치

    아래 사이트에서 자신의 OS에 맞는 protoc 컴파일러를 다운받습니다.

    https://github.com/google/protobuf/releases

     

    여기선 macOS에서 brew를 사용하여 설치하겠습니다.

    $ brew install protobuf

    설치가 완료되면 버전 정보가 출력됩니다.

    $ protoc --version
    libprotoc 3.13.0

    3. protoc-gen-go 설치

    go에서는 .proto 파일을 protoc로 컴파일하기 위해 별도 plugin을 추가 설치해야 합니다.

    $ go install google.golang.org/protobuf/cmd/protoc-gen-go
    cannot find package "google.golang.org/protobuf/cmd/protoc-gen-go" in any of ...

    위 에러가 출력된다면 아래 명령어를 사용하면 됩니다.

    $ go get -u google.golang.org/protobuf/cmd/protoc-gen-go

    4. protoc 로 컴파일

    $ protoc -I=. --go_out=. ./person.proto

    컴파일 후, ./personpb/person.pb.go 파일이 생성된 것을 확인할 수 있습니다.

    $ tree .
    .
    ├── person.proto
    └── personpb
        └── person.pb.go

    5. protocol buffer 사용

    편의상 모듈 초기화를 해줍니다.

    $ go mod init protocol_buffer

     

    main.go 파일을 생성하고 아래 코드를 작성합니다.

    아래 코드는 writeMessage 함수에서 마샬링을 통해 데이터를 직렬화하고 readMessage 함수에서 언마샬링을 통해 데이터 사용하는 예제입니다.

    package main
    
    import (
      "io/ioutil"
      "log"
      "github.com/golang/protobuf/proto"
      pb "protocol_buffer/personpb"
    )
    
    const (
      filename = "person"
    )
    
    func main() {
      writeMessage()
      readMessage()
    }
    
    func writeMessage() {
      person := &pb.Person{Name: "yoongrammer", Id: 1}
      // 데이터 인코딩
      out, err := proto.Marshal(person)
      if err != nil {
        log.Fatalf("Failed to encode Person: %v", err)
      }
    
      if err := ioutil.WriteFile(filename, out, 0644); err != nil {
        log.Fatalf("Failed to write Person: %v", err)
      }
    }
    
    func readMessage() {
      in, err := ioutil.ReadFile(filename)
      if err != nil {
        log.Fatalln("Error reading file:", err)
      }
    
      person := &pb.Person{}
      // 데이터 디코딩
      if err := proto.Unmarshal(in, person); err != nil {
        log.Fatalln("Failed to parse Persion:", err)
      }
    
      log.Printf("name: %s", person.GetName())
      log.Printf("ID: %d", person.GetId())
    }

    Output:

    $ go run main.go
    2020/11/08 23:58:12 name: yoongrammer
    2020/11/08 23:58:12 ID: 1
    728x90

    '분산컴퓨팅' 카테고리의 다른 글

    Raft Consensus Algorithm 알아보기  (0) 2021.01.20
    JSON 알아보기  (0) 2020.12.28
    gRPC 란?  (0) 2020.11.11
    RPC (Remote Procedure Calls) 란?  (0) 2020.11.10
    Comments