API 오류 이해

이 가이드에서는 Data Manager API가 오류를 처리하고 전달하는 방법을 설명합니다. API 오류의 구조와 의미를 이해하는 것은 잘못된 입력부터 일시적인 서비스 사용 불가에 이르기까지 문제를 원활하게 처리할 수 있는 강력한 애플리케이션을 빌드하는 데 매우 중요합니다.

Data Manager API는 gRPC 상태 코드를 기반으로 하는 표준 Google API 오류 모델을 따릅니다. 오류가 발생하는 각 API 응답에는 다음이 포함된 Status 객체가 포함됩니다.

  • 숫자 오류 코드입니다.
  • 오류 메시지입니다.
  • 추가 오류 세부정보(선택사항)입니다.

표준 오류 코드

Data Manager API는 gRPC 및 HTTP에서 정의한 표준 오류 코드 집합을 사용합니다. 이러한 코드는 오류 유형을 대략적으로 나타냅니다. 문제의 근본적인 성격을 이해하려면 항상 이 코드를 먼저 확인해야 합니다.

이러한 코드에 대한 자세한 내용은 API 디자인 가이드 - 오류 코드를 참조하세요.

빠른 실패 모델

Data Manager API는 빠른 실패 모델을 사용합니다. 요청에 구조적 오류가 포함되어 있거나 레코드의 필수 필드 유효성 검사가 실패하면 전체 요청이 실패하고 API는 해당 요청의 데이터를 처리하지 않습니다.

부분 실패 모델과의 비교

빠른 실패 모델은 Google Ads API 및 Campaign Manager 360 API와 같은 다른 Google API의 부분 실패 모델과 다릅니다. 부분 실패 모델에서는 일부 레코드에 오류가 있더라도 요청이 성공하고 응답에 실패한 레코드의 오류 세부정보가 포함됩니다.

부분 실패는 편리할 수 있지만 부분 실패 모델은 오류를 사전 경고하지 않으므로 각 응답에서 오류를 명시적으로 확인해야 하는 상당한 위험이 있습니다. API가 요청의 레코드 중 다수 또는 전부를 거부하더라도 요청이 성공하기 때문에 중요한 문제가 가려질 수 있습니다. 요청의 상당 부분에 오류가 있지만 응답을 검사하지 않으면 데이터의 광범위한 문제를 전혀 인식하지 못할 수 있으며, 누적 결과가 예상과 일치하지 않는 경우 며칠 또는 몇 주 후에야 이러한 문제를 발견할 수 있습니다.

빠른 실패 모델은 데이터 또는 통합 문제를 즉시 알려주므로 적절한 조치를 취할 수 있어 이러한 문제를 방지할 수 있습니다.

validateOnly로 빠른 실패 오류 확인

대부분의 처리 및 삭제 요청은 validateOnly 필드를 지원합니다. validateOnly를 true로 설정하면 Data Manager API는 일반 요청과 동일한 기본 유효성 검사를 실행하지만 데이터를 수집하거나 삭제하지 않습니다.

  • 요청에 오류가 있으면 일반 요청에서 수신하는 것과 동일한 오류 응답과 함께 실패합니다.
  • 요청이 검증을 통과하면 성공합니다. 응답에는 일반 요청과 마찬가지로 선택사항 필드의 fieldWarnings가 포함됩니다.

validateOnly를 사용하여 다음을 수행합니다.

  • 실시간 데이터에 영향을 주지 않고 신규 또는 업데이트된 통합을 테스트합니다.
  • 요청을 다시 보내기 전에 수정사항이 오류를 해결하는지 확인합니다.

오류 처리

