Protobuf - DynamicMessage, Descriptor

2026. 8. 25. 18:12·기타/잡동사니

DynamicMessage

What

Descriptor를 런타임에 받아 아무 protobuf 메시지 타입이나 담을 수 있는 범용 Message 구현체다.
생성 클래스(ConnectDefault1) 을 대체한다.

public final class DynamicMessage extends AbstractMessage {
  DynamicMessage(Descriptor type,
                  FieldSet<FiledDescriptor> fields, // 값이 여기 담긴다.
                  FieldDescriptor[] oneOfCases,
                  UnknownFieldSet unknownFields);
}

생성 클래스는 필드가 Java field 로 박혀 있지만
DynamicMessage는 FieldDescriptor -> Object 맵 (FieldSet)에 담는다.
그래서 타입이 컴파일 타임에 필요 없다.

  생성 클래스 DynamicMessage
타입 정보 클래스 자체 생성자에 받은 Descriptor
값 저장 private ByteString messageData_; FieldSet<FieldDescriptor>
필드 접근 msg.getMessageData() msg.getField(fd) → Object
필드 지정 컴파일 타임 검증 findFieldByName("...") 런타임 조회
파싱 ConnectDefault1.parseFrom(bytes) DynamicMessage.parseFrom(descriptor, bytes)

Why

사전에 생성 클래스를 만들 수 없는 상황을 위함이다.
컴파일 시점에 .proto 파일이 없는 케이스가 대표적이다.

Descriptor 가 설계도라면 DynamicMessage 는 설계도로 찍어낸 인스턴스다.

대가가 있다.

  • Field Access 가 Map 조회 + Boxing 이라 생성 클래스보다 느리고 할당이 많다.
  • getField()는 Object 를 반환한다. 캐스팅 실패시 런타임 ClassCastException 이 발생하낟.
  • 필드를 문자열 이름으로 찾는다.

How

(1) Parsing

Descriptor descriptor = envelopeDescriptor(schemaId, indexes);
DynamicMessage envelope = DynamicMessage.parseFrom(descriptor, payload);

(2) 단일 필드 읽기


FieldDescriptor field = envelope.getDescriptorForType().findFieldByName("MessageData");
if (field == null) {
    throw WireFormatException.withNoStackTrace("Not a PubSub envelope: no field MessageData");
}

byte[] data = ((ByteString) envelope.getField(field)).toByteArray();

값 타입 매핑 — getField 가 돌려주는 Java 타입

캐스팅을 틀리기 쉬운 부분이다.

proto 타입 Java 객체
double / float Double / Float
int32, sint32, uint32, fixed32 Integer
int64, sint64, uint64, fixed64 Long
bool Boolean
string String
bytes ByteString (byte[] 아님)
enum EnumValueDescriptor
message Message (실제 구현은 DynamicMessage)
repeated T List<위 타입>

 

Descriptor

What

메시지 타입 하나의 런타임 타입 정보. Java 의 Class 객체에 대응한다.

.proto 파일 하나는 FileDescriptor 가 되고, 그 안에 메시지별 Descriptor 가 들어 있다.

ConnectDefault1 의 Descriptor 가 갖는 정보:

name: ConnectDefault1
fields:
  1  MessageData    bytes      optional
  2  AttributesMap  message    repeated  → ConnectDefault2Entry
nested types:
  ConnectDefault2Entry
    1  key    string
    2  value  string

Why

직렬화된 protobuf 바이너리에는 필드 번호와 wire type 만 있고 field명, type명이 없다.

0a 86 02 ...
^^ field 1, wiretype 2 ← 바이트가 가진 정보는 여기까지

이걸 MessageData 라는 bytes 필드로 해석하려면 설계도가 필요하다. Descriptor가 바로 그 설계도다.
protoc 로 생성한 클래스도 내부에 반드시 Descriptor 를 포함한다.

즉, 런타임에 타입 정보를 가지고 .proto 로 직렬화된 데이터를 각 언어의 객체로 파싱 지원하기 위함이다.

Descriptor 는 항상 존재한다.

ConnectDefault1.getDescriptor()      // 생성 클래스에도 있다
envelope.getDescriptorForType()      // DynamicMessage 에도 있다 (Message 인터페이스 메서드)

How

FieldDescriptor field = envelope.getDescriptorForType()   // 이 메시지의 설계도
    .findFieldByName(ccPubSubProperties.messageData());   // "MessageData" 필드 찾기
return ((ByteString) envelope.getField(field)).toByteArray();

Java 리플랙션으로 필드를 꺼내는 것과 구조가 같다.

  • Class <-> Descriptor
  • Field <-> FieldDescriptor
    위와 같이 대응된다.
저작자표시 (새창열림)

'기타 > 잡동사니' 카테고리의 다른 글

2025년 개발 회고  (0) 2026.01.10
Clean Code - 테스트는 어디서든 반복 실행 가능해야한다  (0) 2025.11.07
cursor AI Editor 설정  (0) 2025.03.31
Yaml 파일 쓰기 - 고급  (0) 2024.11.11
Semantic Versioning  (0) 2024.09.20
'기타/잡동사니' 카테고리의 다른 글
  • 2025년 개발 회고
  • Clean Code - 테스트는 어디서든 반복 실행 가능해야한다
  • cursor AI Editor 설정
  • Yaml 파일 쓰기 - 고급
M_Falcon
M_Falcon
  • M_Falcon
    Falcon
    M_Falcon
  • 전체
    오늘
    어제
    • 분류 전체보기 (435) N
      • Web (16)
        • Nodejs (14)
        • Javascript (23)
        • FrontEnd (4)
      • DataBase (39)
        • Fundamental (1)
        • Redis (4)
        • PostgreSQL (10)
        • NoSQL (4)
        • MySQL (9)
        • MSSQL (3)
        • Error (4)
      • Algorithm (79)
        • Algorithm (문제풀이) (56)
        • Algorithm (이론) (23)
      • JVM (65)
        • Spring (13)
        • JPA (5)
        • Kotlin (13)
        • Java (24)
        • Error (7)
      • 기타 (72) N
        • Kafka (3)
        • Kubernetes (3)
        • Docker (14)
        • git (19)
        • 잡동사니 (28) N
      • 재테크 (11)
        • 세무 (4)
        • 투자 (3)
        • 보험 (0)
      • BlockChain (2)
        • BitCoin (0)
      • C (32)
        • C (10)
        • C++ (17)
        • Error (3)
      • Low Level (8)
        • OS (3)
        • 시스템 보안 (5)
      • 네트워크 (3)
      • LINUX (31)
        • Linux (27)
        • Error (4)
      • 저작권과 스마트폰의 이해 (0)
      • 생각 뭉치 (6)
      • 궁금증 (2)
      • Private (4)
        • 이직 경험 (0)
        • 꿈을 찾아서 (1)
      • Android (21)
        • OS (4)
  • 블로그 메뉴

    • 홈
    • WEB
    • 알고리즘
    • DataBase
    • Linux
    • Mobile
    • C
    • 방명록
  • 링크

    • github
  • 공지사항

  • 인기 글

  • 태그

    C++
    algorithm
    백준
    android
    Programmers
    Bitcoin
    JPA
    kafka
    java
    프로그래머스
    database
    Spring
    알고리즘
    javascript
    PostgreSQL
    docker
    ubuntu
    Git
    Kotlin
    linux
  • hELLO· Designed By정상우.v4.10.3
M_Falcon
Protobuf - DynamicMessage, Descriptor
상단으로

티스토리툴바