튜토리얼: 통합 Write — 오버라이드와 필드 선택
소요: 약 10분 · 선행: 빠른 시작 또는 C# 가이드
English: Use the language switcher (top right).
언제 쓰나
큰 메시지 인스턴스 하나를 많은 수신자에게 보내야 하고, 수신자마다 소수의 필드만 다를 때(표시 이름, 읽음 여부, 로케일별 문구 등). 전체를 N번 Clone하면 비용이 큽니다.
득팩은 C# 생성 struct마다 Write(oprot, fieldIds, overrides?) 하나를 둡니다. fieldIds에 null이면 전체 필드를 보내고, 선택적으로 Dictionary<int, object>로 필드 ID별 치환값을 넘기며, 메시지 객체를 바꾸거나 통째로 복제하지 않습니다.
1. IDL의 필드 ID
Thrift / 득팩 struct는 숫자 필드 ID를 씁니다.
struct ChatMessage {
1: i32 msgId;
2: string body;
3: string displayNameForRecipient;
4: bool isReadForRecipient;
}
npx deukpack ... --csharp 후 생성된 클래스에는 nested FieldId 클래스가 자동 포함됩니다. 숫자를 직접 쓸 필요 없이 ChatMessage.FieldId.DisplayNameForRecipient처럼 이름으로 참조합니다. IDL이 바뀌면 재생성 시 컴파일 타임에 깨집니다.
2. C# — 오버라이드(전체 필드)
var msg = BuildMessageOnce(); // 무거운 작업은 한 번
var overrides = new Dictionary<int, object>
{
{ ChatMessage.FieldId.DisplayNameForRecipient, "Alice (you)" },
{ ChatMessage.FieldId.IsReadForRecipient, false }
};
msg.Write(oprot, null, overrides);
규칙:
- 키 =
StructName.FieldId.PropertyName(생성된const int). 숫자 직접 사용 금지. - 값 = 생성된 프로퍼티와 같은 CLR 타입 (타입이 맞지 않으면 캐스트 예외).
overrides가 null이거나 비어 있으면Write(oprot)/Write(oprot, null, null)와 동일.- 필드가 다른 struct이거나 list/map이면 값은 그 필드 타입 전체 인스턴스(또는 컬렉션 전체)로 넘긴다. 안쪽 멤버만 골라 바꾸는 중첩 키 API는 없다. → 코어 DEUKPACK_WRITE_WITH_OVERRIDES_API.md §1.3.
전체 API 표·C++/JS: API·타입 참조 · 코어 저장소 DEUKPACK_WRITE_WITH_OVERRIDES_API.md.
3. JavaScript (--js)
js/generated_deuk.js struct 헬퍼마다 toJson, toBinary, FieldId가 붙습니다. 안 쓰는 인자는 null:
var F = ChatMessage.FieldId;
var json = ChatMessage.toJson(msg, null, {
[F.DisplayNameForRecipient]: "Bob",
[F.IsReadForRecipient]: true
});
4. C++
각 struct에 C#/JS와 맞는 kFieldId_* 상수가 있습니다. 타입 옆에 생성된 pack/바이너리(또는 Thrift 호환) 쓰기 경로를 쓰면 되며, 현재 생성물에는 별도 apply_overrides 단계가 없습니다.
API·타입 참조 참고.
5. 필드 선택 — 같은 Write
Write(oprot, fieldIds, overrides?) 에서 fieldIds가 null이 아니면 나열된 필드만 직렬화합니다. partial 타입이나 필드 단위 복사가 필요 없습니다.
var full = LoadFullUser();
full.Write(oprot, new[] {
UserRecord.FieldId.DisplayName,
UserRecord.FieldId.Level,
UserRecord.FieldId.AvatarUrl
}, null);
선택과 오버라이드를 함께:
full.Write(oprot, new[] {
UserRecord.FieldId.DisplayName,
UserRecord.FieldId.Level
}, new Dictionary<int, object> {
{ UserRecord.FieldId.DisplayName, r.LocalizedName }
});
JavaScript — 인자 형태 동일:
var F = UserRecord.FieldId;
var ids = [F.DisplayName, F.Level];
var json = UserRecord.toJson(full, ids, null);
var json2 = UserRecord.toJson(full, ids, { [F.DisplayName]: r.LocalizedName });
6. struct extends — 상속
IDL에서 extends를 쓰면 부모 필드를 자식에 자동 병합한다. 공통 필드를 한 곳에 정의하고, 자식은 고유 필드만 추가하면 된다.
struct UserBase {
1: i32 id;
2: string displayName;
}
struct UserFull extends UserBase {
3: i32 level;
4: string avatarUrl;
}
생성된 UserFull은 필드 4개(1~4)를 모두 갖는다. 다단 상속(A → B → C)도 지원하며 필드 ID 충돌 시 빌드 에러가 난다.
7. 관련: 와이어 프로파일(서브셋 타입)
다른 목적: 클라이언트 프로파일마다 필드 자체를 숨김 → wireProfiles annotation과 --wire-profile. DEUKPACK_WIRE_PROFILE_SUBSET.md.
8. 기능 비교표
| 오버라이드(전체 필드) | 필드 서브셋 | Wire Profile | extends | |
|---|---|---|---|---|
| 목적 | 전 필드 전송, 일부 값만 교체 | 선택한 필드만 전송 | 빌드 타임 서브셋 타입 | 공통 필드 상속 |
| 결정 시점 | 런타임 | 런타임 | 빌드 타임 | IDL 정의 시 |
| API | Write(oprot, null, overrides) |
Write(oprot, fieldIds, overrides?) |
— | 모든 기능과 |
| 동적 선택 | 가능 | 가능 | 불가 | — |
9. 코어 저장소 샘플
- IDL 스니펫·필드 ID 설명: examples/write-with-overrides/README.md
- C# 스모크: examples/consumer-csharp —
DemoUser.FieldId.Name,DemoUser.FieldId.Home등 사용