오류

이 장에서는 Google API 오류 모델의 개요를 제공합니다. 또한 개발자가 오류를 올바르게 생성 및 처리할 수 있는 방법에 대한 일반적인 지침을 설명합니다.

Google API는 프로토콜 제약이 없는 간단한 오류 모델을 사용하기 때문에 다양한 API, API 프로토콜(예: gRPC, HTTP), 오류 컨텍스트(예: 비동기 오류, 배치 오류, 워크플로 오류) 전반에서 일관된 경험을 제공할 수 있습니다.

오류 모델

Google API의 오류 모델은 API 오류가 발생할 때 클라이언트에 반환되는 google.rpc.Status 인스턴스를 통해 논리적으로 정의됩니다. 다음 코드 스니펫은 오류 모델의 전체 디자인을 나타낸 것입니다.

package google.rpc;

// The `Status` type defines a logical error model that is suitable for
// different programming environments, including REST APIs and RPC APIs.
message Status {
  // A simple error code that can be easily handled by the client. The
  // actual error code is defined by `google.rpc.Code`.
  int32 code = 1;

  // A developer-facing human-readable error message in English. It should
  // both explain the error and offer an actionable resolution to it.
  string message = 2;

  // Additional error information that the client code can use to handle
  // the error, such as retry info or a help link.
  repeated google.protobuf.Any details = 3;
}

대부분 Google API는 리소스 중심 API 디자인을 사용하기 때문에 오류를 처리할 때도 적은 수의 표준 오류 집합을 다수의 리소스와 함께 사용하여 동일한 디자인 원칙을 따릅니다. 예를 들어 서버가 여러 종류의 'not found' 오류를 정의하지 않고 표준 google.rpc.Code.NOT_FOUND 오류 코드 하나를 사용하여 클라이언트에게 찾을 수 없는 특정 리소스를 알려줍니다. 오류 공간이 작을수록 문서 복잡성이 줄어들고, 클라이언트 라이브러리에서 더욱 효과적인 관용 매핑이 가능하고, 클라이언트 로직 복잡성이 줄어드는 동시에 실행 가능한 정보를 포함시킬 때의 제한사항이 없어집니다.

오류 코드

Google API는 google.rpc.Code를 통해 정의된 표준 오류 코드를 사용해야 합니다. 각 API에서 오류 코드를 추가로 정의해서는 안 됩니다. 개발자가 다수의 오류 코드를 처리하기 위한 로직을 작성할 가능성은 거의 없기 때문입니다. 참고로 API 1개당 처리하는 평균 오류 코드 수가 3개라고 가정할 경우 대부분 애플리케이션 로직이 오류 처리에만 사용되어 개발자에게 유용한 환경이라고 말할 수 없습니다.

오류 메시지

오류 메시지는 사용자가 API 오류를 쉽고 빠르게 이해하고 해결하는 데 효과적이어야 합니다. 일반적으로 오류 메시지를 작성할 때 다음과 같은 가이드라인을 고려해야 합니다.

  • 사용자가 API 전문가라고 가정하지 마세요. 사용자는 클라이언트 개발자, 운영 담당자, IT 부서 직원 또는 앱 최종 사용자가 될 수도 있습니다.
  • 사용자가 서비스 구현체에 대해 무엇이든 알고 있거나, 혹은 오류 컨텍스트(로그 분석 등)에 익숙하다고 가정하지 마세요.
  • 가능하다면 오류 메시지는 기술 사용자(반드시 API 개발자일 필요는 없음)가 오류에 대응하여 수정할 수 있도록 작성되어야 합니다.
  • 오류 메시지는 간략하게 작성하세요. 필요하다면 혼동을 느낀 사용자가 질문을 하거나, 의견을 제시하거나, 혹은 오류 메시지로는 알지 못하는 정보를 더 얻을 수 있도록 링크를 제공하세요. 그 밖에 펼칠 수 있는 세부정보 필드를 사용하는 것도 좋습니다.

오류 세부정보

Google API는 오류 세부정보에 대한 표준 오류 페이로드 집합을 정의하고 있습니다. 오류 세부정보는 google/rpc/error_details.proto에서 찾아볼 수 있습니다. 여기에서는 할당량 결함, 잘못된 매개변수 등 가장 공통적인 API 오류 요건에 대해서 살펴보겠습니다. 오류 코드와 마찬가지로 개발자는 가능하면 표준 페이로드를 사용해야 합니다.

오류 세부정보 유형은 애플리케이션 코드가 오류를 처리하는 데 유용한 경우에 한해 추가할 수 있습니다. 오류 정보를 사람만 처리할 수 있는 경우에는 오류 메시지 내용을 신뢰하는 동시에 추가 오류 세부정보 유형을 추가하지 말고 개발자에게 오류 처리를 맡기는 것이 좋습니다.

