Protobuf JSON 변환·디코딩

Protobuf 바이너리를 JSON으로 변환·디코딩합니다. .proto 없이도 읽을 수 있고, 16진수·Base64·gRPC 프레임을 지원하며 int64와 Timestamp는 ProtoJSON대로 출력합니다. JSON을 바이너리로 인코딩할 수도 있습니다.

  • 브라우저에서 처리
  • 데이터가 브라우저 밖으로 나가지 않습니다
  • 무료 · 회원가입 불필요
“바이너리 → JSON”은 Protobuf 바이트를 디코딩합니다. “JSON → 바이너리”는 ProtoJSON을 바이트로 되돌리며 .proto 스키마와 메시지 타입이 필요합니다. “샘플 JSON”은 선택한 타입의 필드를 하나씩, 자리 표시 값으로 출력합니다. 어느 모드든 입력하는 대로 출력이 바뀝니다. JSON 칸이 빈 상태에서 “JSON → 바이너리”로 바꾸면 바이트를 디코딩한 JSON이 채워집니다.
바이트를 어떤 메시지로 디코딩할지, JSON을 어떤 메시지로 인코딩할지 고릅니다. 목록에는 .proto 파일의 타입이 나오고, 다른 타입이 필드로 쓰지 않는 첫 번째 타입(보통 최상위 메시지)이 처음부터 선택됩니다. “스키마 없음(raw 디코딩)”은 “바이너리 → JSON”에만 있으며 이름 없이 와이어 형식을 읽습니다.
“자동”은 16진수, Base64, 이스케이프 문자열, 바이트 목록을 구분하고 어떤 형식으로 읽었는지 상태 줄에 표시합니다. 16진수로도 Base64로도 유효한 텍스트는 16진수로 읽고 참고에 알립니다. 틀렸다면 여기서 Base64를 고르세요. “자동”은 입력이 프레임 플래그(00, 01, 80, 81)로 시작하고 5바이트 헤더들이 입력 전체를 정확히 나눌 때 gRPC 프레임을 뗍니다. 메시지가 여러 개면 JSON 배열로 출력하고, gRPC-Web의 trailers는 참고에 표시하며, 압축 플래그가 있는 프레임은 gzip으로 풉니다. 다른 압축 방식은 지원하지 않습니다. “있음”은 프레임이 깨져 있으면 오류로 알립니다. “없음”은 모든 바이트를 메시지로 읽습니다. “lowerCamelCase”는 각 필드의 ProtoJSON 이름을 출력합니다. json_name 옵션이 있으면 그 이름, 없으면 필드 이름을 lowerCamelCase로 바꾼 이름입니다. “.proto 그대로”는 스키마에 적힌 이름으로 출력합니다. “JSON → 바이너리”는 이 설정과 관계없이 두 이름을 모두 받습니다. ProtoJSON은 존재 여부를 기록하지 않는 필드(proto3의 일반 필드)가 기본값(0, 빈 문자열, false, enum의 첫 값)이면 출력하지 않습니다. 빈 repeated와 map 필드도 출력하지 않습니다. 켜면 이들도 출력합니다. 존재 여부를 기록하는 필드(optional, oneof, 메시지 필드)는 바이트에 설정되어 있을 때만 출력합니다. 스키마 없이 디코딩할 때의 표시 방식입니다. “JSON”은 길이 접두 값을 읽을 수 있는 텍스트면 문자열로, 메시지로 파싱되면 중첩 객체로, 둘 다 아니면 base64로 보여 줍니다. “JSON(다른 해석 포함)”은 각 숫자를 같은 와이어 타입의 다른 타입(sint64, sfixed64, float, double 등)으로 읽은 값도 보여 줍니다. “protoc --decode_raw”는 protoc이 출력하는 텍스트를 그대로 내며 중첩 메시지를 우선합니다. 끄면 메시지 타입에 없는 키가 JSON에 있을 때 줄과 열, 비슷한 필드가 있으면 그 이름을 알리고 인코딩을 멈춥니다. 켜면 그런 키를 건너뛰고 참고에 나열하며, 스키마에 없는 enum 이름도 건너뜁니다.

