📌 Protocol Buffers란?
Protocol Buffers(ProtoBuf)란?
Protocol Buffers(ProtoBuf)는 Google에서 개발한 데이터 직렬화(Serialization) 포맷으로, 효율적인 데이터 저장 및 통신을 위해 사용되는 경량 직렬화(Serialization) 기술이다.
기존의 JSON, XML과 같은 텍스트 기반 데이터 포맷보다 크기가 작고, 빠르며, 네트워크 대역폭을 절약할 수 있도록 설계되었다.
ProtoBuf는 바이너리(Binary) 포맷을 사용하여 데이터를 압축하고, 다양한 프로그래밍 언어 및 플랫폼에서 쉽게 사용할 수 있다.
✅ ProtoBuf의 핵심 특징
- 텍스트 기반(JSON, XML)보다 빠르고 크기가 작음 ➡️ 바이너리 직렬화로 네트워크 전송 최적화
- 언어 및 플랫폼 독립적 (Java, Python, Go, C++ 등 지원) ➡️ Java, Python, Go, C++ 등 다양한 언어에서 지원
- 바이너리 포맷을 사용하여 네트워크 트래픽 절감 및 빠른 처리 가능 ➡️ 네트워크 트래픽 절감 및 높은 데이터 처리 속도 제공
- 자동 코드 생성 지원 (IDL을 기반으로 언어별 코드 생성) ➡️ .proto 파일을 통해 다양한 언어로 코드 자동 생성
💡 ProtoBuf가 필요한 이유?
기존 JSON을 사용할 경우, 데이터 크기가 커지고, 텍스트 파싱 비용이 높아 성능이 저하된다.
ProtoBuf는 더 작은 크기와 빠른 파싱 성능을 제공하여 네트워크 환경이 제한적인 곳에서도 효율적으로 사용할 수 있다.
IDL(Interface Definition Language)란?
IDL(인터페이스 정의 언어)은 서로 다른 소프트웨어 시스템이 정보를 어떻게 교환할지 정의하는 언어이다.
ProtoBuf는 .proto 파일을 사용하여 IDL을 정의하며, 데이터의 구조 및 타입을 명확하게 기술할 수 있다.
IDL의 주요 목적
- 타입 안전성(Type Safety) ➡️ 데이터 타입을 명확히 정의하여 오류 방지
- 언어 독립성(Language Independence) ➡️ Java, Python, Go 등 여러 언어에서 사용 가능
- 명세화(Specification) ➡️ 데이터 구조를 표준화하여 시스템 간 원활한 통신 보장
- 자동 코드 생성(AUtomatic Code Generation) ➡️ .proto 파일을 기반으로 다양한 언어의 코드 자동 생성
직렬화(Serialization) 및 역직렬화(Deserialization)
✅ 직렬화(Serialization)란?
- 데이터를 바이너리(Binary) 포맷으로 변환하여 저장 또는 전송하는 과정
- JSON보다 훨씬 작은 크기로 데이터를 변환 가능
- 네트워크 트래픽 절감 및 빠른 데이터 전송 가능
✅ 역직렬화(Deserialization)란?
- 바이너리 데이터 → 원래 객체 형태로 변환하는 과정
- JSON 파싱보다 훨씬 빠르게 데이터를 읽어올 수 있음
JSON과 ProtoBuf 직렬화 비교
JSON (텍스트 기반 직렬화)
{
"id": 1,
"name": "Alice",
"email": "alice@example.com"
}
- 사람이 직접 읽을 수 있음
- 데이터 크기가 크고, 네트워크 대역폭 소모가 많음
- 파싱 속도가 느림
ProtoBuf (바이너리 직렬화)
\x08\x01\x12\x05Alice\x1A\x11alice@example.com
- 크기가 작고, 빠르게 처리 가능
- 네트워크 트래픽 절감
- 사람이 직업 읽기 어려움
효율적인 데이터 표현
ProtoBuf는 JSON 대비 70% 이상 작은 데이터 크기를 유지하며, 빠른 파싱 속도를 제공하여 성능을 최적화한다.
성능 요소 | ProtoBuf | JSON |
데이터 크기 | 작음 (바이너리) | 큼 (텍스트 기반) |
전송 속도 | 빠름 | 느림 (파싱 비용 발생) |
네트워크 효율성 | 트래픽 절감 | 트래픽 증가 |
처리 속도 | 빠름 | 상대적으로 느림 |
예제: JSON과 ProtoBuf 비교 (Java 코드)
// 1️⃣ JSON 직렬화
ObjectMapper objectMapper = new ObjectMapper();
String jsonData = objectMapper.writeValueAsString(new User(1, "Alice", "alice@example.com"));
// 2️⃣ ProtoBuf 직렬화
UserProto.User user = UserProto.User.newBuilder()
.setId(1)
.setName("Alice")
.setEmail("alice@example.com")
.build();
byte[] protoData = user.toByteArray();
- JSON 직렬화 ➡️ 사람이 읽을 수 있지만 크기가 큼
- ProtoBuf 직렬화 ➡️ 더 작은 크기와 빠른 데이터 처리 가능
📌 ProtoBuf의 구조
Protocol Buffers(ProtoBuf)는 데이터를 효율적으로 직렬화하고 전송하기 위한 구조를 가지고 있으며, 이는 메시지 타입, 필드, 데이터 타입, 연속번호, 서비스 정의 등 다양한 요소로 구성된다.
💡 ProtoBuf의 핵심 구조
요소 | 설명 |
메시지 타입 | 객체와 유사한 구조로, 하나 이상의 필드를 포함 |
필드 | 메시지 내부에서 데이터를 담는 요소 |
필드 규칙 | required, optional, repeated 등의 규칙 |
연속번호 | 필드의 고유 식별 번호 |
데이터 타입 | 기본 타입과 사용자 정의 타입 (messge, enum) |
옵션 | 파일, 메시지, 필드 레벨에서 추가 설정 가능 |
서비스 정의 | RPC 시스템에서 사용될 서비스와 메서드 정의 |
확장(Extenstion) | 기존 메시지를 수정하지 않고 기능 추가 |
1️⃣ 메시지 타입 (Message Type)
메시지 타입은 객체 지향 프로그래밍에서 “클래스(Class)“와 유사한 개념이다.
데이터를 그룹화하여 하나의 단위로 관리할 수 있도록 해줍니다.
예제: 사용자 정보를 저장하는 메시지 타입 정의
message User {
int32 id = 1;
string name = 2;
string email = 3;
}
- message 키워드: 새 메시지 타입을 정의
- id, name, email 필드 포함
- 각 필드는 고유한 숫자(1, 2, 3)를 가짐 (연속번호 활용)
📢 💡 JSON과 비교해보면?
{
"id": 1,
"name": "Alice",
"email": "alice@example.com"
}
- ✅ JSON보다 명확한 타입 정의(int32, string 등)
- ✅ JSON은 사람이 읽기 쉽지만, ProtoBuf는 크기가 더 작고 빠름
2️⃣ 필드 (Field)
ProtoBuf에서 필드는 실제 데이터를 저장하는 핵심 요소이다. 각 필드는 이름, 타입, 고유 번호(연속번호)를 가진다.
예제: 자동차 정보 저장 메시지
message Car {
string brand = 1; // 브랜드명
int32 year = 2; // 연식
bool isElectric = 3; // 전기차 여부
}
- 각 필드는 특정 데이터 타입을 가짐 (string, int32, bool 등)
- 고유 번호(1, 2, 3)는 직렬화된 데이터에서 해당 필드를 찾는 데 사용됨
3️⃣ 필드 규칙 (Field Rules)
ProtoBuf 2.x에서는 필드 규칙을 required, optional, repeated로 정의할 수 있었지만, ProtoBuf 3.x부터는 optional, repeated만 사용할 수 있다.
필드 규칙 예제
message Example {
optional string name = 1; // 선택적 필드
repeated int32 numbers = 2; // 여러 개의 값을 저장할 수 있는 필드
}
- optional: 값이 없어도 됨 (기본값 적용)
- repeated: 리스트 형태로 여러 값을 저장 가능
📢 💡 JSON과 비교
{
"numbers": [10, 20, 30]
}
- ✅ JSON의 배열과 유사하지만, ProtoBuf는 바이너리 포맷으로 변환되어 보다 효율적임
4️⃣ 연속번호 (Field Number)
ProtoBuf에서 각 필드는 고유한 번호를 가져야 하며, 직렬화할 때 필드의 식별자로 사용된다.
연속번호는 1부터 시작하며, 기존 필드의 번호를 변경하면 데이터 호환성이 깨질 수 있다.
예제
message Product {
int32 id = 1;
string name = 2;
float price = 3;
}
- 연속번호는 변경하지 않는 것이 중요!
- 삭제한 필드는 reserved 키워드로 지정하여 재사용 방지 가능
필드 제거 후 reserved 사용 예제
message Product {
reserved 4, 5; // 필드 번호 4, 5는 재사용 금지
reserved "old_price"; // 특정 필드명을 재사용하지 않도록 지정
}
- 기존 필드 번호를 다시 사용하면 데이터 충돌이 발생할 수 있으므로 주의!!
5️⃣ 데이터 타입 (Data Types)
ProtoBuf는 기본 데이터 타입과 사용자 정의 타입을 지원한다.
✅ 기본 데이터 타입
타입 | 설명 |
int32, int64 | 정수형 |
float, double | 부동소수점 숫자 |
bool | 논리형 (true/false) |
string | 문자열 |
bytes | 바이너리 데이터 |
사용 예제
message Person {
int32 age = 1;
float height = 2;
bool isStudent = 3;
}
✅ 사용자 정의 타입
✔ 메시지를 타입으로 사용 가능하다!!
message Address {
string city = 1;
string country = 2;
}
message Person {
string name = 1;
Address address = 2; // 다른 메시지를 필드로 포함 가능
}
6️⃣ 서비스 정의 (Service Definition)
ProtoBuf는 RPC(Remote Procedure Call) 시스템에서 서비스 정의도 가능하다.
이를 통해 gRPC와 함께 사용하여 마이크로서비스 간 통신을 효율적으로 수행할 수 있다.
서비스 정의 예제
service UserService {
rpc GetUser (UserRequest) returns (UserResponse);
}
- 서버-클라이언트 간 원격 메서드 호출 가능
7️⃣ Protobuf의 확장 기능 (Extensions)
ProtoBuf는 기존 메시지를 수정하지 않고 새로운 기능을 추가할 수 있도록 확장(Extension) 기능을 제공한다.
예제
extend User {
optional string nickname = 100;
}
- 기존 User 메시지를 수정하지 않고 새로운 필드 추가 가능
- API 변경 없이 데이터 확장 가능 (Backward Compatibility 유지)
8️⃣ 옵션 (Options)
옵션(Options)은 ProtoBuf에서 특정 동작을 조정하기 위해 사용되는 설정 값이다.
이를 통해 파일 수준, 메시지 수준, 필드 수준에서 기본 동작을 변경하거나, 사용자 정의 옵션을 추가할 수 있다.
옵션을 사용하면 다음과 같은 효과를 얻을 수 있다.
옵션 | 설명 |
파일 레벨 옵션 | 언어별 코드 생성 방식 지정 가능 .proto 파일 전체에 영향을 미치는 옵션 |
메시지 레벨 옵션 | 특정 메시지의 동작을 조정하는 옵션 |
필드 레벨 옵션 | 특정 필드의 직렬화 방식 지정 가능 |
사용자 정의 옵션 | 확장성을 고려한 새로운 기능 추가 가능 |
⌥ 파일 레벨 옵션 (File Level Options)
파일 레벨 옵션은 .proto 파일의 맨 위에서 선언되며, 전체 코드 생성 방식에 영향을 준다.
예제: 파일 레벨 옵션
syntax = "proto3"; // 프로토콜 버전 정의
package example; // 패키지명 정의
option java_package = "com.example.protobuf"; // Java 패키지 지정
option java_outer_classname = "UserProto"; // Java에서 생성될 클래스명 설정
option go_package = "examplepb"; // Go 언어의 패키지명 설정
- syntax = "proto3"; ➡️ ProtoBuf 버전(2 또는 3)을 지정
- package example; ➡️ 패키지명을 지정하여 네임스페이스 충돌 방지
- option java_package ➡️ Java 코드 생성 시 패키지 경로 설정
- option java_outer_classname ➡️ Java에서 생성될 클래스명을 명시적으로 지정
⌥ 메시지 레벨 옵션 (Message Level Options)
메시지 레벨 옵션은 특정 메시지의 동작을 변경하는 데 사용된다.
예제: 메시지 레벨 옵션
message User {
option deprecated = true; // 이 메시지는 더 이상 사용되지 않음
string name = 1;
int32 age = 2;
}
- option deprecated = true; ➡️ 해당 메시지가 더 이상 사용되지 않음을 표시
- 클라이언트나 서버에서 경고를 띄울 수 있음
📢 특정 메시지가 더 이상 사용되지 않거나 새로운 데이터 구조로 대체될 경우 유용하다.
⌥ 필드 레벨 옵션 (Field Level Options)
필드 레벨 옵션은 각 필드에 대한 특정 동작을 설정할 때 사용된다.
예제: 필드 레벨 옵션
message User {
string name = 1 [(deprecated) = true]; // 이 필드는 더 이상 사용되지 않음
int32 age = 2 [(json_name) = "user_age"]; // JSON 변환 시 필드명 변경
}
- (deprecated) = true; ➡️ 특정 필드가 더 이상 사용되지 않음을 표시
- (json_name) = "user_age"; ➡️ JSON 직렬화 시 필드명을 user_age로 변환
📢 필드 이름을 외부 API에서 다르게 사용하거나, 특정 필드를 더 이상 사용하지 않을 때 유용하다.
⌥ 사용자 정의 옵션 (Custom Options)
ProtoBuf는 기본 제공 옵션 외에도 사용자가 직접 옵션을 정의할 수 있는 기능을 제공한다.
이를 통해 기본 동작을 확장하고, 특정 도메인에 맞는 기능을 추가할 수 있다.
예제: 사용자 정의 옵션
import "google/protobuf/descriptor.proto"; // 기본 옵션 정의 파일 import
extend google.protobuf.MessageOptions {
optional string author = 50001; // 새로운 옵션 추가
}
message Book {
option (author) = "Alice"; // 새로 정의한 옵션 적용
string title = 1;
}
- extend google.protobuf.MessageOptions ➡️ 기본 메시지 옵션 확장
- optional string author = 50001; ➡️ author라는 새로운 옵션 추가
- option (author) = "Alice"; ➡️ 메시지에 새로운 옵션 적용
'기술(Tech) > Network & System' 카테고리의 다른 글
gRPC에 대해 알아보자 (0) | 2025.02.15 |
---|---|
모바일 애플리케이션 및 웹 애플리케이션 최적화 전략 (1) | 2025.02.09 |
REST API vs. GraphQL: 어떤 것을 선택해야 할까?💭 (0) | 2025.02.09 |