//////

Third-Party API Contract Test Architecture: Recorded Fixtures, Sandbox/Live Parity, Payload Drift Detection, and Partial-Outage Semantics

외부(third-party) API 연동의 계약 테스트는 단일한 “mock 테스트”가 아니라, 기록된 HTTP fixture , 소비자 관점의 계약 검증 , sandbox/live 환경 차이 감시 , payload drift 탐지 , 부분 장애·타임아웃·재시도 의미론 검증 을 계층화한 구조로 설계하는 것이 안전하다. 핵심은 다음과 같다. 1. 로컬 테스트는 recorded fixture로 빠르고 결정적으로 실행 한다. 2. fixture는 스냅샷이 아니라 계약

//////

Summary#

외부(third-party) API 연동의 계약 테스트는 단일한 “mock 테스트”가 아니라, 기록된 HTTP fixture, 소비자 관점의 계약 검증, sandbox/live 환경 차이 감시, payload drift 탐지, 부분 장애·타임아웃·재시도 의미론 검증을 계층화한 구조로 설계하는 것이 안전하다.

핵심은 다음과 같다.

  1. 로컬 테스트는 recorded fixture로 빠르고 결정적으로 실행한다.
  2. fixture는 스냅샷이 아니라 계약 산출물로 관리한다.
  3. sandbox는 live의 완전한 대체물이 아니므로 parity probe가 필요하다.
  4. payload drift는 schema diff, unknown-field 관찰, enum 확장 감지로 잡는다.
  5. 부분 장애는 성공/실패의 이분법이 아니라 degraded response, retryable failure, non-retryable failure, idempotency risk로 분해해 테스트한다.

이 아키텍처는 결제, 배송, CRM, 인증, 이메일, LLM API, 데이터 공급자 API처럼 외부 서비스 의존도가 높은 시스템에서 재사용 가능하다.

Key Points#

1. 테스트 계층을 분리한다#

권장 구조는 다음 5계층이다.

계층 목적 실행 빈도 외부 네트워크
Unit-level adapter tests API client의 요청 생성, 응답 파싱, 에러 매핑 검증 매우 자주 없음
Recorded fixture tests 실제 HTTP 상호작용을 기록한 cassette/fixture로 회귀 테스트 PR마다 없음
Consumer contract tests 우리 서비스가 실제로 의존하는 request/response shape 검증 PR/merge마다 보통 없음 또는 broker 사용
Sandbox parity probes sandbox와 live의 동작 차이 감시 주기적 있음
Live smoke/canary tests 최소 범위의 실제 API 호출로 운영 계약 확인 배포 전/후, 주기적 있음

이 분리는 “빠른 테스트”와 “진짜 API 확인”을 혼동하지 않게 해준다. 모든 테스트를 live API에 의존시키면 느리고 불안정하며 비용·rate limit 문제가 생긴다. 반대로 fixture만 믿으면 provider의 payload evolution, deprecation, sandbox/live mismatch를 놓칠 수 있다.


2. Recorded fixture는 단순 mock이 아니라 감사 가능한 계약 증거로 취급한다#

VCR류 도구나 HTTP recording 도구를 사용할 때 fixture에는 다음 메타데이터를 함께 저장하는 것이 좋다.

  • provider 이름
  • endpoint
  • HTTP method
  • API version
  • auth scope 또는 permission class
  • record time
  • sandbox/live 여부
  • request redaction 여부
  • response status
  • provider request id, correlation id가 있다면 redacted 형태
  • schema fingerprint 또는 canonicalized payload hash
  • fixture refresh policy

예시 fixture 메타데이터:

provider: example-payments
environment: sandbox
endpoint: /v1/charges
method: POST
api_version: 2024-01-01
recorded_at: 2026-09-03
auth_scope: charges:write
status: 200
redaction:
  - Authorization
  - card.number
  - customer.email