16진수(공백, 줄바꿈, 콜론, 0x 접두사 가능), Base64(표준·URL-safe, 패딩 유무 무관, data: URL 가능), 이스케이프 문자열(Python 바이트 리터럴이나 protoc 8진수 이스케이프), -128~255 범위의 바이트 값 목록을 붙여넣을 수 있습니다. 입력하는 대로 디코딩합니다. “파일 열기”나 칸에 파일을 끌어다 놓으면 2MB 이하의 바이너리 파일을 16진수로 불러옵니다.
선택한 메시지 타입의 ProtoJSON입니다. 입력하는 대로 인코딩합니다. 키는 필드의 JSON 이름이나 .proto 이름 모두 쓸 수 있습니다. 정수는 숫자나 문자열로, enum은 이름이나 숫자로 쓸 수 있고 bytes는 Base64입니다. 오류는 줄과 열을 알려 줍니다.
필드 이름을 알려 주는 .proto 소스입니다. “바이너리 → JSON”에서는 생략할 수 있으며 그러면 필드가 번호로 표시됩니다. 다른 두 모드에는 필요합니다. import하는 파일은 “.proto 파일 추가”로 넣거나 칸에 끌어다 놓으세요. google/protobuf의 잘 알려진 타입(any, duration, empty, field_mask, struct, timestamp, wrappers)은 내장되어 있습니다. 컴파일된 descriptor set은 불러올 수 없고, descriptor.proto를 확장하는 커스텀 옵션은 무시합니다.
출력
  
예시·자세한 설명·자주 묻는 질문 실제 출력이 있는 예시, 다른 도구와의 차이, 자주 묻는 질문.

예: 회원 데이터

다음 회원 메시지를 예로 듭니다.

syntax = "proto3";

package member.v1;

message Member {
  int64 member_id = 1;
  string name = 2;
  bytes legacy_name = 3;
}

protoc --encode로 만든 16바이트 08 8a d1 d4 09 12 09 ed 99 8d ea b8 b8 eb 8f 99를 member.v1.Member로 디코딩하면 다음과 같습니다. 한글은 UTF-8에서 한 글자 3바이트라 “홍”은 ed 99 8d입니다.

{
  "memberId": "20261002",
  "name": "홍길동"
}

ProtoJSON 규격에 따라 member_id는 memberId가 되고, 64비트 정수는 작은 값이어도 문자열로 나옵니다.

예: EUC-KR로 저장된 이름

오래된 시스템에서 넘어온 데이터는 한글이 EUC-KR(CP949)로 들어 있는 경우가 있습니다. Python에서 '홍길동'.encode('euc-kr')은 c8 ab b1 e6 b5 bf입니다. 이 6바이트가 string 필드인 name에 들어 있으면 디코딩은 실패합니다.

7바이트: string 필드 name가 올바른 UTF-8이 아닙니다(proto3는 UTF-8을 요구). 바이너리는 bytes 필드에 넣어야 합니다.

proto3의 string은 UTF-8이어야 하므로 protoc(C++)와 Python protobuf 7.36.2도 이 메시지를 “invalid UTF-8”로 거부합니다(2026-10-02 실측). 같은 바이트를 bytes 필드인 legacy_name(필드 3)에 넣으면 Base64로 출력됩니다.

{
  "memberId": "20261002",
  "legacyName": "yKux5rW/"
}

스키마 없이 raw “JSON”으로 보면 EUC-KR 바이트는 UTF-8 텍스트도, 메시지도 아니라서 "2": "base64:yKux5rW/"로 표시됩니다. proto2 파일이라면 UTF-8을 검사하지 않으므로 오류 대신 U+FFFD 대체 문자로 보여 주고 참고에 그 사실을 적습니다.

예: JSON에서 gRPC 메시지로

“JSON → 바이너리”에서 다음 JSON을 넣고 “gRPC 프레임 추가”를 켜면, 앞에 5바이트(플래그 00 + 빅엔디언 길이 00 00 00 10)가 붙은 메시지가 나옵니다.

{"memberId": "20261002", "name": "홍길동"}
00 00 00 00 10 08 8a d1 d4 09 12 09 ed 99 8d ea b8 b8 eb 8f 99

gRPC over HTTP/2의 Length-Prefixed-Message 형식이므로 grpcurl 없이 테스트 요청 본문을 만들 때 쓸 수 있습니다. 키는 memberId, member_id 모두 받고, enum은 이름이나 번호, int64는 문자열이나 숫자로 써도 됩니다. 출력은 16진수, Base64, Python 바이트 리터럴, C 배열 중에서 고르거나 .bin으로 저장할 수 있습니다.

다른 도구와의 차이