요청이 실패하면 다음 단계를 따르세요.

  1. 오류 코드를 확인하여 오류 유형을 찾습니다.

    • gRPC를 사용하는 경우 오류 코드는 Status의 code 필드에 있습니다. 클라이언트 라이브러리를 사용하는 경우 오류 코드에 해당하는 특정 유형의 예외가 발생할 수 있습니다. 예를 들어 자바용 클라이언트 라이브러리는 오류 코드가 INVALID_ARGUMENT인 경우 com.google.api.gax.rpc.InvalidArgumentException를 발생시킵니다.
    • REST를 사용하는 경우 오류 코드는 error.status의 오류 응답에 있고 해당 HTTP 상태는 error.code에 있습니다.
  2. 오류 코드의 표준 세부정보 페이로드를 확인합니다. 표준 세부정보 페이로드는 Google API의 오류 메시지 집합입니다. 구조화되고 일관된 방식으로 오류 세부정보를 제공합니다. Data Manager API의 각 오류에는 여러 표준 세부정보 페이로드 메시지가 있을 수 있습니다. Data Manager API 클라이언트 라이브러리에는 오류에서 표준 세부정보 페이로드를 가져오는 도우미 메서드가 있습니다.

    오류 코드와 관계없이 ErrorInfo, RequestInfo, Help, LocalizedMessage 페이로드를 확인하고 로깅하는 것이 좋습니다.

    • ErrorInfo에는 다른 페이로드에 없을 수 있는 정보가 있습니다.
    • RequestInfo에는 요청 ID가 있으며, 이는 지원팀에 문의해야 하는 경우에 유용합니다.
    • Help 및 LocalizedMessage에는 오류를 해결하는 데 도움이 되는 링크와 기타 세부정보가 포함되어 있습니다.

    또한 BadRequest 페이로드는 오류를 일으킨 필드에 대한 정보를 제공하므로 INVALID_ARGUMENT 오류에 유용합니다.

수집 경고

Data Manager API는 최대한 많은 수집 요청을 허용합니다. 필수적이지 않은 데이터를 포함하는 경우 해당 필드의 검증 실패로 인해 요청이 실패하지 않습니다. 예를 들어 장바구니 항목에 판매자 제품 ID가 누락된 경우 API는 나머지 요청을 처리하고 경고를 반환합니다.

성공적인 수집 응답 (HTTP 상태 코드 200)에는 fieldWarnings 목록에 이러한 경고가 포함됩니다. 각 항목은 다음 필드가 있는 FieldWarning 객체입니다.

field

요청 내 필드의 위치이며 스네이크 표기법 경로 문법으로 표시됩니다.

경로가 목록의 항목 (repeated 필드)을 가리키는 경우 색인은 목록 이름 뒤에 대괄호 ([...])로 표시됩니다.

예를 들어 events.events[0].cart_data.items[0].merchant_product_id은 요청의 첫 번째 이벤트에 있는 장바구니 데이터의 첫 번째 항목과 관련된 경고를 식별합니다.

description

제공된 값으로 인해 경고가 발생한 이유에 대한 설명입니다.

reason

경고 유형을 식별하는 WarningReason enum 값입니다.

FieldWarning를 사용한 예시

다음은 장바구니 항목 중 하나에 판매자 제품 ID가 누락되어 경고가 포함된 성공적인 수집 요청의 샘플 응답입니다.

{
  "requestId": "126365e1-16d0-4c81-9de9-f362711e250a",
  "fieldWarnings": [
    {
      "field": "events.events[0].cart_data.items[0].merchant_product_id",
      "description": "The merchant product ID is missing in the cart item.",
      "reason": "WARNING_REASON_CART_DATA_ITEM_MERCHANT_PRODUCT_ID_MISSING"
    }
  ]
}

표준 세부정보 페이로드

Data Manager API의 가장 일반적인 표준 세부정보 페이로드는 다음과 같습니다.

BadRequest

INVALID_ARGUMENT (HTTP 상태 코드 400)로 요청이 실패하면 BadRequest 페이로드를 확인합니다.

BadRequest 메시지는 요청에 잘못된 값이 있는 필드가 있거나 필수 필드의 값이 누락되었음을 나타냅니다. BadRequest의 field_violations 목록을 확인하여 오류가 있는 필드를 찾습니다. 각 field_violations 항목에는 오류를 수정하는 데 도움이 되는 정보가 있습니다.

