Protocol Buffers(Protobuf)는 빠르고 작고 언어 중립적이지만, 바이너리라서 사람이 읽을 수 없습니다. 메시지 내용을 확인하거나, API를 디버깅하거나, 테스트 픽스처를 만들어야 할 때 Protobuf to JSON 변환기.proto 스키마(또는 raw 바이너리)에서 읽기 쉬운 JSON으로 가는 가장 빠른 길입니다. gRPC 서버도, 코드 생성도, 로컬 환경 구성도 필요 없습니다.

Protobuf to JSON 변환기 사용해보기 →

Protocol Buffers란?

Protocol Buffers는 Google이 설계한 바이너리 직렬화 포맷입니다. JSON과 비교하면:

  • 전송 크기가 작다: 직렬화된 Protobuf 메시지는 보통 동일한 JSON보다 3–10배 작음
  • 직렬화/역직렬화가 빠르다: 바이너리 인코딩은 문자열 파싱을 건너뜀
  • 강타입: .proto 파일이 모든 필드 이름과 타입을 명확히 정의
  • 언어 중립: Go, Python, Java, TypeScript, Rust 등 다양한 언어용 코드 생성기가 존재

대가는 불투명성입니다. 대응하는 스키마 없이는 Protobuf 바이너리를 읽을 수 없고, 디버깅에는 별도 도구가 필요합니다.

두 가지 변환 모드

모드 1: 스키마에서 샘플 JSON 생성

.proto 스키마를 붙여넣으면 모든 필드가 합리적인 placeholder 값으로 채워진 샘플 JSON 객체가 만들어집니다. 활용:

  • 테스트 작성: JSON 픽스처를 손으로 쓰지 않고 스키마에서 생성
  • API 문서화: 응답이 어떻게 생겼는지 사용자에게 보여주기
  • 프론트엔드 mocking: 백엔드가 개발 중이어도 안정된 JSON 형태로 UI 작업

입력 예제(.proto):

syntax = "proto3";

message UserProfile {
  string user_id = 1;
  string display_name = 2;
  string email = 3;
  int64 created_at = 4;
  repeated string roles = 5;
  Address address = 6;
}

message Address {
  string street = 1;
  string city = 2;
  string country_code = 3;
  string postal_code = 4;
}

출력 예제(샘플 JSON):

{
  "userId": "string",
  "displayName": "string",
  "email": "string",
  "createdAt": "0",
  "roles": ["string"],
  "address": {
    "street": "string",
    "city": "string",
    "countryCode": "string",
    "postalCode": "string"
  }
}

user_iduserId로 바뀐 점에 주목하세요. Protobuf의 JSON 직렬화는 기본적으로 camelCase를 사용하며, 이는 JSON의 표준 관례와 일치합니다.

모드 2: 바이너리 메시지를 JSON으로 디코딩

16진 인코딩된 Protobuf 바이너리와 스키마를 함께 붙여넣으면 도구가 바이너리를 사람이 읽을 수 있는 JSON으로 디코딩합니다. 다음 상황에서 유용합니다:

  • 네트워크 캡처(Wireshark, mitmproxy)에서 얻은 페이로드 확인
  • 완전한 클라이언트 없이 gRPC 엔드포인트 디버깅
  • 한 서비스가 직렬화한 메시지를 다른 서비스가 기대대로 받을 수 있는지 검증

16진 입력 예제:

0a 07 75 73 65 72 2d 31 32 33 12 05 41 6c 69 63 65

위의 UserProfile 스키마와 함께 디코딩하면:

{
  "userId": "user-123",
  "displayName": "Alice"
}

바이너리에 없는(또는 zero value로 설정된) 필드는 JSON 출력에서 생략됩니다.

.proto 문법 빠른 정리

Protobuf가 처음이라면, 변환기에 붙여넣을 스키마를 이해하기 위해 알아야 할 핵심 문법입니다.

메시지 정의

syntax = "proto3";

message SearchRequest {
  string query = 1;       // 필드 이름, 타입, 필드 번호
  int32 page_number = 2;
  int32 results_per_page = 3;
}

필드 번호(1, 2, 3)는 Protobuf가 바이너리 wire format에서 필드를 식별하는 방식이지 값이 아닙니다. 번호 1–15는 인코딩 시 1바이트, 16–2047은 2바이트를 사용하므로 자주 쓰는 필드에는 작은 번호를 부여하세요.

스칼라 타입

proto3 타입JSON 타입비고
stringstringUTF-8 인코딩
bytesbase64 문자열바이너리 데이터
boolboolean
int32, sint32number
int64, sint64stringJSON number는 큰 int64에서 정밀도 손실
float, doublenumber
uint32, uint64number / string

int64는 JSON에서 문자열이 됩니다. 이는 Protobuf JSON 매핑 명세가 의도한 동작으로, JavaScript의 Number 타입이 64비트 정수를 정확히 표현하지 못하기 때문입니다.