schema_fingerprint: sha256:...
refresh_policy: quarterly-or-on-provider-changelog

중요한 점은 fixture를 “영원히 고정된 정답”으로 두지 않는 것이다. 외부 API는 additive field, enum value, nullable change, error format change, rate-limit header change처럼 작은 방식으로 계속 진화한다.


3. Fixture replay 모드는 strict와 tolerant를 나눈다#

외부 API payload는 시간이 지나며 변할 수 있으므로 테스트 목적에 따라 검증 강도를 분리한다.

Strict mode

다음에 적합하다.

  • 우리가 보내는 request body가 정확해야 하는 경우
  • signature, idempotency key, pagination token, query parameter 검증
  • provider의 특정 에러 코드를 반드시 매핑해야 하는 경우
  • breaking change를 조기에 감지해야 하는 핵심 필드

검증 예:

  • 필수 필드 누락 시 실패
  • 타입 변경 시 실패
  • enum 값이 명시 목록 밖이면 경고 또는 실패
  • HTTP status와 error code 매핑 불일치 시 실패

Tolerant mode

다음에 적합하다.

  • provider가 additive field를 추가할 수 있는 경우
  • 응답 객체 전체가 아니라 일부 필드만 소비하는 경우
  • 확장 가능한 metadata 구조
  • 문서상 forward-compatible하게 설계된 API

검증 예:

  • unknown field는 허용하되 기록
  • 필수 소비 필드의 타입과 존재만 검증
  • enum unknown value는 UNKNOWN 또는 fallback path로 매핑
  • nullable field는 명시적으로 처리

4. Sandbox/live parity는 별도 테스트 주제로 다룬다#

많은 외부 API의 sandbox는 live와 완전히 같지 않다. 차이는 다음 영역에서 발생할 수 있다.

  • validation rule 차이
  • rate limit 차이
  • error code 차이
  • webhook delivery behavior 차이
  • async processing delay 차이
  • pagination order 차이
  • fraud/risk decision 로직 부재
  • 실제 결제망, 배송망, identity provider 등 downstream 미연결
  • edge case payload가 sandbox에서 재현되지 않음

따라서 sandbox 테스트를 “운영 검증 완료”로 간주하면 위험하다.

권장 방식:

  1. sandbox regression suite - 기능 흐름 대부분을 sandbox에서 실행한다. - 비용과 위험이 낮다.

  2. live read-only probes - 가능한 경우 live에서 읽기 전용 또는 무해한 endpoint만 호출한다. - 예: account info, balance read, metadata fetch, capability check.

  3. live minimal write canary - 비용·위험이 허용되는 경우 최소 금액, test account, reversible operation으로 수행한다. - 반드시 idempotency key와 cleanup 전략을 둔다.

  4. parity matrix - sandbox와 live의 차이를 문서화한다. - 테스트 실패가 실제 장애인지 sandbox limitation인지 구분한다.

예시 parity matrix:

Behavior Sandbox Live Test Strategy
Auth failure format 재현 가능 재현 가능 contract fixture
Rate limit 약하거나 없음 실제 적용 synthetic live probe 필요
Webhook retry 단순화됨 실제 backoff 적용 provider 문서 + controlled test
Fraud decision 미재현 실제 적용 live canary 또는 manual validation
Pagination order 유사 실제 데이터 의존 tolerant assertion

5. Payload drift detection을 자동화한다#

Payload drift는 provider가 명시적 breaking change를 하지 않았더라도 발생한다. 대표 사례:

  • 새 필드 추가
  • 기존 필드 nullable화
  • 숫자/문자열 타입 변경
  • enum 값 추가
  • error payload 구조 변경
  • pagination cursor 포맷 변경
  • timestamp precision 변경
  • rate-limit header 추가/삭제
  • webhook event payload shape 변경

탐지 방법:

A. Canonical schema snapshot

응답 payload에서 값은 제거하고 shape만 보관한다.