field

요청 내 필드의 위치이며 스네이크 표기법 경로 문법으로 표시됩니다.

경로가 목록의 항목 (repeated 필드)을 가리키는 경우 색인은 목록 이름 뒤에 대괄호 ([...])로 표시됩니다.

예를 들어 destinations[0].operating_account.account_id는 destinations 목록의 첫 번째 항목에 있는 operating_account의 account_id입니다.

description

값으로 인해 오류가 발생한 이유에 대한 설명입니다.

reason

ErrorReason enum(예: INVALID_HEX_ENCODING 또는 INVALID_CURRENCY_CODE)

BadRequest의 예문

다음은 BadRequest 메시지가 있는 INVALID_ARGUMENT 오류의 샘플 응답입니다. field_violations는 오류가 숫자가 아닌 accountId임을 보여줍니다. field 값 destinations[0].login_account.account_id은 필드 위반이 있는 accountId가 destinations 목록의 첫 번째 항목의 login_account에 있음을 보여줍니다.

{
  "error": {
    "code": 400,
    "message": "There was a problem with the request.",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "INVALID_ARGUMENT",
        "domain": "datamanager.googleapis.com",
        "metadata": {
          "requestId": "t-a8896317-069f-4198-afed-182a3872a660"
        }
      },
      {
        "@type": "type.googleapis.com/google.rpc.RequestInfo",
        "requestId": "t-a8896317-069f-4198-afed-182a3872a660"
      },
      {
        "@type": "type.googleapis.com/google.rpc.BadRequest",
        "fieldViolations": [
          {
            "field": "destinations[0].login_account.account_id",
            "description": "String is not a valid number.",
            "reason": "INVALID_NUMBER_FORMAT"
          }
        ]
      }
    ]
  }
}

다음은 BadRequest 메시지가 있는 INVALID_ARGUMENT 오류의 또 다른 샘플 응답입니다. 이 경우 field_violations 목록에 두 가지 오류가 표시됩니다.

  1. 첫 번째 event에는 이벤트의 두 번째 사용자 식별자에 16진수로 인코딩되지 않은 값이 있습니다.

  2. 두 번째 event에는 이벤트의 세 번째 사용자 식별자에 16진수로 인코딩되지 않은 값이 있습니다.

{
  "error": {
    "code": 400,
    "message": "There was a problem with the request.",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "INVALID_ARGUMENT",
        "domain": "datamanager.googleapis.com",
        "metadata": {
          "requestId": "t-6bc8fb83-d648-4942-9c49-2604276638d8"
        }
      },
      {
        "@type": "type.googleapis.com/google.rpc.RequestInfo",
        "requestId": "t-6bc8fb83-d648-4942-9c49-2604276638d8"
      },
      {
        "@type": "type.googleapis.com/google.rpc.BadRequest",
        "fieldViolations": [
          {
            "field": "events.events[0].user_data.user_identifiers[1]",
            "description": "The HEX encoded value is malformed.",
            "reason": "INVALID_HEX_ENCODING"
          },
          {
            "field": "events.events[1].user_data.user_identifiers[2]",
            "description": "The HEX encoded value is malformed.",
            "reason": "INVALID_HEX_ENCODING"
          }
        ]
      }
    ]
  }
}

RequestInfo

요청이 실패할 때마다 RequestInfo 페이로드를 확인합니다. RequestInfo에는 API 요청을 고유하게 식별하는 request_id가 포함됩니다.

{
  "@type": "type.googleapis.com/google.rpc.RequestInfo",
  "requestId": "t-4490c640-dc5d-4c28-91c1-04a1cae0f49f"
}

오류를 로깅하거나 지원팀에 문의할 때 문제 진단에 도움이 되도록 요청 ID를 포함해야 합니다.

ErrorInfo

ErrorInfo 메시지를 확인하여 다른 표준 세부정보 페이로드에서 캡처되지 않을 수 있는 추가 정보를 검색합니다. ErrorInfo 페이로드에는 오류에 대한 정보가 포함된 metadata 맵이 포함됩니다.

