내 mock server가 나에게 거짓말을 했어요. 그래서 stateful API sandbox를 만들었습니다.

발행: (2026년 3월 31일 오전 08:26 GMT+9)
7 분 소요
원문: Dev.to

Source: Dev.to

FetchSandbox

무상태 목업의 문제점

지난 달에 결제 API와 통합 작업을 했습니다. 목업 서버를 대상으로 테스트를 작성했고, 모든 테스트가 통과했으며, 스테이징에 배포했지만 전체 흐름이 깨졌습니다.

목업은 POST /charges{"id": "ch_123"}를 반환한다고 알려줍니다. 실제로도 그렇게 반환합니다. 그런데 제 코드는 상태를 확인하기 위해 GET /charges/ch_123를 호출했는데, 목업은 404를 반환했습니다. 목업은 실제로 무언가를 저장하지 않기 때문입니다. 각 요청은 독립된 우주에 존재합니다.

이 일에 반나절을 허비했습니다. 그리고 이것이 처음이 아니었습니다.

저는 Prism, WireMock, Mockoon을 사용해 보았습니다 — 이들 모두 훌륭한 도구입니다. OpenAPI 사양을 지정하면 응답을 자동으로 생성해 주지만, 응답은 정해져 있습니다. 요청 간에 메모리가 없습니다:

POST /customers → 201 {"id": "cust_123"}
GET /customers/cust_123 → 404   # 방금 만든 것을 알지 못함

HTTP 클라이언트를 테스트하는 단위 테스트에서는 잘 작동합니다. 하지만 다단계 흐름이 등장하는 순간 무너집니다.

실제 Stripe 통합이 어떻게 이루어지는지 생각해 보세요:

  1. 고객 생성
  2. 해당 고객을 위한 결제 의도 생성 (1단계에서 얻은 고객 ID 필요)
  3. 결제 의도 확인 (2단계에서 얻은 PI ID 필요)
  4. 웹훅 발생 (서버에서 이를 처리해야 함)

목업 서버는 2‑4단계를 수행할 수 없습니다. ID가 전달되지 않으며, 웹훅도 발생하지 않습니다. 결국 환상적인 상황을 테스트하고 있는 겁니다.

실제로 필요했던 것

나는 다음과 같은 샌드박스가 필요했습니다:

  • POST가 실제 리소스를 생성하고 나중에 GET으로 조회할 수 있음
  • 요청 간에 ID가 연쇄적으로 연결되어 프로덕션 환경과 동일하게 동작함
  • 상태 전환이 정상적으로 이루어짐 (pending 상태에서 succeeded 상태로 전환)
  • 변화가 있을 때 웹훅이 발동함

요컨대 – 모의(mock) 서버가 아니라 실제 API와 동일하게 동작하는 아주 작은 가짜 버전이 필요했습니다.

So I built one

저는 몇 달 동안 **FetchSandbox**에 몰두해 왔습니다. OpenAPI 사양을 제공하면 시드 데이터, 상태 머신, 웹훅 이벤트가 포함된 상태 저장 샌드박스를 생성합니다.

npm install -g fetchsandbox

fetchsandbox generate ./stripe-openapi.yaml
# ✓ Sandbox ready: 587 endpoints, 63 seed records

fetchsandbox run stripe --all
# ✓ Accept a payment — 3/3 steps passed
# ✓ Onboard a connected account — 3/3 steps passed
# ✓ Respond to a dispute — 2/2 steps passed
# ✓ All workflows passed — 3/3 (9ms)

run --all 명령은 제가 원하던 바로 그 기능입니다. 모든 통합 워크플로우를 끝까지 실행하며—리소스를 생성하고, 단계 간에 ID를 연결하며, 각 응답을 검증합니다. 문제가 발생하면 어느 단계에서 왜 실패했는지 정확히 확인할 수 있습니다.

Source:

구축하면서 놀랐던 점

오류 시나리오는 정상 흐름보다 더 어려웠다

--scenario 플래그를 추가해서 전체 샌드박스를 “auth_failure” 모드로 전환하고 어떤 일이 일어나는지 확인할 수 있습니다:

fetchsandbox run stripe accept_payment --scenario auth_failure
# ✗ Step 1: POST /v1/payment_intents → 401 Unauthorized
# Scenario "auth_failure" correctly caused failure.
# Scenario reset to default.

내 코드에는 결제‑intent 엔드포인트에서 401을 처리하지 못하고 고객 엔드포인트에서만 처리하는 버그가 있었습니다. 일반적인 모킹으로는 절대 잡히지 않았을 겁니다.

웹훅은 끝없는 구멍이었다

실제 Stripe 연동에서는 로직의 절반이 웹훅 핸들러에 들어갑니다. 이제 샌드박스는 리소스가 변할 때마다 웹훅 이벤트를 발생시키며, 실시간으로 확인할 수 있습니다:

fetchsandbox webhook-listen stripe
# 12:04:31  payment_intent.created  pi_xyz  → requires_confirmation
# 12:04:32  payment_intent.succeeded pi_xyz → succeeded

상태를 살펴보는 것이 과소평가된다

워크플로를 실행한 뒤, 샌드박스에 정확히 어떤 데이터가 있는지 확인할 수 있습니다:

fetchsandbox state stripe customers
# customers — 3 records
# ┌──────────────┬─────────────────┬──────────┐
# │ id           │ email           │ status   │
# ├──────────────┼─────────────────┼──────────┤
# │ cust_abc123  │ test@acme.com   │ active   │
# └──────────────┴─────────────────┴──────────┘

대안과 비교했을 때

기능Mock server (Prism)Vendor sandbox (Stripe test mode)FetchSandbox
설정 시간1 min15‑30 min (account + keys)타사 API에 대한 다른 사람들의 테스트 설정이 어떻게 생겼는지 궁금합니다.

모든 것을 모킹하시나요? 벤더 테스트 모드를 사용하시나요? 혹은 혼합형? 댓글을 남겨 주세요 — 저는 이 문제에 몇 달째 몰두하고 있으며 아직도 배우고 있습니다.

0 조회
Back to Blog

관련 글

더 보기 »