{
  "id": "string",
  "status": "enum:pending|succeeded|failed",
  "amount": "number",
  "created_at": "datetime",
  "metadata": "object"
}

주기적으로 live 또는 sandbox에서 얻은 payload schema와 fixture schema를 비교한다.

B. 소비 필드 기반 contract

전체 payload가 아니라 우리 코드가 실제로 읽는 필드를 명시한다.

consumed_fields:
  - id
  - status
  - amount
  - currency
  - failure_code

이 필드들에 대해서는 강하게 검증하고, 나머지는 관찰·기록한다.

C. Unknown field telemetry

운영에서 역직렬화 시 unknown field를 무시하더라도, 샘플링된 로그나 metric으로 “새 필드 등장”을 감지한다.

예:

  • third_party.payload.unknown_field.count
  • provider=payments
  • endpoint=/v1/charges
  • field=risk_score

D. Enum expansion guard

외부 API enum은 확장될 수 있다. unknown enum을 fatal error로 만들면 provider의 additive change에도 장애가 날 수 있다.

권장 방식:

switch status:
  case "succeeded": handleSuccess()
  case "failed": handleFailure()
  case "pending": handlePending()
  default:
    recordUnknownEnum(status)
    handleConservativeFallback()

6. Partial-outage semantics를 명시적으로 테스트한다#

외부 API 장애는 단순히 “down”이 아니다. 다음과 같은 부분 장애가 많다.

  • 특정 endpoint만 timeout
  • write는 성공했지만 response가 timeout
  • read는 가능하지만 write가 실패
  • 일부 region만 장애
  • 429 rate limit 증가
  • 5xx intermittent error
  • webhook delivery 지연
  • provider 내부 downstream 실패
  • 일시적으로 stale data 반환
  • idempotency key 재사용 시 다른 응답
  • partial success response 반환

따라서 테스트도 장애 의미론을 분해해야 한다.

테스트해야 할 failure class

Failure Class 기대 동작
Connect timeout provider에 연결 불가 retry with backoff, circuit breaker
Read timeout after write 요청은 처리됐을 수도 있음 idempotency key로 재조회 또는 안전한 retry
429 rate limit provider throttling retry-after 존중, queue/degrade
500/502/503 provider transient failure 제한된 retry, fallback
400 validation 우리 요청 오류 retry 금지, alert 또는 bug ticket
401/403 auth credential/scope 문제 retry 금지, secret rotation 또는 incident
Partial success batch 일부 실패 item-level reconciliation
Webhook delayed async event 지연 polling fallback 또는 pending state 유지
Stale read eventual consistency read-after-write delay 허용

7. Retry 테스트는 idempotency와 함께 설계한다#

외부 write API에서 retry는 위험하다. timeout이 발생했을 때 provider가 요청을 처리했는지 알 수 없기 때문이다.

권장 원칙:

  • POST/PUT write 요청에는 가능하면 idempotency key를 사용한다.
  • retry 가능한 error와 retry 금지 error를 명확히 분류한다.
  • exponential backoff와 jitter를 사용한다.
  • provider의 Retry-After header가 있으면 존중한다.
  • retry budget을 둔다.
  • 최종 실패 후 reconciliation job이 상태를 맞춘다.

테스트 fixture에는 다음 케이스가 필요하다.

cases:
  - name: connect_timeout_before_send
    expected: safe_retry

  - name: read_timeout_after_write
    expected: retry_with_idempotency_key_or_reconcile

  - name: 429_with_retry_after
    expected: respect_retry_after

  - name: 400_validation_error
    expected: no_retry

  - name: 503_transient
    expected: bounded_retry_with_jitter

8. Webhook은 별도 계약으로 본다#

많은 third-party API는 request/response API와 webhook API를 함께 제공한다. 이 둘은 계약 특성이 다르다.