다음은 error_details 페이로드에 대한 몇 가지 예시입니다.

  • ErrorInfo: 안정적이면서 확장 가능한 구조화된 오류 정보를 제공합니다.
  • RetryInfo: 클라이언트가 실패한 요청을 재시도할 수 있는 시점을 설명하며 Code.UNAVAILABLE 또는 Code.ABORTED에서 반환될 수 있습니다.
  • QuotaFailure: 할당량 검사가 어떻게 실패했는지 설명하며 Code.RESOURCE_EXHAUSTED에서 반환될 수 있습니다.
  • BadRequest: 클라이언트 요청의 위반사항을 설명하며 Code.INVALID_ARGUMENT에서 반환될 수 있습니다.

오류 정보

ErrorInfo는 특수한 종류의 오류 페이로드입니다. 사람 및 애플리케이션 모두 사용할 수 있는 안정적이고 확장 가능한 오류 정보를 제공합니다. 각 ErrorInfo에는 오류 도메인, 오류 이유, 오류 메타데이터 집합(이 예시)의 세 가지 정보가 있습니다. 자세한 내용은 ErrorInfo 정의를 참조하세요.

Google API의 경우 기본 오류 도메인은 googleapis.com이며 이에 해당하는 오류 원인은 google.api.ErrorReason 열거형에 의해 정의됩니다. 자세한 내용은 google.api.ErrorReason 정의를 참조하세요.

오류 현지화

google.rpc.Statusmessage 필드는 개발자용이며 영어로 작성되어야 합니다.

사용자에게 오류 메시지를 표시해야 하는 경우 google.rpc.LocalizedMessage를 세부정보 필드로 사용합니다. google.rpc.LocalizedMessage의 메시지 필드는 현지화될 수 있지만 google.rpc.Status의 메시지 필드는 영어로 작성되어야 합니다.

기본적으로 API 서비스는 인증된 사용자의 언어 또는 HTTP Accept-Language 헤더 또는 language_code 매개변수를 요청에 사용하여 현지화 언어를 결정해야 합니다.

오류 매핑

Google API는 여러 프로그래밍 환경에서 액세스할 수 있습니다. 환경마다 일반적으로 고유한 오류 처리 방법이 있습니다. 다음 섹션에서는 일반적으로 사용되는 환경에서 오류 모델을 매핑하는 방법을 설명합니다.

HTTP 매핑

proto3 메시지가 기본 JSON 인코딩을 사용하는 반면 Google의 API 플랫폼은 하위 호환성을 이유로 Google JSON HTTP API에 대해 다른 오류 스키마를 사용합니다.

스키마:

// This message defines the error schema for Google's JSON HTTP APIs.
message Error {
  // Deprecated. This message is only used by error format v1.
  message ErrorProto {}
  // This message has the same semantics as `google.rpc.Status`. It uses HTTP
  // status code instead of gRPC status code. It has extra fields `status` and
  // `errors` for backward compatibility with [Google API Client
  // Libraries](https://developers.google.com/api-client-library).
  message Status {
    // The HTTP status code that corresponds to `google.rpc.Status.code`.
    int32 code = 1;
    // This corresponds to `google.rpc.Status.message`.
    string message = 2;
    // Deprecated. This field is only used by error format v1.
    repeated ErrorProto errors = 3;
    // This is the enum version for `google.rpc.Status.code`.
    google.rpc.Code status = 4;
    // This corresponds to `google.rpc.Status.details`.
    repeated google.protobuf.Any details = 5;
  }
  // The actual error payload. The nested message structure is for backward
  // compatibility with [Google API Client
  // Libraries](https://developers.google.com/api-client-library). It also
  // makes the error more readable to developers.
  Status error = 1;
}

예시(링크)

{
  "error": {
    "code": 400,
    "message": "API key not valid. Please pass a valid API key.",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "API_KEY_INVALID",
        "domain": "googleapis.com",
        "metadata": {
          "service": "translate.googleapis.com"
        }
      }
    ]
  }
}

gRPC 매핑

RPC 프로토콜에 따라 매핑되는 오류 모델도 다릅니다. gRPC의 경우에는 각각 지원되는 언어로 생성된 코드와 런타임 라이브러리에서 기본적으로 오류 모델이 지원됩니다. 자세한 내용은 gRPC의 API 문서를 참조하세요. 예를 들어 gRPC 자바의 io.grpc.Status를 참조하세요.

클라이언트 라이브러리 매핑

