본문 바로가기

Tech/gRPC

[gRPC] Proto file (.proto) - Message, Service

반응형

1. 프로토 파일 (Proto file)

프로토콜 버퍼를 사용하기 위해서는 사용할 데이터와 서비스에 대해서 프로토 파일에 작성해야한다. 프로토 파일에 작성된 프로토콜 버퍼는 아래의 과정을 거쳐서 다른 프로그래밍 언어에서 사용할 수 있도록 변환된다.

 

https://protobuf.dev/overview/

 

우리가 사용할 데이터 구조를 .proto 파일에 작성하면 프로토콜 버퍼 컴파일러인 protoc 가 이를 컴파일하여 해당 프로그래밍 언어의 소스코드를 생성한다. 이렇게 생성된 소스코드를 사용자의 프로젝트 코드와 함께 컴파일하여 프로토콜 버퍼 클래스를 프로그램에서 호출하여 사용할 수 있도록 한다.

 

아래에는 프로토콜 버퍼 데이터를 정의하기 위해 프로토 파일 문법에 대해서 정리한다. 버전은 proto3 를 기준으로 정리하였다.

2. Message Type

syntax = "proto3";

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

 

첫번째 줄의 syntax 는 어떤 버전의 문법을 사용하는지를 지정하고 있다. 이 예제는 proto3 버전을 사용하기 때문에 syntax 에 proto3 값을 매핑해주었다. 만약 값을 지정해주지 않으면 프로토콜 버퍼 컴파일러는 proto2 버전으로 간주한다.

 

Person 메시지는 포함해야할 데이터를 의미하는 name, id, email 총 3개의 필드로 구성되어 있다. 각각의 필드는 이름과 값의 쌍으로 이루어져 있으며 필드의 타입이 선언되어 있어야 한다.

- Field number

각 필드는 값으로 번호를 할당받아야 한다. 번호는 1 부터 536,870,911 사이의 값으로 할당해야 하며 다음의 제약을 따라야 한다.

 

- 각 필드의 번호는 메시지 안에서 고유한 번호여야 한다.
- 19,000 에서 19,999 까지의 번호는 프로토콜 버퍼 구현을 위해서 예약되어 있다.
   해당 번호를 사용하면 프로토콜 버퍼 컴파일러에서 오류가 발생한다.
- 미리 예약된 필드 번호나 익스텐션 등에서 할당된 번호는 사용할 수 없다.

 

필드 번호는 메시지 인코딩 이후 바이너리 데이터에서 필드를 식별하는데 사용한다. 그렇기 때문에 위 제약처럼 중복해서 사용하거나 예약된 번호를 재사용하지 않아야 한다.

 

필드 번호 1 ~ 15 의 값들은 1byte, 16 ~ 2047 까지는 2byte 의 공간을 사용한다. 이때문에 자주 호출되는 필드에 대해서는 1 ~ 15 와 같이 크기가 작은 번호로 매기는 것을 추천한다. 이와 관련해서는 프로토콜 버퍼 인코딩 문서에 자세히 설명되어 있다. (https://protobuf.dev/programming-guides/encoding/#structure)

- Reserved Field

메시지 타입을 수정하면서 삭제되거나 더이상 사용하지 않는 필드는 삭제할 수 있다. 이떄 삭제된 필드의 번호를 reserved 처리할 수 있다. reserved 처리는 해당 필드 번호를 예약된 번호로 설정하여 다른 필드에서 사용할 수 없도록 하는 것이다.

 

앞서 필드 번호 항목에서 본 것과 같이 필드 번호는 메시지 내부에서 고유한 값이어야 한다. 그런데 메시지가 업데이트 되는 과정에서 이전에 사용했던 필드 번호가 재사용되는 경우 이전의 와이어 포맷과 맞지않아서 오류가 발생할 수 있다. 기존에 사용되었던 필드 번호를 reserved 처리하여 후에 재사용할 수 없도록 하여 문제를 막을 수 있다.

 

message Foo {
    reserved 2, 15, 9 to 11; // 필드 번호를 reserved 처리 (9 to 11 = 9, 10, 11)
    reserved "foo", "bar"; // 필드 이름을 reserved 처리
}

 

위의 예제와 같이 필드 번호 또는 필드 이름을 reserved 처리할 수 있다. 이때 필드 번호와 이름은 각각 따로 선언하여야 한다.

- Field label

메시지의 필드는 다음의 라벨들 중 하나를 따른다. 각 라벨들은 데이터 객체에서 해당 필드의 값의 유무 및 개수에 대한 제한을 정의한다.

 

- optional: 해당 필드를 가지지 않거나 1개만 가진다.
- repeated: 이 필드를 0개 이상 가진다. 값의 순서는 유지된다.
- map: 키-값 쌍으로 구성된 필드.
- default: 명시적으로 라벨이 적용되지 않았을 때 암묵적으로 default 로 지정되며 명시적으로 default 필드 라벨을 선언할 수는 없다. 해당 필드가 없거나 1개만 가질 수 있다.

3. Service Type

정의된 메시지 타입을 RPC 에서 사용하기 위해서는 RPC 서비스 인터페이스를 정의해야한다. 프로토콜 버퍼 컴파일러는 선언된 프로그래밍 언어에 맞는 서비스 인터페이스 코드와 stub 을 생성한다.

 

gRPC 에서는 4가지 서비스 메서드를 정의할 수 있다. 아래는 각각 4개의 종류의 메서드를 정의한 예제이다.

 

service DemoService{
    // Unary RPC - 단일 요청/단일 응답
    rpc UnaryDemo(DemoRequest) returns (DemoResponse);

    // Server streaming RPC - 단일 요청 / 다중 응답
    rpc ServerStreamDemo(DemoRequest) returns (stream DemoResponse);

    // Client streaming RPC - 다중 요청 / 단일 응답
    rpc ClientStreamDemo(stream DemoRequest) returns (DemoResponse);

    // Bidirectional streaming RPC - 다중 요청 / 다중 응답
    rpc BidirectionalStreamDemo(stream DemoRequest) returns (stream DemoResponse);
}

 

위의 예제는 매개변수 DemoRequest 받고 DemoResponse 를 반환하는 RPC 메서드들을 가진 DemoService 인터페이스를 정의한 것이다. DemoService 가 가지고 있는 메서드들은 각각 요청과 응답을 단일 또는 stream 형식으로 가지는 경우들을 가정하여 예시로 정의하였다. 각 메서드들의 실제 동작에 대해서는 추후 자바로 예제를 만들어서 확인해볼 예정이다.

[Reference]

- https://grpc.io/docs/what-is-grpc/core-concepts/

- https://protobuf.dev/overview/

- https://blog.naver.com/n_cloudplatform/221751268831

- https://blog.naver.com/n_cloudplatform/221751405158

반응형

'Tech > gRPC' 카테고리의 다른 글

[gRPC] gRPC - Java  (0) 2024.05.23
[gRPC] gRPC 와 Protocol Buffer  (0) 2024.05.09