예를 들어 다음은 Data Manager API가 사용 설정되지 않은 Google Cloud 프로젝트의 사용자 인증 정보를 사용하여 발생한 PERMISSION_DENIED 실패의 ErrorInfo입니다. ErrorInfo는 오류에 대한 추가 정보를 제공합니다. 예를 들면 다음과 같습니다.

  • metadata.consumer 아래의 요청과 연결된 프로젝트입니다.
  • metadata.serviceTitle 아래의 서비스 이름입니다.
  • metadata.activationUrl에서 서비스를 사용 설정할 수 있는 URL입니다.
{
  "error": {
    "code": 403,
    "message": "Data Manager API has not been used in project PROJECT_NUMBER before or it is disabled. Enable it by visiting https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.",
    "status": "PERMISSION_DENIED",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "SERVICE_DISABLED",
        "domain": "googleapis.com",
        "metadata": {
          "consumer": "projects/PROJECT_NUMBER",
          "service": "datamanager.googleapis.com",
          "containerInfo": "PROJECT_NUMBER",
          "serviceTitle": "Data Manager API",
          "activationUrl": "https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER"
        }
      },
      ...
    ]
  }
}

할당량 및 비율 제한 오류

요청이 프로젝트 한도를 초과하면 API는 RESOURCE_EXHAUSTED 오류 (HTTP 상태 코드 429)를 반환합니다. ErrorInfo 페이로드는 metadata 맵에서 초과된 한도에 대한 세부정보를 제공합니다.

consumer
요청과 연결된 Google Cloud 프로젝트이며 projects/PROJECT_NUMBER 형식입니다.
quota_limit
초과된 할당량 한도의 이름입니다(예: IngestionMutateRequestsPerMinutePerProject 또는 IngestionMutateRequestsPerDayPerProject). 이 값을 사용하여 애플리케이션이 분당 한도 또는 일일 한도를 초과했는지 확인할 수 있습니다. 한도 이름의 전체 목록은 프로젝트 한도를 참조하세요.
quota_location
할당량이 적용되는 위치입니다. Data Manager API의 경우 항상 global입니다.
quota_metric
한도와 연결된 측정항목입니다(예: datamanager.googleapis.com/ingestion_mutate_requests).
service
서비스 이름(datamanager.googleapis.com)입니다.

다음은 요청이 수집 변형 요청의 분당 한도를 초과하는 경우의 RESOURCE_EXHAUSTED 오류 응답 예시입니다.

{
  "error": {
    "code": 429,
    "message": "Quota exceeded for quota metric 'Ingestion mutate requests' and limit 'Ingestion mutate requests per minute' of service 'datamanager.googleapis.com' for consumer 'project_number:PROJECT_NUMBER'.",
    "status": "RESOURCE_EXHAUSTED",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "RATE_LIMIT_EXCEEDED",
        "domain": "googleapis.com",
        "metadata": {
          "consumer": "projects/PROJECT_NUMBER",
          "quota_limit": "IngestionMutateRequestsPerMinutePerProject",
          "quota_location": "global",
          "quota_metric": "datamanager.googleapis.com/ingestion_mutate_requests",
          "service": "datamanager.googleapis.com"
        }
      }
    ]
  }
}

Help 및 LocalizedMessage

Help 및 LocalizedMessage 페이로드를 확인하여 오류를 이해하고 수정하는 데 도움이 되는 문서 및 현지화된 오류 메시지 링크를 가져옵니다.

예를 들어 다음은 Data Manager API가 사용 설정되지 않은 Google Cloud 프로젝트의 사용자 인증 정보를 사용하여 발생한 PERMISSION_DENIED 실패의 Help 및 LocalizedMessage입니다. Help 페이로드는 서비스를 사용 설정할 수 있는 URL을 표시하고 LocalizedMessage에는 오류에 대한 설명이 있습니다.

