gRPC를 Python에서 사용하는 방법을 알아보겠습니다.

gRPC Python Client

Proto File을 통해 생성된 Python 코드를 사용하여 gRPC 서버에 요청을 보내는 클라이언트를 작성해보겠습니다.

1. gRPC Python Package 설치

gRPC Python Package를 설치합니다.

pip install grpcio
pip install grpcio-tools

grpcio는 gRPC Python 라이브러리입니다.
grpcio-tools는 .proto 파일을 Python 코드로 변환하는데 사용됩니다.

2. .proto 파일 작성

gRPC 서버와 클라이언트가 통신할 때 사용할 .proto 파일을 작성합니다.

현업에서 쓰일만한 예제를 작성해보겠습니다. partsclass <–> parts <–> sales 구조로 이루어진 예제입니다.

** partclass.proto 파일 작성

syntax = "proto3";

option csharp_namespace = "SalesDB.partclassProtos";

package partclassSvc;

import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/wrappers.proto";
import "google/protobuf/empty.proto";

service partclass {
   rpc GetByKey (GetByKeyRequest) returns (partclassResponse);
   rpc GetByKeyJson (GetByKeyRequest) returns (partclassResponse);
   rpc GetAll (google.protobuf.Empty) returns (partclassSetResponse);
   rpc GetAllJson (google.protobuf.Empty) returns (partclassSetResponse);
   rpc Insert (InsertRequest) returns (partclassResponse);
   rpc InsertJson (InsertRequest) returns (partclassResponse);
   rpc Update (UpdateRequest) returns (UpdateResponse);
   rpc Delete (DeleteRequest) returns (DeleteResponse);
}


message GrpcDecimal {
    int64 units = 1;
    sfixed32 nanos = 2;
}

message partclassResponse {
   bytes partclass = 1;
   string jsonString = 2;
}
message partclassSetResponse {
   bytes partclassSet = 1;
   string jsonString = 2;
}
message DataSetResponse {
   bytes dataSet = 1;
   string jsonString = 2;
}
message GetByKeyRequest {
   sint32 partclasscode = 1;
}
message InsertRequest {
   string partclassname = 1;
}
message UpdateRequest {
   sint32 partclasscode = 1;
   string partclassname = 2;
}

message UpdateResponse {
   int32 count = 1;
}

message DeleteRequest {
   sint32 partclasscode = 1;
}

message DeleteResponse {
   int32 count = 1;
}

** parts.proto 파일 작성

syntax = "proto3";

option csharp_namespace = "SalesDB.partsProtos";

package partsSvc;

import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/wrappers.proto";
import "google/protobuf/empty.proto";

service parts {
   rpc GetForpartclass (GetForpartclassRequest) returns (partsSetResponse);
   rpc GetForpartclassJson (GetForpartclassRequest) returns (partsSetResponse);
   rpc GetByKey (GetByKeyRequest) returns (partsResponse);
   rpc GetByKeyJson (GetByKeyRequest) returns (partsResponse);
   rpc GetAll (google.protobuf.Empty) returns (partsSetResponse);
   rpc GetAllJson (google.protobuf.Empty) returns (partsSetResponse);
   rpc Insert (InsertRequest) returns (partsResponse);
   rpc InsertJson (InsertRequest) returns (partsResponse);
   rpc Update (UpdateRequest) returns (UpdateResponse);
   rpc Delete (DeleteRequest) returns (DeleteResponse);
}


message GrpcDecimal {
    int64 units = 1;
    sfixed32 nanos = 2;
}

message partsResponse {
   bytes parts = 1;
   string jsonString = 2;
}
message partsSetResponse {
   bytes partsSet = 1;
   string jsonString = 2;
}
message DataSetResponse {
   bytes dataSet = 1;
   string jsonString = 2;
}
message GetForpartclassRequest {
  sint32  partclasscode = 1;
}
message GetByKeyRequest {
   string partcode = 1;
}
message InsertRequest {
   string partcode = 1;
   sint32 partclasscode = 2;
   string partname = 3;
   google.protobuf.StringValue spec = 4;
   GrpcDecimal price = 5;
   oneof oneofManufacturedate { google.protobuf.Timestamp manufacturedate = 6; }
   google.protobuf.StringValue remark = 7;
}
message UpdateRequest {
   string partcode = 1;
   sint32 partclasscode = 2;
   string partname = 3;
   google.protobuf.StringValue spec = 4;
   GrpcDecimal price = 5;
   oneof oneofManufacturedate { google.protobuf.Timestamp manufacturedate = 6; }
   google.protobuf.StringValue remark = 7;
}

