absl::Status payload — 구조화된 에러 컨텍스트
한 줄 요약:
absl::Status의 payload는 메시지 외에 구조화된 데이터를 첨부하는 메커니즘이다. URL-based key로 namespace를 분리해 라이브러리 간 충돌을 막고, gRPC error_details와의 연동을 위해 설계되었다.
#어떤 문제를 푸는가
에러 메시지만으로는 부족한 경우가 있다.
return absl::FailedPreconditionError( "rate limit exceeded; retry after 5 seconds; user=alice");- 읽기 어려움 — 사람이 파싱해야 함.
- 자동 처리 불가 — UI가 “5초 후 재시도” 카운트다운 표시 못 함.
- 확장 어려움 — 새 정보 추가하려면 메시지 형식 합의 필요.
구조화된 데이터가 필요하다. Status payload가 그것이다.
auto s = absl::FailedPreconditionError("rate limit exceeded");s.SetPayload( "type.googleapis.com/google.rpc.RetryInfo", SerializeRetryInfo({.retry_after = absl::Seconds(5)}));// 호출자는 payload를 꺼내 자동 처리 가능#API
class Status {public: // payload 설정 void SetPayload(absl::string_view type_url, absl::Cord payload);
// payload 조회 absl::optional<absl::Cord> GetPayload(absl::string_view type_url) const;
// 모든 payload 순회 void ForEachPayload( absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor ) const;
// payload 제거 bool ErasePayload(absl::string_view type_url);};absl::Cord는 큰 string을 효율적으로 다루는 type. payload는 보통 직렬화된 protobuf다.
#URL-based key
key가 string이 아니라 URL인 것이 핵심이다.
// 잘 정의된 namespace"type.googleapis.com/google.rpc.RetryInfo""type.googleapis.com/google.rpc.DebugInfo""type.googleapis.com/google.rpc.QuotaFailure"
// 사용자 정의"myorg.example.com/MyError""github.com/myproject/internal/error_type"이유는 충돌 방지. 라이브러리 A가 “rate_limit”이라는 key를 쓰고 라이브러리 B도 같은 key를 쓰면 곱하기. URL은 자연스럽게 namespace를 분리한다.
Google은 type.googleapis.com/<protobuf message name> 형식을 표준으로 정했다. protobuf의 Any type과 같은 컨벤션.
#gRPC error_details와의 통합
gRPC는 status에 구조화된 에러 정보를 담는 방법으로 google.rpc.Status를 정의한다. details 필드가 임의의 protobuf를 담는다.
message Status { int32 code = 1; string message = 2; repeated google.protobuf.Any details = 3;}Abseil의 Status payload는 이 details와 1
// gRPC에서 받은 status를 Abseil status로 변환absl::Status FromGrpcStatus(const grpc::Status& gs) { absl::Status s(static_cast<absl::StatusCode>(gs.error_code()), gs.error_message());
// details를 payload로 옮김 google::rpc::Status proto_status; if (proto_status.ParseFromString(gs.error_details())) { for (const auto& detail : proto_status.details()) { s.SetPayload(detail.type_url(), absl::Cord(detail.value())); } } return s;}#표준 protobuf 메시지
Google이 제공하는 표준 error detail은 google.rpc.error_details 정의에 있다.
| Type URL | 용도 |
|---|---|
RetryInfo | 재시도 권장 시간 |
DebugInfo | 디버깅용 stack trace, detail |
QuotaFailure | quota 초과 항목 목록 |
PreconditionFailure | 전제 위반의 종류 |
BadRequest | 잘못된 필드 목록 |
RequestInfo | 원래 요청 식별자 |
ResourceInfo | 영향받은 리소스 |
Help | 도움말 링크 |
LocalizedMessage | 사용자에게 보여줄 다국어 메시지 |
이 protobuf들을 사용하면 gRPC ecosystem 전체와 호환된다.
#사용 예시
#RetryInfo
#include "google/rpc/error_details.pb.h"
absl::Status MakeRateLimitError() { auto s = absl::ResourceExhaustedError("rate limit exceeded");
google::rpc::RetryInfo retry; retry.mutable_retry_delay()->set_seconds(5); s.SetPayload( "type.googleapis.com/google.rpc.RetryInfo", absl::Cord(retry.SerializeAsString()));
return s;}
void HandleError(const absl::Status& s) { auto retry_data = s.GetPayload("type.googleapis.com/google.rpc.RetryInfo"); if (retry_data) { google::rpc::RetryInfo retry; retry.ParseFromString(std::string(*retry_data)); absl::Duration delay = absl::Seconds(retry.retry_delay().seconds()) + absl::Nanoseconds(retry.retry_delay().nanos()); // 자동 재시도 스케줄링 ScheduleRetry(delay); }}#BadRequest — 필드 검증
absl::Status ValidateUser(const User& u) { if (u.name().empty() && u.email().empty()) { google::rpc::BadRequest bad; if (u.name().empty()) { auto* v = bad.add_field_violations(); v->set_field("name"); v->set_description("must not be empty"); } if (u.email().empty()) { auto* v = bad.add_field_violations(); v->set_field("email"); v->set_description("must not be empty"); }
auto s = absl::InvalidArgumentError("validation failed"); s.SetPayload("type.googleapis.com/google.rpc.BadRequest", absl::Cord(bad.SerializeAsString())); return s; } return absl::OkStatus();}UI는 어느 필드가 잘못됐는지 정확히 알 수 있다.
#내부 구현
// 의사 코드class StatusRep { StatusCode code; std::string message;
// payload는 보통 없음 — error의 일부 시나리오에서만 std::unique_ptr<absl::flat_hash_map<std::string, absl::Cord>> payloads;};payload는 lazy. 첫 SetPayload 호출 시점에 hash map 할당. payload가 없는 status는 추가 메모리 비용 없음.
이 lazy 구조는 “대부분의 error는 payload 없음”이라는 관측을 활용한다. payload는 RPC boundary나 자동 처리가 필요한 특수 케이스에서만 쓰이고, 일상적인 validation error에는 message만 충분하다.
#코드 리뷰 포인트
// 회피 — 그냥 string으로 구조화된 정보 인코딩return absl::FailedPreconditionError( absl::StrCat("retry_after=", 5, "; user=", user));
// Good — payload 사용auto s = absl::FailedPreconditionError("rate limit");google::rpc::RetryInfo r;r.mutable_retry_delay()->set_seconds(5);s.SetPayload("type.googleapis.com/google.rpc.RetryInfo", absl::Cord(r.SerializeAsString()));return s;// 회피 — URL 형식 안 지킴s.SetPayload("my_error", payload);// 충돌 위험. 다른 라이브러리도 "my_error"를 쓸 수 있음.
// Good — namespace 명시s.SetPayload("myorg.example.com/MyError", payload);// 회피 — payload에 raw binary 직접 넣기s.SetPayload("...", absl::Cord(reinterpret_cast<const char*>(&data), sizeof(data)));// portability, versioning, language compatibility 문제.
// Good — protobuf 또는 다른 명시적 직렬화s.SetPayload("...", absl::Cord(my_message.SerializeAsString()));리뷰에서:
- 메시지에 구조화 정보를 인코딩하지 않았는가 — payload로 옮길 것.
- URL이 namespace 분리되어 있는가.
- payload가 protobuf 같은 portable 형식인가.
#자주 보는 안티패턴
// 회피 — payload를 너무 자주 사용return absl::NotFoundError("user not found").SetPayload(...);// 단순한 NotFound는 message만으로 충분.// payload는 자동 처리가 필요할 때만.// 회피 — payload type을 caller마다 다르게// Server A: SetPayload("retry_info_v1", ...);// Server B: SetPayload("RetryInfo", ...);// 표준 URL을 모르고 각자 정의.
// Good — Google 표준 URL 사용"type.googleapis.com/google.rpc.RetryInfo"// 회피 — payload를 message에도 중복 인코딩auto s = absl::InvalidArgumentError( absl::StrCat("validation failed: field=", field));s.SetPayload("...", SerializeBadRequest({field}));// message는 사람용 요약, payload는 기계용 구조. 분리.
// Goodauto s = absl::InvalidArgumentError("validation failed");s.SetPayload("...", SerializeBadRequest({field}));#std와의 비교
표준 C++에는 비교할 만한 것이 없다. std::error_code에는 message조차 첨부할 수 없다. std::exception은 message 하나뿐.
언어 비교:
| 언어 | 메커니즘 |
|---|---|
| Rust | Error::source() chain |
| Go | errors.Wrap, error.As() |
| Java | exception chain |
| Python | __cause__, custom attributes |
| C++ (Abseil) | URL-keyed payload map |
Abseil의 URL key는 protobuf Any type에서 차용. cross-language portability를 노린 설계다.
#정리
absl::Status::SetPayload는 메시지 외에 구조화된 데이터를 첨부.- key는 URL 형식. namespace 충돌 방지.
- gRPC
error_details와 1 매핑. - Google이 표준 error_details protobuf 제공 (RetryInfo, BadRequest 등).
- 일상적인 에러에는 불필요. RPC boundary, 자동 처리 시에 사용.
#다음 편
Part 3-05에서 Status와 exception 사이 변환을 본다. exception을 던지는 라이브러리와 Status 기반 코드를 어떻게 묶고, gRPC status / std::error_code와는 어떻게 변환하는지.
#관련 항목
Abseil Code Review · 18 of 79
- 1 Abseil Code Review — Google production-grade C++ 라이브러리 분석
- 2 Abseil 개요 — Google이 std를 보완한 이유
- 3 Abseil 설계 철학 — std 호환과 추가 기능의 균형
- 4 Abseil 빌드와 의존성 — Bazel vs CMake
- 5 Abseil LTS vs HEAD 릴리스 모델 분석
- 6 Abseil Versioning과 ABI 호환성 정책
- 7 Abseil 매크로 — ABSL_HAVE_*·ABSL_ATTRIBUTE_*
- 8 Abseil ABSL_PREDICT_TRUE/FALSE — branch hint
- 9 absl::LogSeverity — 로그 레벨 타입
- 10 Abseil type_traits — negation·conjunction·void_t
- 11 Abseil Conformance·Policy 분석
- 12 Abseil Memory utilities 분석
- 13 Abseil raw_logging — heap-free 로깅
- 14 Abseil thread_annotations — clang TSA 통합
- 15 absl::Status — exception-free error handling
- 16 absl::StatusOr<T> — 값 또는 에러
- 17 absl status_macros — ASSIGN_OR_RETURN·RETURN_IF_ERROR
- 18 absl::Status payload — 구조화된 에러 컨텍스트
- 19 absl::Status ↔ exception 변환 패턴
- 20 absl::string_view — non-owning 문자열 참조
- 21 absl::string_view 함정 — dangling·c_str·임시 객체
- 22 absl::StrCat — 가변 인자 문자열 연결과 AlphaNum
- 23 absl::StrSplit — Delimiter·Predicate·컨테이너 변환
- 24 absl::StrJoin — 컨테이너 결합과 Formatter
- 25 absl::StrFormat — type-safe printf·FormatSpec
- 26 Abseil ASCII 함수 — locale-free 분류·대소문자 변환
- 27 Abseil Escape — CEscape·HexEscape·Base64
- 28 absl::flat_hash_map — Swiss Table 기반 hash map
- 29 absl::flat_hash_set — set 버전 Swiss Table
- 30 absl::node_hash_map — stable pointer가 필요할 때
- 31 absl::btree_map — sorted·cache-friendly B-tree
- 32 absl::FixedArray — 런타임 크기 stack 배열
- 33 absl::InlinedVector — small buffer optimization
- 34 Abseil Swiss Table internals — control byte·SIMD probing
- 35 absl::Mutex — reader-writer·fairness·deadlock 검출
- 36 absl::Mutex Conditional Critical Section — Await로 cv 없애기
- 37 absl::Notification — once-only signal
- 38 absl::BlockingCounter·Barrier — 다중 thread 조율
- 39 absl::Mutex annotations — clang thread-safety로 race를 컴파일 타임에
- 40 absl::Time·Duration 분석 — 단단한 type
- 41 absl::Time Format·Parse
- 42 absl::CivilTime 분석
- 43 absl::time_zone 분석
- 44 absl::Time mocking — 테스트 친화 시간
- 45 absl::BitGen — 모던 난수 생성기
- 46 Abseil Random Distributions — Uniform·Exponential
- 47 Abseil Mocking Random — 테스트 결정성
- 48 Abseil Random Seeding·Entropy
- 49 absl::int128·uint128 분석
- 50 absl::bits — popcount·countl_zero
- 51 absl::optional vs std::optional
- 52 absl::variant 분석
- 53 absl::span 분석
- 54 absl::any 분석
- 55 absl::compare — three-way 비교
- 56 Abseil utility — apply·in_place
- 57 Abseil AbslHashValue 분석
- 58 Abseil HashState chaining
- 59 Abseil Custom hashable 구현
- 60 Abseil LOG·VLOG·CHECK 분석
- 61 Abseil LogSink 분석
- 62 Abseil LogEntry·structured logging
- 63 Abseil Stack trace·failure_signal_handler
- 64 ABSL_FLAG 정의 분석
- 65 Abseil ParseCommandLine 동작
- 66 Abseil Flag introspection·validation
- 67 Google 스타일의 Abseil 사용 패턴
- 68 Abseil 자주 보는 anti-pattern
- 69 std → absl 마이그레이션 전략
- 70 absl::Cleanup — 함수 종료 시 실행 보장
- 71 Abseil algorithm container 확장 — c_sort·c_find_if·c_count_if
- 72 absl::function_ref와 any_invocable — 함수 객체 전달의 두 축
- 73 absl::bind_front와 Overload — 함수 객체 보조 도구
- 74 absl::Cord — 분산 시스템용 대용량 문자열
- 75 absl::from_chars·SimpleAtoi — 빠른 숫자 변환
- 76 absl::Cord vs std::string — 선택 기준과 메모리 프로파일
- 77 absl::GetStackTrace와 Symbolize — crash 시 readable stack
- 78 absl::ComputeCrc32c — 하드웨어 가속 체크섬
- 79 absl::PeriodicSampler — 적응형 샘플링·jitter 회피
관련 글
absl::Status ↔ exception 변환 패턴
Part 3-05: Status와 exception/std::error_code/gRPC status 사이 변환 — 라이브러리 경계에서의 안전한 처리.
같은 시리즈에서 이어 읽기
absl::GetStackTrace와 Symbolize — crash 시 readable stack
absl::GetStackTrace로 PC 배열을 받고 absl::Symbolize로 함수 이름·파일·라인으로 변환. signal handler 안에서도 동작하는 async-safe API.
같은 시리즈에서 이어 읽기
absl status_macros — ASSIGN_OR_RETURN·RETURN_IF_ERROR
Part 3-03: RETURN_IF_ERROR와 ASSIGN_OR_RETURN — 에러 전파를 한 줄로. 매크로 expansion 분석과 안전한 사용법.
같은 시리즈에서 이어 읽기