Google 클라이언트 라이브러리에서는 기존 관용구와 일관될 수 있도록 언어마다 오류를 다르게 표시할 수 있습니다. 예를 들어 google-cloud-go 라이브러리는 google.rpc.Status와 동일한 인터페이스를 구현하는 오류를 반환하는 반면 google-cloud-java는 예외를 발생시킵니다.

오류 처리

다음 표에서는 google.rpc.Code에서 정의된 모든 gRPC 오류 코드와 그 원인을 간략히 설명합니다. 오류를 처리하려면 먼저 반환되는 상태 코드에 대한 설명을 확인해야만 그에 따라 호출을 수정할 수 있습니다.

HTTP gRPC 설명
200 OK 오류가 없습니다.
400 INVALID_ARGUMENT 클라이언트가 잘못된 인수를 지정했습니다. 자세한 내용은 오류 메시지와 오류 세부정보에서 확인하세요.
400 FAILED_PRECONDITION 비어있지 않은 디렉토리를 삭제하는 등 현재 시스템 상태에서는 요청을 실행할 수 없습니다.
400 OUT_OF_RANGE 클라이언트가 잘못된 범위로 지정되었습니다.
401 UNAUTHENTICATED OAuth 토큰이 누락되었거나, 잘못되었거나, 만료되어 요청을 인증할 수 없습니다.
403 PERMISSION_DENIED 클라이언트에게 충분한 권한이 없습니다. 이 오류는 OAuth 토큰의 범위가 잘못되었거나, 클라이언트에게 권한이 없거나, API가 사용 설정되지 않았기 때문에 발생합니다.
404 NOT_FOUND 지정된 리소스를 찾을 수 없습니다.
409 ABORTED 읽기-수정-쓰기 충돌 같은 동시 실행 충돌이 발생했습니다.
409 ALREADY_EXISTS 클라이언트가 만들려고 했던 리소스가 이미 존재합니다.
429 RESOURCE_EXHAUSTED 리소스 할당량이 부족하거나 비율 한도에 도달하였습니다. 이때는 클라이언트가 google.rpc.QuotaFailure 오류 세부정보를 찾아 자세한 내용을 확인해야 합니다.
499 CANCELLED 클라이언트에서 요청을 취소하였습니다.
500 DATA_LOSS 복구할 수 없는 데이터 손실 또는 손상이 발생하였습니다. 이때는 클라이언트가 사용자에게 오류를 보고해야 합니다.
500 UNKNOWN 알 수 없는 서버 오류가 발생하였습니다. 전형적인 서버 버그입니다.
500 INTERNAL 내부 서버 오류가 발생하였습니다. 전형적인 서버 버그입니다.
501 NOT_IMPLEMENTED 서버에서 API 메소드를 구현하지 않았습니다.
502 해당 없음 서버에 도달하기 전에 네트워크 오류가 발생했습니다. 일반적으로 네트워크 중단 또는 구성 오류입니다.
503 UNAVAILABLE 서비스를 사용할 수 없습니다. 전형적인 서버 다운입니다.
504 DEADLINE_EXCEEDED 요청 기한이 지났습니다. 이 오류는 호출자가 메소드의 기본 기한보다 짧게 기한을 설정한 후(서버가 요청을 처리할 수 있을 만큼 기한이 충분히 길지 않아서) 요청이 기한 내에 완료되지 않았을 때 발생합니다.

오류 재시도

클라이언트는 지수 백오프로 UNAVAILABLE 오류 503개를 재시도할 수 있습니다. 최소 지연 시간은 달리 명시되지 않는 한 1초가 되어야 합니다. 별도로 명시하지 않는 한 기본 재시도 반복은 한 번이어야 합니다.

429 RESOURCE_EXHAUSTED 오류인 경우 클라이언트는 최소 30초까지 지연 시간을 사용하여 높은 수준에서 재시도할 수 있습니다. 이러한 재시도는 장기 실행 백그라운드 작업에만 유용합니다.

그 밖에 나머지 모든 오류에서는 재시도가 적용되지 않을 수 있습니다. 먼저 요청이 멱등성을 갖는지 확인하고 자세한 내용은 google.rpc.RetryInfo의 안내를 참조하세요.

오류 전파

API 서비스가 다른 서비스에 따라 달라지는 경우에는 오류를 맹목적으로 해당 서비스에서 클라이언트까지 전파해서는 안 됩니다. 오류를 번역할 때는 다음과 같이 권장합니다.

  • 구현 세부정보와 기밀 정보는 숨기세요.
  • 오류 책임자를 조정하세요. 예를 들어 다른 서비스에서 INVALID_ARGUMENT 오류를 수신하는 서버는 INTERNAL을 자체 호출자에게 전파해야 합니다.

오류 재현

