본문 바로가기

Python

Protobuf 를 쉽게 사용해보자 (2/2)

앞에서 protobuf 환경을 설치했다. 

간략히 말하면 protobuf 개발 환경은 library와 compiler로 구성된다. 

protobuf library는 pip install protobuf 로 설치했고, 이때 library 버전을 확인하였다. 

protobuf compiler는 아래 공식 사이트에서 다운 받는데, library 버전과 같은 버전을 다운 받았다. 

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

 

Releases · protocolbuffers/protobuf

Protocol Buffers - Google's data interchange format - protocolbuffers/protobuf

github.com

 

오늘은 실제로 활용을 해보자. 

 

protobuf는 구조화된 데이터를 쉽게 송수신하기 위해 사용한다고 했다. 

그러면 1) 구조화된 데이터(스키마)가 필요하고, 2) 송수신 방법을 알면 protobuf를 사용한다고 볼 수 있다. 

1번의 구조화된 데이터를 보통 스키마(schema) 파일이라고 하고, .proto 확장자를 가진다. 

 

흠.. 일단 쳇지피티에게 간단한 예제를 보여달라고 하자. 

쳇지피티야 간단한 예제를 보여줘. 근데 1번은 이미 했어.

 

쳇지피티도 먼저 라이브러리부터 설치하라고 한다. 우리는 이미 했다!!

.proto 파일을 만들라고 하는군.

.proto 파일을 만들라고 한다. 똑같이 따라하면 되는데.. 

 

간단히 내부를 보자.

 

Person 이라는 메시지가 있고, 내부에 name, age, hobbies 필드를 가지는 것 같다.

name 은 string, age는 int32, hobbies는 repeated string 인데,

사실 repeated는 반복 가능하다는 뜻이므로 hobbies는 string 타입이면서 repeated 가 가능하다는 뜻이다. 

쳇지피티가 생성한 comment 에도 리스트(배열) 이라고 해놨다. 기특한 AI 로세.. 

.proto 파일을 컴파일 하라는데... 필요시 protobuf compiler를 다운로드 하란다. 우리는 이미 했다.

 

앞서 만들었던 .proto 파일을 실제로 컴파일 해보자. 

아까 protobuf compiler를 다운로드 하고 압축을 풀어준 곳으로 가자. 

 

protobuf compiler가 있는 곳으로 .proto 파일을 복사해줬다.

 

 

그곳에 방금 생성한 .proto 파일을 복사한 후, 3번의 컴파일 명령을 실행해보자. 

>> protoc --python_out=. person.proto

컴파일 명령을 실행했다. 아무런 메시지가 없는데?? 무소식이 희소식이다.

 

무소식이 희소식이다. 아무런 메시지가 안나왔지만, 해당 폴더를 보면 없던 파일이 하나 생긴 것을 확인할 수 있다. 

없던 파일이 하나 생겼다.

해당 파일이 protobuf compiler에서 만들어준 자동 생성된 코드이다. 

참고로 해당 파일을 열어보면 아래와 같이 내용이 있다. 

protoc 에서 자동생성한 코드. 복잡하니 자세히 보기는 귀찮다.

 

이제 자동생성된 코드도 얻었으니 사용해보자. 

쳇지피티에서 예제도 만들어줬다. 굿굿

import person_pb2  # 자동 생성된 Python 파일 import

# 1️⃣ Person 객체 생성
person = person_pb2.Person()
person.name = "Alice"
person.age = 25
person.hobbies.extend(["reading", "coding"])

# 2️⃣ Protobuf 직렬화 (Binary 변환)
serialized_data = person.SerializeToString()
print(f"Serialized Data: {serialized_data}")

# 3️⃣ Protobuf 역직렬화 (Binary → Python 객체 변환)
new_person = person_pb2.Person()
new_person.ParseFromString(serialized_data)

# 4️⃣ 결과 출력
print(f"Name: {new_person.name}")
print(f"Age: {new_person.age}")
print(f"Hobbies: {new_person.hobbies}")

 

실제 실행해보니 아래와 같이 나온다

실행해보니 잘 된다.

 

코드를 좀 더 자세히 보자. 

person 객체를 만들고, 해당 객체에 이름, 나이, 취미리스트를 넣어주었다. 

그리고 person.SerializeToString() 을 통해서 serialized_data를 만들었다. 

여기까지가 데이터를 넣고, 직렬화까지 한 내용이다. 

 

이제 다시 person 객체를 하나 만들고, 아까 직렬화한 데이터로부터 새로 만든 객체를 채우려고 한다. 

이를 위해서 ParseFromString()를 호출해서 새로 만든 객체를 이전의 데이터(alice)로 채웠다. 

그리고 출력을 해서 확인하였다. 

 

사실 이정도면 아주 간단한 예제인데, 실제적인 데이터 송수신이 아니라 약간 감이 안오기는 한다. 

한발만 더 나아가서 UDP Socket을 이용해서 데이터를 전송하고 받아서 처리하는 걸로 변경해보자. 

살짝 귀찮으니 쳇지피티에게 얘기해보자. 

쳇지피티야, 예제 다시 만들어줘.

 

ex_person_sender.py

import socket
import person_pb2  # 프로토콜 버퍼 메시지 import

# UDP 서버 설정
UDP_IP = "127.0.0.1"
UDP_PORT = 5005

# UDP 소켓 생성
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# Protobuf 객체 생성 및 데이터 설정
person = person_pb2.Person()
person.name = "Alice"
person.age = 25
person.hobbies.extend(["reading", "gaming"])

# Protobuf 메시지 직렬화
serialized_data = person.SerializeToString()

# 데이터 전송
sock.sendto(serialized_data, (UDP_IP, UDP_PORT))
print(f"Sent: {serialized_data}")

# 소켓 닫기
sock.close()

 

ex_person_receiver.py

import socket
import person_pb2  # 프로토콜 버퍼 메시지 import

# UDP 서버 설정
UDP_IP = "127.0.0.1"
UDP_PORT = 5005

# UDP 소켓 생성 및 바인딩
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((UDP_IP, UDP_PORT))

print(f"Listening on {UDP_IP}:{UDP_PORT}...")

while True:
    # 데이터 수신
    data, addr = sock.recvfrom(1024)  # 최대 1024바이트 수신
    print(f"Received data from {addr}")

    # Protobuf 역직렬화
    received_person = person_pb2.Person()
    received_person.ParseFromString(data)

    # 결과 출력
    print(f"Name: {received_person.name}")
    print(f"Age: {received_person.age}")
    print(f"Hobbies: {received_person.hobbies}")

 

서로 다른 두 개의 터미널을 띄워서 receiver를 먼저 실행한 후, sender를 실행해보자. 

receiver를 먼저 실행한후 sender를 실행하자.

 

sender에서는 데이터를 serialize 한 후 전송하고,

그 전송된 데이터를 receiver가 받아서 역직렬화 한 후 그 내용을 출력한 것을 볼 수 있다. 

 

잘 되는 것을 볼 수 있다. 

이로서 간단히 protobuf 를 사용해보았다. 

 

오늘은 여기까지