message UpdateResponse {
   int32 count = 1;
}

message DeleteRequest {
   string partcode = 1;
}

message DeleteResponse {
   int32 count = 1;
}

** sales.proto 파일 작성

syntax = "proto3";

option csharp_namespace = "SalesDB.salesProtos";

package salesSvc;

import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/wrappers.proto";
import "google/protobuf/empty.proto";

service sales {
   rpc GetForparts (GetForpartsRequest) returns (salesSetResponse);
   rpc GetForpartsJson (GetForpartsRequest) returns (salesSetResponse);
   rpc GetByKey (GetByKeyRequest) returns (salesResponse);
   rpc GetByKeyJson (GetByKeyRequest) returns (salesResponse);
   rpc GetAll (google.protobuf.Empty) returns (salesSetResponse);
   rpc GetAllJson (google.protobuf.Empty) returns (salesSetResponse);
   rpc Insert (InsertRequest) returns (salesResponse);
   rpc InsertJson (InsertRequest) returns (salesResponse);
   rpc Update (UpdateRequest) returns (UpdateResponse);
   rpc Delete (DeleteRequest) returns (DeleteResponse);
}


message GrpcDecimal {
    int64 units = 1;
    sfixed32 nanos = 2;
}

message salesResponse {
   bytes sales = 1;
   string jsonString = 2;
}
message salesSetResponse {
   bytes salesSet = 1;
   string jsonString = 2;
}
message DataSetResponse {
   bytes dataSet = 1;
   string jsonString = 2;
}
message GetForpartsRequest {
  string  partcode = 1;
}
message GetByKeyRequest {
   google.protobuf.Timestamp saledate = 1;
}
message InsertRequest {
   string partcode = 1;
   GrpcDecimal quantity = 2;
   google.protobuf.StringValue remark = 3;
   oneof oneofCreatedate { google.protobuf.Timestamp createdate = 4; }
}
message UpdateRequest {
   google.protobuf.Timestamp saledate = 1;
   string partcode = 2;
   GrpcDecimal quantity = 3;
   google.protobuf.StringValue remark = 4;
   oneof oneofCreatedate { google.protobuf.Timestamp createdate = 5; }
}

message UpdateResponse {
   int32 count = 1;
}

message DeleteRequest {
   google.protobuf.Timestamp saledate = 1;
}

message DeleteResponse {
   int32 count = 1;
}

3개의 proto 파일을 작성하였습니다.
이제 .proto 파일을 Python 코드로 변환하겠습니다.
** 주의: .proto 파일이 있는 디렉토리로 이동한 후 명령어를 실행해야 합니다.

3. .proto 파일을 Python 코드로 변환

.proto 파일을 Python 코드로 변환합니다.

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. partclass.proto parts.proto sales.proto

-I: .proto 파일이 있는 디렉토리를 지정합니다.
–python_out: Python 코드를 저장할 디렉토리를 지정합니다.
–grpc_python_out: gRPC Python 코드를 저장할 위치를 지정합니다.
proto 파일이 여러 개일 경우, 각 파일을 모두 지정해주어야 합니다.
혹은 디렉토리를 지정하여 모든 proto 파일을 변환할 수 있습니다.

** 주의: .proto 파일이 있는 디렉토리로 이동한 후 명령어를 실행해야 합니다.
** 주의2: proto Compiler가 설치되어 있어야 합니다.

초보자들을 위한 절차를 설명

1. python 설치

python을 설치합니다.

python.org에서 python을 다운로드 받아 설치합니다.

  • 필수 설치 항목: Add Python to PATH 체크
  • python 설치 확인
    python --version
    
  • python 설치 위치 확인
    where python
    // 기본 위치: C:\Users\사용자이름\AppData\Local\Programs\Python\Python버전\python.exe
    

//microsoft store에서 python 설치한 경우의 위치

where python
// 기본 위치: C:\Users\사용자이름\AppData\Local\Microsoft\WindowsApps\python.exe

2. 환경 생성

python 가상환경을 생성합니다.

python -m venv grpcmodule

가상환경을 활성화합니다.