로그 분석 및 모니터링으로 오류를 해결할 수 없다면 단순하고 반복 가능한 테스트로 오류를 재현해야 합니다. 테스트를 통해 문제 해결을 위한 추가 정보를 수집할 수 있으며 기술 지원팀에 문의할 때 이 정보를 제공할 수 있습니다.

curl -v시스템 매개변수를 사용하여 Google API 관련 오류를 재현하는 것이 좋습니다. 이를 사용하여 거의 모든 Google API 요청을 재현하고 자세한 디버그 정보를 제공할 수 있습니다. 자세한 내용은 호출하는 API에 대한 해당 문서 페이지를 참조하세요.

오류 생성

서버 개발자인 경우 클라이언트 개발자가 문제를 파악하여 해결할 수 있도록 충분한 정보와 함께 오류를 생성해야 합니다. 동시에 사용자 데이터의 보안 및 개인정보 보호에 대해 인지하고 민감한 정보가 오류 메시지와 오류 세부정보를 통해 공개되지 않도록 주의해야 합니다. 오류는 로깅되는 일이 많아서 다른 사용자에게 노출될 수 있기 때문입니다. 예를 들어 'Client IP address is not on allowlist 128.0.0.0/8'과 같은 오류 메시지는 서버 측 정책에 대한 정보를 노출하며, 이 정보는 로그에 액세스할 수 있는 사용자가 액세스하지 못할 수 있습니다.

적절한 오류를 생성하려면 먼저 각 오류 조건에 가장 적합한 오류 코드를 선택할 수 있도록 google.rpc.Code에 익숙해야 합니다. 서버 애플리케이션은 다수의 오류 조건을 동시에 확인하여 첫 번째 조건을 반환할 수 있습니다.

다음 표는 각 오류 코드와 올바른 오류 메시지의 예를 나열한 것입니다.

HTTP gRPC 오류 메시지 예
400 INVALID_ARGUMENT [yyy, zzz] 중 하나를 예상했지만 요청 필드 x,y,z가 xxx입니다.
400 FAILED_PRECONDITION 리소스 xxx는 비어있는 디렉토리가 아니라서 삭제할 수 없습니다.
400 OUT_OF_RANGE 매개변수 'age'가 범위[0, 125]에서 벗어났습니다.
401 UNAUTHENTICATED 잘못된 사용자 인증 정보입니다.
403 PERMISSION_DENIED 권한 'xxx'가 파일 'yyy'에서 거부되었습니다.
404 NOT_FOUND 리소스 'xxx'를 찾을 수 없습니다.
409 ABORTED 리소스 ‘xxx’에 대한 잠금을 획득할 수 없습니다.
409 ALREADY_EXISTS 리소스 'xxx'는 이미 존재합니다.
429 RESOURCE_EXHAUSTED 할당량 한도 'xxx'를 초과하였습니다.
499 CANCELLED 클라이언트에서 요청을 취소하였습니다.
500 DATA_LOSS 참고를 참조하세요.
500 UNKNOWN 참고를 참조하세요.
500 INTERNAL 참고를 참조하세요.
501 NOT_IMPLEMENTED 메소드 'xxx'가 구현되지 않았습니다.
503 UNAVAILABLE 참고를 참조하세요.
504 DEADLINE_EXCEEDED 참고를 참조하세요.

오류 페이로드

google.rpc 패키지는 커스텀 오류 페이로드에 우선하는 표준 오류 페이로드 집합을 정의합니다. 다음 표는 각 오류 코드를 비롯해 각 코드와 일치하는 표준 오류 페이로드(해당되는 경우)를 나열한 것입니다. 고급 애플리케이션은 오류를 처리할 때 google.rpc.Status에서 이러한 오류 페이로드를 확인하는 것이 좋습니다.

HTTP gRPC 권장되는 오류 세부정보
400 INVALID_ARGUMENT google.rpc.BadRequest
400 FAILED_PRECONDITION google.rpc.PreconditionFailure
400 OUT_OF_RANGE google.rpc.BadRequest
401 UNAUTHENTICATED google.rpc.ErrorInfo
403 PERMISSION_DENIED google.rpc.ErrorInfo
404 NOT_FOUND google.rpc.ResourceInfo
409 ABORTED google.rpc.ErrorInfo
409 ALREADY_EXISTS google.rpc.ResourceInfo
429 RESOURCE_EXHAUSTED google.rpc.QuotaFailure
499 CANCELLED
500 DATA_LOSS google.rpc.DebugInfo
500 UNKNOWN google.rpc.DebugInfo
500 INTERNAL google.rpc.DebugInfo
501 NOT_IMPLEMENTED
503 UNAVAILABLE google.rpc.DebugInfo
504 DEADLINE_EXCEEDED google.rpc.DebugInfo