왜 ProtoBuf를 쓰는가
한 줄 정의
Protocol Buffers는 "메시지 스키마(.proto) 기반으로 직렬화 코드를 자동 생성"하는 포맷/도구 체계입니다.
게임 서버 관점 장점
| 장점 | 설명 |
|---|
| 자동 직렬화/역직렬화 | SerializeToArray, ParseFromArray |
| 다중 언어 연동 | C++ 서버 <-> C# 클라 같은 스키마 공유 |
| 스키마 진화 | 필드 추가 중심으로 하위 호환 설계 가능 |
위치
- Part 11~13의 수동 Reader/Writer 기반 포맷을 ProtoBuf로 대체/보완하는 단계입니다.
.proto 기본 규칙
예시 스키마
syntax = "proto3";
package Protocol;
message BuffData {
uint64 buff_id = 1;
float remain_time = 2;
}
message S_TEST {
uint64 id = 1;
uint32 hp = 2;
uint32 attack = 3;
repeated BuffData buffs = 4;
}
필드 번호 규칙
- 필드 번호(tag)는 메시지의 ABI입니다. 한 번 정하면 바꾸지 마세요.
- 번호 재사용은 구버전/신버전 혼선의 원인이 됩니다.
- 삭제한 필드는
reserved로 막아 재사용 실수를 방지하세요.
타입 주의
- ProtoBuf는
uint16 타입을 제공하지 않습니다.
- 필요하면
uint32를 쓰고 애플리케이션 레벨에서 범위를 검증하세요.
스키마 진화(호환성) 규칙
안전한 변경
| 변경 | 호환성 |
|---|
| 필드 추가 | 보통 안전(기본값 처리) |
| 새 메시지 추가 | 안전 |
repeated 필드 추가 | 안전(기존은 빈 리스트) |
위험/금지 변경
| 변경 | 위험 |
|---|
| 필드 번호 변경 | 기존 데이터 의미 붕괴 |
| 삭제 후 번호 재사용 | 과거 데이터가 다른 필드로 해석 |
| 타입 변경(wire type 불일치) | 파싱 실패/의미 왜곡 |
삭제 절차
message S_TEST {
reserved 5, 7;
reserved "old_field_name";
}
- 삭제한 번호/이름은 반드시
reserved로 남기는 습관을 들이세요.
C++ 직렬화/역직렬화 패턴
직렬화(송신)
Protocol::S_TEST pkt;
pkt.set_id(1000);
pkt.set_hp(100);
pkt.set_attack(10);
const int payloadSize = pkt.ByteSizeLong();
std::vector<uint8> payload(payloadSize);
if (!pkt.SerializeToArray(payload.data(), payloadSize))
{
}
역직렬화(수신)
Protocol::S_TEST pkt;
bool ok = pkt.ParseFromArray(payloadPtr, payloadLen);
if (!ok)
{
}
- 헤더(
size, id)는 엔진 프레이밍용으로 유지하고,
- payload 영역만 ProtoBuf로 serialize/parse 하는 구조가 실무에서 가장 흔합니다.
빌드/생성 파이프라인
생성 명령
protoc --cpp_out=. --proto_path=. *.proto
Windows 프로젝트 반영
protoc.exe와 libprotobuf 준비(Release/Debug 매칭)
- 생성된
.pb.h, .pb.cc를 프로젝트에 포함
- 필요 시
.pb.cc의 PCH 설정 정리
- 빌드 전 이벤트에 protoc 호출 스크립트 연결
팀 운영 팁
.proto 변경 시 생성 파일 갱신을 CI에서 검사하면 누락을 줄일 수 있습니다.
- 런타임 DLL/정적 링크 정책(Debug/Release)을 팀 규칙으로 통일하세요.
성능과 운영 포인트
성능
- 작은 메시지는 ProtoBuf 비용이 낮고, 수동 직렬화 대비 개발 생산성이 높습니다.
- 매우 핫한 경로는 프로파일링 후 Arena/객체 재사용 최적화를 검토하세요.
운영 검증
- 패킷별 parse 실패율
- 메시지별 평균 payload 크기
- 버전 불일치(ID/스키마 mismatch) 발생률
장애 대응
- Parse 실패는 "손상된 데이터 또는 버전 불일치" 신호일 수 있습니다.
- 서비스 특성에 따라 drop 또는 disconnect 정책을 명시적으로 선택하세요.
강의 시 유의사항
강조 포인트
- ProtoBuf 도입의 목적은 "빠른 개발 + 안전한 스키마 진화"입니다.
- 핵심은 API 암기가 아니라 필드 번호 관리 규칙(
reserved, 재사용 금지)입니다.
- Unity(C#) <-> C++ 서버의 계약 통합 도구로 매우 유용합니다.
자주 하는 오해
| 오해 | 바로잡기 |
|---|
| 필드 이름만 같으면 호환된다 | 실제 호환성은 필드 번호가 결정한다 |
| 필드 삭제는 그냥 지우면 끝 | 번호/이름을 reserved로 막아야 안전하다 |
| Parse 실패는 드물어서 무시 가능 | 운영에서 버전/입력 이상 조기 신호다 |
체크 질문 (스스로 답해보기)
- 왜 필드 번호 변경이 가장 위험한 스키마 변경인가?
uint16 대신 uint32를 쓰고 어떤 추가 검증을 해야 하는가?
- Parse 실패 시 drop과 disconnect 중 어떤 정책을 택할지 기준은 무엇인가?