CS

protobufjs

Realuda72 2025. 1. 8. 22:20

 Protocol Buffers가 구글에서 만든 언어 중립적, 플랫폼 중립적 직렬화 도구라는 것을 알았다.

 하지만 잘 살펴보면 protocol buffers가 지원하는 언어중에 JavaScript는 없다는 것을 알 수 있다.

 Protocol Buffers의 공식 문서를 보면 구글이 직접 지원하지 않는 언어라도 서드파티 프로젝트로 지원되는 언어가 있을 수 있다고 한다.

 protobufjs는 JavaScript에서 Protocol Buffers를 사용할 수 있도록 지원하는 라이브러리이다.

 오늘은 protobufjs를 알아보자.

https://www.npmjs.com/package/protobufjs

 

protobufjs

Protocol Buffers for JavaScript (& TypeScript).. Latest version: 7.4.0, last published: 5 months ago. Start using protobufjs in your project by running `npm i protobufjs`. There are 4056 other projects in the npm registry using protobufjs.

www.npmjs.com

 

 

설치하기

Node.js를 쓰고 있다면 아래와 같은 명령어로 protobufjs를 설치할 수 있다.

// npm으로 protobufjs를 설치하는 방법
npm install protobufjs --save

 

 JavaScript는 동적으로 자료형을 정하는 언어이기 때문에 protobuf.js는 valid message라는 컨셉으로 최대한의 성능을 이끌어낸다.

 

Valid message(유효한 메세지)

A valid message is an object (1) not missing any required fields and (2) exclusively composed of JS types understood by the wire format writer.

 Valid message는 (1) 필요한 필드가 모두 있고 (2) 와이어 포맷 라이터가 이해할 수 있는 JS 자료형으로만 구성된 객체이다.

 위의 조건을 갖추지 못한 메세지는 유효하지 않다.

 

 Valid message에는 두가지 종류가 있다.

  • Message instances(프로토타입의 기본값을 가지고 있는 메시지 클래스의 명시적 인스턴스)는 자체적으로 valid message의 요건을 갖춘다.
  • 일반적인 JavaScript objects도 마찬가지로 valid message의 요건을 갖추는 형태이다.

와이어 포맷 라이터는 이런 타입들을 이해한다.

Field type Expected JS type (create, encode) Conversion (from Object)
s-/u-/int32
s-/fixed32
number (32 bit integer) value | 0 if signed
value >>> 0 if unsigned
s-/u-/int64
s-/fixed64
Long-like (optimal)
number (53 bit integer)
Long.fromValue(value) with long.js
parseInt(value, 10) otherwise
float
double
number Number(value)
bool boolean Boolean(value)
string string String(value)
bytes Uint8Array (optimal)
Buffer (optimal under node)
Array.<number> (8 bit integers)
base64.decode(value) if a string
Object with non-zero .length is assumed to be buffer-like
enum number (32 bit integer) Looks up the numeric id if a string
message Valid message Message.fromObject(value)
repeated T Array<T> Copy
map<K, V> Object<K,V> Copy
  •  명시적인 undefined와 null은 필드가 optional이면 설정되지 않은 것으로 간주한다.

Toolset

 성능상의 이유로 각 메시지 클래스는 각 메서드가 단 하나의 작업만 수행하는 독특한 메서드 세트를 제공합니다. 이를 통해 성능이 중요한 불필요한 주장이나 중복 작업을 피할 수 있으며, 사용자가 필요한 경우 유효한 메시지일 수 있는 일반 JavaScript 객체에 대한 검증을 명시적으로 수행하도록 강제할 수도 있습니다.

 

 Message에서 쓸 수 있는 메서드들을 알아보자.

  • Message.verify(message: Object): null|string

 JavaScript Object가 유효한 메세지를 만족하는지 검사한다. 에러를 핸들링하는 대신 에러 메세지를 반환한다.

var payload = "invalid (not an object)";
var err = AwesomeMessage.verify(payload);
if (err)
  throw Error(err);
  • Message.encode(message: Message|Object [, writer: Writer]): Writer

 메세지 인스턴스나 유효한 JavaScript Object를 인코딩한다. 메세지가 유효한지 검사하지 않는다.