grpcmodule\Scripts\activate

3. grpcio, grpcio-tools 설치

활성화된 가상환경에서 필요한 패키지를 설치합니다.

pip install grpcio
(grpcmodule) {사용자 path}\Scripts>pip install grpcio
Collecting grpcio
  Downloading grpcio-1.62.2-cp312-cp312-win_amd64.whl.metadata (4.2 kB)
Downloading grpcio-1.62.2-cp312-cp312-win_amd64.whl (3.8 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.8/3.8 MB 9.2 MB/s eta 0:00:00
Installing collected packages: grpcio
Successfully installed grpcio-1.62.2
pip install grpcio-tools
(grpcmodule) {사용자 path}\Scripts>pip install grpcio-tools
Collecting grpcio-tools
  Downloading grpcio_tools-1.62.2-cp312-cp312-win_amd64.whl.metadata (6.4 kB)
Collecting protobuf<5.0dev,>=4.21.6 (from grpcio-tools)
  Downloading protobuf-4.25.3-cp310-abi3-win_amd64.whl.metadata (541 bytes)
Requirement already satisfied: grpcio>=1.62.2 in {사용자 path}\lib\site-packages (from grpcio-tools) (1.62.2)
Collecting setuptools (from grpcio-tools)
  Downloading setuptools-69.5.1-py3-none-any.whl.metadata (6.2 kB)
Downloading grpcio_tools-1.62.2-cp312-cp312-win_amd64.whl (1.1 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.1/1.1 MB 6.1 MB/s eta 0:00:00
Downloading protobuf-4.25.3-cp310-abi3-win_amd64.whl (413 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 413.4/413.4 kB 8.6 MB/s eta 0:00:00
Downloading setuptools-69.5.1-py3-none-any.whl (894 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 894.6/894.6 kB 11.3 MB/s eta 0:00:00
Installing collected packages: setuptools, protobuf, grpcio-tools
Successfully installed grpcio-tools-1.62.2 protobuf-4.25.3 setuptools-69.5.1

4. proto 파일 컴파일

.proto 파일을 Python 코드로 변환합니다.

// 먼저 .proto 파일이 있는 디렉토리로 이동합니다.
cd {proto 파일이 있는 디렉토리}

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. partclass.proto parts.proto sales.proto

5. Compile된 파일 확인

.proto 파일을 Python 코드로 변환하면, 해당 디렉토리에 _pb2.py 파일과 _pb2_grpc.py 파일이 생성됩니다.

dir

// 결과
partclass_pb2.py
partclass_pb2_grpc.py
parts_pb2.py
parts_pb2_grpc.py
sales_pb2.py
sales_pb2_grpc.py

4. gRPC Client 작성

gRPC 클라이언트를 작성합니다. 예제로는 partclass.proto 파일을 사용하여 작성하겠습니다.

import grpc
import grpc
from google.protobuf.empty_pb2 import Empty  # 확인: 정상적으로 Empty 임포트

# Generated classes import
import partclass_pb2
import partclass_pb2_grpc

# Server address configuration
http_server_address = 'loclahost:5100'


def run():
    channel = grpc.insecure_channel(http_server_address)
    stub = partclass_pb2_grpc.partclassStub(channel)
    response = stub.GetAllJson(Empty())  # 수정: Empty() 형태로 객체 생성
    print(response)
    
if __name__ == '__main__':
    run()

결과를 확인해보겠습니다.
DataBase에 저장된 데이터를 가져오는 부분이므로, 데이터베이스에 데이터가 없다면 빈 리스트가 출력됩니다.

(grpcmodule) python -m app
jsonString: "[{\"partclasscode\":3,\"partclassname\":\"class3\"},{\"partclasscode\":11,\"partclassname\":\"\\uCE5C\\uAD6C\"},{\"partclasscode\":12,\"partclassname\":\"Apple\"},{\"partclasscode\":14,\"partclassname\":\"Samsung\"},{\"partclasscode\":15,\"partclassname\":\"TV\"},{\"partclasscode\":17,\"partclassname\":\"Table\"},{\"partclasscode\":18,\"partclassname\":\"Table\"},{\"partclasscode\":19,\"partclassname\":\"Table\"},{\"partclasscode\":1,\"partclassname\":\"Table\"}]"

마치며

Note: 만들고나니 내것이 아니었다. ```

Leave a comment