오류

이번 장에서는 Google API 오류 모델에 대해서 간략히 언급한 후 개발자가 오류를 올바르게 생성하여 처리할 수 있는 방법에 대한 일반 지침을 설명합니다.

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

오류 모델

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

package google.rpc;

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 delay 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 페이로드의 예입니다.

  • RetryInfo 클라이언트가 실패한 요청을 재시도할 수 있는 시점을 설명하며 다음 경우에 반환될 수 있음: Code.UNAVAILABLE 또는 Code.ABORTED
  • QuotaFailure 할당량 검사가 어떻게 실패했는지 설명하며 다음 경우에 반환될 수 있음: Code.RESOURCE_EXHAUSTED
  • BadRequest 클라이언트 요청에서 위반사항을 설명하며 다음 경우에 반환될 수 있음: Code.INVALID_ARGUMENT

HTTP 매핑

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

스키마:

// The error schema for Google REST APIs. NOTE: this schema is not used for
// other wire protocols.
message Error {
  // This message has the same semantics as `google.rpc.Status`. It has an extra
  // field `status` for backward compatibility with Google API Client Library.
  message Status {
    // This corresponds to `google.rpc.Status.code`.
    int32 code = 1;
    // This corresponds to `google.rpc.Status.message`.
    string message = 2;
    // 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. It also makes the error
  // more readable to developers.
  Status error = 1;
}

예를 들면 다음과 같습니다.

{
  "error": {
    "code": 401,
    "message": "Request had invalid credentials.",
    "status": "UNAUTHENTICATED",
    "details": [{
      "@type": "type.googleapis.com/google.rpc.RetryInfo",
      ...
    }]
  }
}

RPC 매핑

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

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

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

오류 현지화

google.rpc.Statusmessage 필드는 개발자에게 표시되며, 영어로 작성되어야 합니다.

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

기본적으로 API 서비스는 인증된 사용자의 언어 또는 HTTP Accept-Language 헤더를 사용해 현지화 언어를 결정해야 합니다.

오류 처리

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

HTTP RPC 설명
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 메소드를 구현하지 않았습니다.
503 UNAVAILABLE 서비스를 사용할 수 없습니다. 전형적인 서버 다운입니다.
504 DEADLINE_EXCEEDED 요청 기한이 지났습니다. 이 오류는 호출자가 메소드의 기본 기한보다 짧게 기한을 설정한 후(서버가 요청을 처리할 수 있을 만큼 기한이 충분히 길지 않아서) 요청이 기한 내에 완료되지 않았을 때 발생합니다.

오류 재시도

클라이언트는 지수 백오프를 사용해 500 및 503 오류를 재시도해야 합니다. 최소 지연 시간은 달리 명시되지 않는 한 1초가 되어야 합니다. 429 오류일 때는 클라이언트가 최소 30초까지 지연 시간을 이용해 재시도할 수 있습니다. 그 밖에 나머지 오류에서는 재시도가 적용되지 않을 수도 있습니다. 먼저 요청이 멱등성을 갖는지 확인한 후 오류 메시지에서 지침을 참조하세요.

오류 전파

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

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

오류 생성

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

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

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

HTTP RPC 오류 메시지 예
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.DebugInfo 오류 세부정보만 생성하는 것이 좋습니다. DebugInfo는 서버 측 로깅에서만 사용하도록 특별히 설계되었으므로 클라이언트에 전송해서는 안 됩니다.

google.rpc 패키지는 커스텀 오류 페이로드보다 많이 사용되는 표준 오류 페이로드의 집합을 정의합니다. 다음 표는 각 오류 코드를 비롯해 각 코드와 일치하는 표준 오류 페이로드(해당되는 경우)를 나열한 것입니다.

HTTP RPC 권장되는 오류 세부정보
400 INVALID_ARGUMENT google.rpc.BadRequest
400 FAILED_PRECONDITION google.rpc.PreconditionFailure
400 OUT_OF_RANGE google.rpc.BadRequest
401 UNAUTHENTICATED
403 PERMISSION_DENIED
404 NOT_FOUND google.rpc.ResourceInfo
409 ABORTED
409 ALREADY_EXISTS google.rpc.ResourceInfo
429 RESOURCE_EXHAUSTED google.rpc.QuotaFailure
499 CANCELLED
500 DATA_LOSS
500 UNKNOWN
500 INTERNAL
501 NOT_IMPLEMENTED
503 UNAVAILABLE
504 DEADLINE_EXCEEDED
이 페이지가 도움이 되었나요? 평가를 부탁드립니다.

다음에 대한 의견 보내기...