2026-10-02에 영어 페이지의 주문 데이터(Timestamp와 2^53을 넘는 int64 포함)로 확인했습니다. Bing에서 “protobuf 디코딩” 상위에 나오는 MakerBox의 Protobuf 디코더는 스키마 없이 읽고 큰 정수도 정확하지만, gRPC 프레임 헤더가 붙은 데이터는 “필드 번호 0이 있습니다”라며 5바이트를 먼저 떼라고 안내하고, .proto는 받지 않습니다. codertools.net(한국어판 있음)과 egohero.com은 .proto를 쓰지만 import "google/protobuf/timestamp.proto"가 있으면 egohero는 “no such Type or Enum ‘google.protobuf.Timestamp’”로 멈추고 codertools는 메시지 타입 목록이 비어 있었습니다. Timestamp를 빼면 둘 다 order_id를 "9007199254740992"(실제보다 1 작음)로, 이름은 snake_case 그대로 출력했습니다. 어느 도구도 입력을 서버로 보내지 않았습니다.

제한

  • .proto 소스만 읽습니다. protoc --descriptor_set_out으로 만든 디스크립터나 텍스트 형식 메시지는 불러올 수 없습니다. 내장 import는 any, duration, empty, field_mask, struct, timestamp, wrappers이고, google/api/annotations.proto 등에서 타입을 쓴다면 직접 추가해야 합니다. descriptor.proto를 확장하는 사용자 정의 옵션은 무시합니다. 옵션은 바이트에 영향을 주지 않습니다.
  • 알 수 없는 필드는 JSON에 들어가지 않습니다. 참고에 번호, 와이어 타입, 바이트 위치를 적고, 알 수 없는 필드 없이 디코딩되는 타입도 알려 줍니다.
  • 타입을 잘못 골라도 디코딩이 될 수 있습니다. 바이트에는 타입 이름이 없으니 참고의 알 수 없는 필드 수를 확인하세요.
  • 중첩은 100단계까지, 여는 파일은 2MB까지입니다. 압축 프레임은 gzip만 풉니다.
  • proto2와 editions 2023/2024의 그룹, required, closed enum, 확장("[pkg.ext]"), 필드 존재 여부, packed와 expanded, delimited 메시지를 지원합니다.

관련 도구: 캡처 확인은 HAR 파일 분석기, 페이로드 변환은 Base64 인코드 / 디코드, JSON 검사는 JSON Schema 유효성 검사기를 쓰세요.

FAQ

.proto 파일 없이도 디코딩할 수 있나요?

네. 스키마를 비워 두면 protoc --decode_raw와 같은 방식으로 읽어 필드 번호, 숫자, 문자열, 중첩 메시지를 보여 줍니다. 필드 이름, enum 이름, 부호 있는 타입과 64비트 타입, Timestamp 같은 잘 알려진 타입은 바이트에 들어 있지 않으므로 .proto가 필요합니다.

int64가 문자열로 나오는 이유는 무엇인가요?

ProtoJSON 규격은 int64, uint64, sint64, fixed64, sfixed64를 10진수 문자열로 쓰도록 정합니다. JavaScript는 JSON 숫자를 double로 읽기 때문에 2^53을 넘으면 아래 자리가 바뀝니다. 이 도구는 64비트 필드를 BigInt로 읽어 자릿수를 잃지 않습니다.

출력에서 일부 필드가 빠지는 이유는 무엇인가요?

ProtoJSON은 proto3의 일반 필드가 기본값(0, 빈 문자열, false, enum의 첫 값)이면 출력하지 않습니다. “기본값 필드도 출력”을 켜면 보입니다. 스키마에 없는 번호의 필드는 ProtoJSON에 쓸 수 없으므로 “참고”에 바이트 위치와 함께 표시합니다.

gRPC 캡처를 그대로 붙여넣어도 되나요?

됩니다. gRPC 프레임이 “자동”이면 각 메시지 앞의 5바이트(플래그와 길이)를 떼고 차례로 디코딩합니다. gRPC-Web의 trailers는 참고에 표시하고, 압축 플래그가 01인 프레임은 gzip으로 풀어서 읽습니다.

데이터가 업로드되나요?

아니요. 파싱, 디코딩, 인코딩은 모두 브라우저 안에서 실행됩니다. 저장하는 것은 옵션 선택뿐이며 스키마, 바이트, JSON은 저장하지 않습니다.