{
  "error": {
    "code": 403,
    "message": "Data Manager API has not been used in project PROJECT_NUMBER before or it is disabled. Enable it by visiting https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.",
    "status": "PERMISSION_DENIED",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.LocalizedMessage",
        "locale": "en-US",
        "message": "Data Manager API has not been used in project PROJECT_NUMBER before or it is disabled. Enable it by visiting https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry."
      },
      {
        "@type": "type.googleapis.com/google.rpc.Help",
        "links": [
          {
            "description": "Google API Console API activation",
            "url": "https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER"
          }
        ]
      },
      ...
    ]
  }
}

액세스 오류 세부정보

클라이언트 라이브러리 중 하나를 사용하는 경우 도우미 메서드를 사용하여 표준 세부정보 페이로드를 가져옵니다.

.NET

try {
    // Send API request
}
catch (Grpc.Core.RpcException rpcException)
{
    Console.WriteLine($"Exception encountered: {rpcException.Message}");
    var statusDetails =
        Google.Api.Gax.Grpc.RpcExceptionExtensions.GetAllStatusDetails(
            rpcException
        );
    foreach (var detail in statusDetails)
    {
        if (detail is Google.Rpc.BadRequest)
        {
            Google.Rpc.BadRequest badRequest = (Google.Rpc.BadRequest)detail;
            foreach (
                BadRequest.Types.FieldViolation? fieldViolation in badRequest.FieldViolations
            )
            {
                // Access attributes such as fieldViolation!.Reason and fieldViolation!.Field
            }
        }
        else if (detail is Google.Rpc.RequestInfo)
        {
            Google.Rpc.RequestInfo requestInfo = (Google.Rpc.RequestInfo)detail;
            string requestId = requestInfo.RequestId;
            // Log the requestId...
        }
        else if (detail is Google.Rpc.ErrorInfo)
        {
            Google.Rpc.ErrorInfo errorInfo = (Google.Rpc.ErrorInfo)detail;
            // Log the errorInfo.Reason and errorInfo.Metadata...

            // If handling a rate limit error, check the exceeded quota limit:
            if (errorInfo.Reason == "RATE_LIMIT_EXCEEDED" &&
                errorInfo.Metadata.TryGetValue("quota_limit", out string quotaLimit))
            {
                // Inspect quotaLimit to determine whether it is a per-minute
                // or daily limit (for example,
                // IngestionMutateRequestsPerMinutePerProject).
            }

            // Log the details in the 'Metadata' map...
            foreach (
                KeyValuePair<String, String> metadataEntry in errorInfo.Metadata
            )
            {
                // Log the metadataEntry.Key and metadataEntry.Value...
            }
        }
        else
        {
            // ...
        }
    }
}

자바

try {
  // Send API request
} catch (com.google.api.gax.rpc.InvalidArgumentException invalidArgumentException) {
  // Gets the standard BadRequest payload from the exception.
  BadRequest badRequest = invalidArgumentException.getErrorDetails().getBadRequest();
  for (int i = 0; i < badRequest.getFieldViolationsCount(); i++) {
    FieldViolation fieldViolation = badRequest.getFieldViolations(i);
    // Access attributes such as fieldViolation.getField() and fieldViolation.getReason()
  }

  // Gets the standard RequestInfo payload from the exception.
  RequestInfo requestInfo = invalidArgumentException.getErrorDetails().getRequestInfo();
  if (requestInfo != null) {
    String requestId = requestInfo.getRequestId();
    // Log the requestId...
  }
} catch (com.google.api.gax.rpc.ApiException apiException) {
  // Fallback exception handler for other types of ApiException.

  // Gets the standard ErrorInfo payload from the exception.
  ErrorInfo errorInfo = apiException.getErrorDetails().getErrorInfo();
  // Log the 'reason' and 'domain'...

  // If handling a rate limit error, check the exceeded quota limit:
  if (errorInfo != null && "RATE_LIMIT_EXCEEDED".equals(errorInfo.getReason())) {
    String quotaLimit = errorInfo.getMetadataMap().get("quota_limit");
    // Inspect quotaLimit to determine whether it is a per-minute
    // or daily limit (for example,
    // IngestionMutateRequestsPerMinutePerProject).
  }

  // Log the details in the 'metadata' map...
  for (Entry<String, String> metadataEntry : errorInfo.getMetadataMap().entrySet()) {
    // Log the metadataEntry key and value...
  }

  // Gets the standard RequestInfo payload from the exception.
  RequestInfo requestInfo = apiException.getErrorDetails().getRequestInfo();
  if (requestInfo != null) {
    String requestId = requestInfo.getRequestId();
    // Log the requestId...
  }
  ...
}

