Protobuf JSON 변환·디코딩
Protobuf 바이너리를 JSON으로 변환·디코딩합니다. .proto 없이도 읽을 수 있고, 16진수·Base64·gRPC 프레임을 지원하며 int64와 Timestamp는 ProtoJSON대로 출력합니다. JSON을 바이너리로 인코딩할 수도 있습니다.
- 브라우저에서 처리
- 데이터가 브라우저 밖으로 나가지 않습니다
- 무료 · 회원가입 불필요
WeChat으로 스캔하여 공유
예시·자세한 설명·자주 묻는 질문 실제 출력이 있는 예시, 다른 도구와의 차이, 자주 묻는 질문.
예: 회원 데이터
다음 회원 메시지를 예로 듭니다.
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은 저장하지 않습니다.