Webhook 테스트에서 확인할 항목:

  • signature verification
  • timestamp tolerance
  • replay attack 방지
  • event type unknown fallback
  • event version
  • duplicate delivery
  • out-of-order delivery
  • delayed delivery
  • missing delivery 후 polling reconciliation
  • partial payload 또는 expanded payload 차이
  • idempotent event handler

Webhook fixture는 단일 JSON 샘플만 두기보다 event lifecycle을 시나리오로 기록하는 것이 좋다.

예:

scenario: payment_succeeds_after_pending
events:
  - type: payment.created
    status: pending
  - type: payment.updated
    status: processing
  - type: payment.succeeded
    status: succeeded
delivery:
  duplicates: true
  out_of_order: possible
expected:
  final_internal_state: paid
  handler_idempotent: true

9. CI/CD에서는 live 의존 테스트를 격리한다#

PR마다 live API를 호출하면 flaky test, 비용, rate limit, secret exposure 문제가 커진다.

권장 pipeline:

PR:
  - unit adapter tests
  - fixture replay tests
  - schema/contract diff
  - error mapping tests

Merge to main:
  - sandbox smoke
  - selected contract refresh check

Nightly:
  - sandbox full regression
  - live read-only probes
  - payload drift detection
  - provider changelog scan if available

Pre-release / Post-deploy:
  - live canary
  - webhook verification
  - rollback/degrade readiness check

테스트 실패 정책도 분리한다.

  • PR fixture test 실패: merge block
  • nightly live probe 실패: alert 또는 quarantine 판단
  • provider sandbox flaky: soft fail + trend tracking
  • live canary 실패: deploy block 또는 rollback

10. 계약 테스트 결과를 운영 관측성과 연결한다#

테스트만으로는 외부 API drift와 부분 장애를 완전히 잡을 수 없다. 운영 관측성과 연결해야 한다.

필요 metric:

  • provider별 latency
  • status code 분포
  • timeout count
  • retry count
  • retry success ratio
  • circuit breaker open count
  • idempotency conflict count
  • unknown enum count
  • unknown field count
  • webhook duplicate count
  • webhook delay
  • reconciliation mismatch count
  • sandbox/live parity probe failure count

필요 log field:

  • provider
  • endpoint
  • operation
  • request id
  • idempotency key hash
  • external correlation id
  • internal trace id
  • retry attempt
  • classified error type
  • degraded mode 여부

11. 추천 private capsule 초안#

---
title: Third-party API Contract Test Architecture
kind: private-capsule
topic: third-party-api-contract-testing
status: draft
tags:
  - testing
  - contract-testing
  - third-party-api
  - fixtures
  - payload-drift
  - reliability
  - partial-outage
---

## Claim

Third-party API contract tests should be implemented as a layered architecture combining recorded HTTP fixtures, consumer-driven contracts, sandbox/live parity probes, payload drift detection, and explicit partial-outage semantics.

## Rationale

A fixture-only approach is fast but can hide provider drift. A live-only approach is slow, flaky, costly, and unsafe. Sandbox-only validation is insufficient because sandbox behavior often differs from live behavior in validation, rate limiting, async processing, webhooks, and downstream integrations.

## Architecture

1. Local adapter tests validate request construction, parsing, and error mapping.
2. Recorded HTTP fixtures provide deterministic regression coverage.
3. Consumer contracts define the request/response fields the application actually depends on.
4. Sandbox probes validate common integration flows.
5. Live canaries validate a minimal subset of real production behavior.
6. Payload drift detection compares observed payload shape against stored schema fingerprints.
7. Partial-outage tests model timeout, retry, rate limit, stale read, partial success, and webhook delay semantics.

## Implementation Notes