Enum

enum Status {
  STATUS_UNKNOWN = 0;    // proto3: 첫 값은 반드시 0
  STATUS_ACTIVE = 1;
  STATUS_INACTIVE = 2;
  STATUS_SUSPENDED = 3;
}

message User {
  string user_id = 1;
  Status status = 2;
}

JSON 출력에서 enum은 기본적으로 정수가 아닌 문자열 이름("STATUS_ACTIVE")으로 직렬화되어 가독성이 훨씬 좋습니다.

Repeated 필드(배열)

message Order {
  string order_id = 1;
  repeated LineItem items = 2;
  repeated string tags = 3;
}

repeated는 JSON 배열에 매핑됩니다. 빈 repeated 필드는 직렬화기 설정에 따라 []로 직렬화되거나 생략됩니다.

중첩 메시지와 oneof

message Notification {
  string id = 1;
  oneof content {
    EmailNotification email = 2;
    PushNotification push = 3;
    SMSNotification sms = 4;
  }
}

oneof는 나열된 필드 중 정확히 하나만 동시에 설정할 수 있다는 의미입니다. JSON에는 설정된 필드만 나타납니다.

Import

import "google/protobuf/timestamp.proto";

message Event {
  string event_id = 1;
  google.protobuf.Timestamp occurred_at = 2;
}

Well-Known Types(Timestamp, Duration, Struct, Any, FieldMask)는 Protobuf 명세에 정의된 전용 JSON 표현을 가집니다.

Protobuf JSON 매핑 규칙

공식 JSON 매핑은 각 proto3 타입이 JSON에서 어떻게 표현되는지 정의합니다:

시나리오JSON 표현
int64/uint64십진 문자열("9007199254740993")
bytesbase64 문자열
enum문자열 이름(알 수 없는 값은 정수)
TimestampRFC 3339 문자열("2026-04-15T10:00:00Z")
Durations 접미사 문자열("3.5s")
FieldMaskcamelCase 점 구분 문자열
Any{"@type": "...", ...필드}
StructJSON 객체
ListValueJSON 배열
NullValueJSON null
기본값 필드생략(proto3 기본 동작)

마지막 항목이 가장 자주 발목을 잡습니다. proto3에서는 zero value(빈 문자열, 0, false, 빈 배열)로 설정된 필드가 기본적으로 JSON 출력에서 생략됩니다. 모두 출력하려면 Go의 EmitUnpopulated 또는 다른 언어의 동등한 직렬화기 옵션을 켜세요.

gRPC 트래픽 디코딩

gRPC는 HTTP/2 위에서 Protobuf를 사용합니다. grpcurl이나 grpc-gateway로 디버깅할 때 raw 바이너리 응답이 자주 필요해집니다. 워크플로:

  1. 네트워크 도구나 로그에서 16진 덤프 확보
  2. gRPC framing(5바이트 헤더: 압축 플래그 1바이트 + 메시지 길이 4바이트) 제거
  3. 남은 바이트를 .proto 스키마와 함께 변환기에 붙여넣기

서버 리플렉션이 켜져 있다면 grpcurl이 스키마 해석을 자동으로 처리합니다:

grpcurl -plaintext localhost:50051 list
grpcurl -plaintext -d '{"userId": "123"}' localhost:50051 user.UserService/GetUser

리플렉션이 없는 서비스에는 .proto 파일을 직접 전달합니다:

grpcurl -plaintext -proto user.proto -d '{"userId": "123"}' localhost:50051 user.UserService/GetUser

일반적인 사용 사례

테스트 픽스처 생성

JSON 픽스처를 손으로 쓰지 말고 스키마에서 생성하세요. 픽스처가 스키마 정의와 동기화된 상태로 유지됩니다.

API 응답 문서화

응답 메시지 정의를 붙여넣고 생성된 JSON을 API 문서에 복사하세요. 스키마가 바뀌면 샘플을 다시 생성하면 됩니다.

직렬화 버그 추적

JSON 출력에서 있어야 할 필드가 빠져 있다면, 그 필드가 zero value로 설정된 것입니다. 바이너리 디코드 모드로 실제로 직렬화된 내용을 확인하세요.

프라이버시

이 변환기는 protobufjs를 사용해 브라우저에서 완전히 실행됩니다. .proto 파일과 바이너리 메시지 데이터는 어떤 서버로도 전송되지 않습니다. 내부 API를 설명하는 스키마나 사용자 데이터를 담은 메시지에서도 안심하고 사용할 수 있습니다.

지금 사용해보기

.proto 스키마를 붙여넣어 샘플 JSON 구조를 생성하거나, 스키마와 16진 인코딩된 바이너리 메시지를 함께 붙여넣어 디코딩해보세요.

Protobuf to JSON 변환기 열기 →