프로토콜 버퍼

Jaemyeong Lee·2025년 3월 23일

게임 서버1

목록 보기
153/220

왜 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)
{
    // 파싱 실패: 로그 후 drop/disconnect 정책 적용
}

PacketHeader와 결합

  • 헤더(size, id)는 엔진 프레이밍용으로 유지하고,
  • payload 영역만 ProtoBuf로 serialize/parse 하는 구조가 실무에서 가장 흔합니다.

빌드/생성 파이프라인

생성 명령

protoc --cpp_out=. --proto_path=. *.proto

Windows 프로젝트 반영

  1. protoc.exelibprotobuf 준비(Release/Debug 매칭)
  2. 생성된 .pb.h, .pb.cc를 프로젝트에 포함
  3. 필요 시 .pb.cc의 PCH 설정 정리
  4. 빌드 전 이벤트에 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 중 어떤 정책을 택할지 기준은 무엇인가?

profile
李家네_공부방

0개의 댓글