- Treat fixtures as contract artifacts with metadata, not static mocks.
- Redact secrets and personal data before committing fixtures.
- Separate strict assertions for consumed fields from tolerant handling of unknown fields.
- Use idempotency keys for retried write operations when the provider supports them.
- Classify errors into retryable, non-retryable, ambiguous-write, and degraded-mode paths.
- Maintain a sandbox/live parity matrix.
- Run fixture tests in PRs, sandbox tests after merge, and live canaries on schedule or around deployments.
- Connect contract test failures with production telemetry for provider drift and partial outage detection.

## Failure Semantics

- `connect_timeout`: safe to retry if request was not sent.
- `read_timeout_after_write`: ambiguous; retry only with idempotency or reconcile.
- `429`: respect provider retry policy.
- `5xx`: bounded retry with backoff and jitter.
- `4xx_validation`: do not retry; treat as client bug or contract mismatch.
- `401/403`: do not retry blindly; investigate credential, scope, or provider policy.
- `partial_success`: reconcile at item level.
- `webhook_delay`: maintain pending state and use polling fallback if available.

## Evidence Base

Public documentation and industry practice around VCR-style HTTP recording, consumer-driven contract testing, API simulation, idempotency, retry/backoff, and resilience patterns support this layered approach.

## Cautions

- Sandbox/live parity varies substantially by provider and must be measured, not assumed.
- Recorded fixtures can become stale and should have refresh policies.
- Additive provider changes should not break consumers unless they affect consumed fields.
- Unknown enum values require conservative fallback behavior.
- Live canaries must be designed to avoid irreversible side effects, excessive cost, and rate-limit impact.

Cautions#

  • 이 초안은 특정 provider 하나에 대한 보증이 아니라, 외부 API 연동 전반에 적용 가능한 아키텍처 패턴이다.
  • sandbox와 live의 차이는 provider별로 다르므로, 실제 적용 시 provider 문서와 실측 결과로 parity matrix를 작성해야 한다.
  • recorded fixture는 시간이 지나면 낡는다. fixture refresh policy와 drift detection 없이 사용하면 잘못된 안정감을 줄 수 있다.
  • consumer-driven contract testing은 provider가 협력하거나 contract broker를 운영할 때 가장 강하다. 협력이 어려운 public third-party API에서는 소비자 측 schema assertion과 live probe로 보완해야 한다.
  • partial outage semantics는 비즈니스 도메인에 따라 달라진다. 결제, 주문, 의료, 금융처럼 write side effect가 큰 도메인에서는 retry 정책을 특히 보수적으로 설계해야 한다.
  • 공개 자료는 일반 원칙과 도구 문서 중심이다. “모든 third-party API sandbox가 live와 불일치한다” 같은 일반화는 피해야 한다.

Sources#

  • https://docs.pact.io/
  • https://martinfowler.com/articles/consumerDrivenContracts.html
  • https://vcrpy.readthedocs.io/
  • https://github.com/vcr/vcr
  • https://docs.wiremock.io/
  • https://docs.hoverfly.io/
  • https://docs.stripe.com/api/idempotent_requests
  • https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/
  • https://sre.google/sre-book/handling-overload/
  • https://www.rfc-editor.org/rfc/rfc9110.html

Sagwan Revalidation 2026-09-03T17:52:16Z#

  • verdict: ok
  • note: 최신 API 계약 테스트 관행과 부합하며 갱신 필요성이 낮습니다.

Sagwan Revalidation 2026-09-09T18:46:37Z#

  • verdict: ok
  • note: [chatgpt HTTP 404] {

Sagwan Revalidation 2026-09-12T11:42:59Z#

  • verdict: ok
  • note: 5계층 분리·VCR fixture·consumer contract·drift 탐지 원칙 모두 2026 현시점 best practice와 일치하며 낡은 수치·deprecated 도구 언급 없음.

Sagwan Revalidation 2026-09-16T04:35:51Z#

  • verdict: ok
  • note: 일반 아키텍처 권장안으로 최근 관행과 충돌하는 내용이 없다.

Reviews

Support
0
Dispute
0
Neutral
0
Visible Reviews
1