gRPC의 헤더 크기 오류 처리

수집 또는 삭제 요청의 많은 레코드가 검증에 실패하면 Data Manager API는 응답의 BadRequest 페이로드에 모든 오류를 반환합니다.

gRPC에서 서비스는 HTTP/2 응답 헤더(메타데이터)에 이러한 오류 세부정보를 반환합니다. 기본적으로 gRPC 클라이언트 라이브러리는 수신 헤더를 최대 크기 (일반적으로 8~10KB)로 제한합니다. 오류 세부정보가 이 한도를 초과하면 클라이언트 측 HTTP/2 전송이 오류 응답을 파싱하기 전에 실패하여 다음과 같은 예외가 발생합니다.

Http2Exception: Header size exceeded max allowed size (10240)

클라이언트가 응답 헤더를 처리할 수 없으므로 스트림을 취소하고(서버 측 로그에 HTTP 상태 499 Client Closed Request로 표시됨) 애플리케이션이 BadRequest 오류 세부정보에 액세스할 수 없습니다.

이 문제를 해결하고 전체 오류 목록을 검사하려면 최대 수신 메타데이터 크기 (예: 16MB)를 늘려 gRPC 클라이언트를 구성합니다.

.NET

// Creates a client for the ingestion service. Increases the maximum metadata size to
// 16MB to prevent RESOURCE_EXHAUSTED errors when responses contain large error
// details.
IngestionServiceClient ingestionServiceClient = new IngestionServiceClientBuilder
{
    GrpcChannelOptions = GrpcChannelOptions.Empty.WithCustomOption(
        "grpc.max_metadata_size",
        16 * 1024 * 1024
    ),
}.Build();

자바

// Creates a client for the ingestion service. Increases the maximum metadata size to 16MB to
// prevent RESOURCE_EXHAUSTED errors when responses contain large error details.
InstantiatingGrpcChannelProvider provider =
    IngestionServiceSettings.defaultGrpcTransportProviderBuilder()
        .setMaxInboundMetadataSize(16 * 1024 * 1024)
        .build();
IngestionServiceSettings settings =
    IngestionServiceSettings.newBuilder().setTransportChannelProvider(provider).build();
try (IngestionServiceClient ingestionServiceClient = IngestionServiceClient.create(settings)) {
  // ...
}

노드

// Creates a client for the ingestion service. Increases the maximum metadata
// size to 16MB to prevent RESOURCE_EXHAUSTED errors when responses contain
// large error details.
const client = new IngestionServiceClient({
  'grpc.max_metadata_size': 16 * 1024 * 1024,
});

PHP

// Creates a client for the ingestion service. Increases the maximum metadata size to 16MB to
// prevent RESOURCE_EXHAUSTED errors when responses contain large error details.
$client = new IngestionServiceClient([
    'transportConfig' => [
        'grpc' => [
            'stubOpts' => [
                'grpc.max_metadata_size' => 16 * 1024 * 1024,
            ],
        ],
    ],
]);

Python

# Creates a client for the ingestion service. Increases the maximum
# metadata size to 16MB to prevent RESOURCE_EXHAUSTED errors when responses
# contain large error details.
channel = datamanager_v1.services.ingestion_service.transports.IngestionServiceGrpcTransport.create_channel(
    options=[("grpc.max_metadata_size", 16 * 1024 * 1024)]
)
transport = datamanager_v1.services.ingestion_service.transports.IngestionServiceGrpcTransport(
    channel=channel
)
client: datamanager_v1.IngestionServiceClient = (
    datamanager_v1.IngestionServiceClient(transport=transport)
)