var buffer = AwesomeMessage.encode(message).finish();

 

  • Message.decode(reader: Reader|Uint8Array): Message

 버퍼를 메세지 인스턴스로 디코딩한다. 만약 필요한 필드가 없으면 되는데까지 디코딩한 인스턴스를 가지는 util.ProtocolError를 던진다. 만약 와이어 포맷 자체가 잘못됐다면 Error를 던진다.

try {
  var decodedMessage = AwesomeMessage.decode(buffer);
} catch (e) {
    if (e instanceof protobuf.util.ProtocolError) {
      // e.instance holds the so far decoded message with missing required fields
    } else {
      // wire format is invalid
    }
}
  • Message.decodeDelimited(reader: Reader|Uint8Array): Message

Message.decode와 비슷하지만 varint로 추가된 길이의 메세지를 추가로 읽는다.

  • Message.create(properties: Object): Message

 유효한 메세지 조건을 만족하는 속성 객체로부터 새로운 메세지 인스턴스를 생성한다. 중복 변환을 수행하지 않으므로 Message.fromObject보다 Message.create를 사용하는 것이 더 좋다.

var message = AwesomeMessage.create({ awesomeField: "AwesomeString" });
  • Message.fromObject(object: Object): Message

 유효하지 않은 일반 JavaScript object를 변환 단계를 통해 메세지 인스턴스로 변환한다.

var message = AwesomeMessage.fromObject({ awesomeField: 42 });
// converts awesomeField to a string
  • Message.toObject(message: Message [, options: ConversionOptions]): Object

 메세지 인스턴스를 다른 라이브러리 또는 저장소와의 상호 운용성을 위해 임의의 일반 JavaScript object로 변환한다. 결과적으로 생성된 일반 JavaScript object는 변환 옵션에 따라 유효한 메세지 조건을 갖출 수 있지만, 대부분은 그렇지 못하다.

var object = AwesomeMessage.toObject(message, {
  enums: String,  // enums as string names
  longs: String,  // longs as strings (requires long.js)
  bytes: String,  // bytes as base64 encoded strings
  defaults: true, // includes default values
  arrays: true,   // populates empty arrays (repeated fields) even if defaults=false
  objects: true,  // populates empty objects (map fields) even if defaults=false
  oneofs: true    // includes virtual oneof fields set to the present field's name
});

 

 참고로, 아래의 그림은 각 메서드와 유효한 메세지 개념 사이의 관계를 보여준다.

예시

 라이브러리를 사용해서 .proto파일을 로드하고 바로 사용할 수 있는 메세지 클래스로 파싱하고 컴파일할 수 있다.

// awesome.proto
package awesomepackage;
syntax = "proto3";

message AwesomeMessage {
    string awesome_field = 1; // becomes awesomeField
}
protobuf.load("awesome.proto", function(err, root) {
    if (err)
        throw err;

    // Obtain a message type
    var AwesomeMessage = root.lookupType("awesomepackage.AwesomeMessage");

    // Exemplary payload
    var payload = { awesomeField: "AwesomeString" };

    // Verify the payload if necessary (i.e. when possibly incomplete or invalid)
    var errMsg = AwesomeMessage.verify(payload);
    if (errMsg)
        throw Error(errMsg);

    // Create a new message
    var message = AwesomeMessage.create(payload); // or use .fromObject if conversion is necessary

    // Encode a message to an Uint8Array (browser) or Buffer (node)
    var buffer = AwesomeMessage.encode(message).finish();
    // ... do something with buffer

    // Decode an Uint8Array (browser) or Buffer (node) to a message
    var message = AwesomeMessage.decode(buffer);
    // ... do something with message

    // If the application uses length-delimited buffers, there is also encodeDelimited and decodeDelimited.

    // Maybe convert the message back to a plain object
    var object = AwesomeMessage.toObject(message, {
        longs: String,
        enums: String,
        bytes: String,
        // see ConversionOptions
    });
});

 

 

'CS' 카테고리의 다른 글

로드 밸런싱  (0) 2025.01.14
대칭키 암호화와 공개키 암호화  (0) 2025.01.14
OSI 7계층 어플리케이션 계층  (0) 2024.12.30
TCP와 UDP  (0) 2024.12.27
OSI 4계층: 전송 계층  (0) 2024.12.24