오류 처리 권장사항

복원력이 우수한 애플리케이션을 빌드하려면 다음 권장사항을 구현하세요.

전송 전 유효성 검사
통합을 빌드하거나 변경할 때 데이터를 수집하기 전에 validateOnly를 true로 설정하여 요청을 전송하면 빠른 실패 오류를 포착할 수 있습니다.
오류 세부정보 검사
항상 BadRequest와 같은 표준 세부정보 페이로드 중 하나를 찾습니다. 각 표준 세부정보 페이로드에는 오류의 원인을 이해하는 데 도움이 되는 정보가 포함되어 있습니다.
클라이언트 오류와 서버 오류 구분

오류가 구현 (클라이언트) 문제로 인해 발생했는지 아니면 API (서버) 문제로 인해 발생했는지 확인합니다.

  • 클라이언트 오류: INVALID_ARGUMENT, NOT_FOUND, PERMISSION_DENIED, FAILED_PRECONDITION, UNAUTHENTICATED과 같은 코드 이러한 오류는 요청 또는 애플리케이션의 상태/사용자 인증 정보를 변경해야 합니다. 문제를 해결하지 않은 상태로 요청을 다시 시도하지 마세요.
  • 서버 오류: UNAVAILABLE, INTERNAL, DEADLINE_EXCEEDED, UNKNOWN과 같은 코드입니다. 이는 API 서비스에 일시적인 문제가 있음을 나타냅니다.
재시도 전략 구현

오류를 다시 시도할 수 있는지 확인하고 재시도 전략을 사용합니다.

  • 일시적인 서버 오류 (예: UNAVAILABLE, DEADLINE_EXCEEDED, INTERNAL, UNKNOWN, ABORTED) 및 분당 비율 제한 (RATE_LIMIT_EXCEEDED가 있는 RESOURCE_EXHAUSTED)에 대해서만 다시 시도합니다.

  • 비율 제한의 경우 ErrorInfo의 quota_limit를 검사합니다.

    • 한도가 분당 (예: IngestionMutateRequestsPerMinutePerProject)인 경우 요청을 일시중지하고 지터가 있는 지수 백오프를 사용하여 다시 시도합니다.

    • 한도가 일일 (예: IngestionMutateRequestsPerDayPerProject)인 경우 즉시 다시 시도하지 마세요. 일일 할당량이 태평양 표준시 자정에 재설정될 때까지 처리를 일시중지합니다.

  • 지수 백오프 알고리즘을 사용하여 재시도 간 대기 시간을 늘립니다. 이렇게 하면 이미 과부하가 걸린 서비스에 과부하가 걸리는 것을 방지할 수 있습니다. 예를 들어 1초, 2초, 4초 동안 대기한 후 최대 재시도 횟수 또는 총 대기 시간까지 계속합니다.

  • 백오프 지연에 소량의 무작위 '지터'를 추가하여 여러 클라이언트가 동시에 재시도하는 'Thundering Herd' 문제를 방지합니다.

철저한 로깅

모든 표준 세부정보 페이로드, 특히 요청 ID를 포함한 전체 오류 응답을 로깅합니다. 이 정보는 필요한 경우 디버깅하고 Google 지원팀에 문제를 신고하는 데 필수적입니다.

사용자 의견 제공

표준 세부정보 페이로드의 코드와 메시지를 기반으로 애플리케이션 사용자에게 명확하고 유용한 의견을 제공합니다. 예를 들어 '오류가 발생했습니다' 대신 '거래 ID가 누락되었습니다' 또는 '대상 계정 ID를 찾을 수 없습니다'라고 표시할 수 있습니다.

이 가이드라인을 따르면 Data Manager API에서 반환하는 오류를 효과적으로 진단하고 처리하여 더 안정적이고 사용자 친화적인 애플리케이션을 만들 수 있습니다.