DEV Community

Cover image for 스트라이프 웹훅 CI에서 Apidog으로 캡처 및 검증하는 방법
Rihpig
Rihpig

Posted on • Originally published at apidog.com

스트라이프 웹훅 CI에서 Apidog으로 캡처 및 검증하는 방법

고객이 결제하면 Stripe는 백엔드로 payment_intent.succeeded 이벤트를 전송합니다. 이 이벤트를 처리해 주문을 결제 완료로 전환해야 합니다. 웹훅은 비동기적으로 도착하므로 핸들러 오류나 배포 문제를 놓치기 쉽습니다. 따라서 CI에서 이벤트가 도착했고, 올바르게 처리됐으며, 주문 상태까지 변경됐는지 검증해야 합니다.

지금 Apidog를 사용해 보세요

문제는 웹훅이 일반 API 요청과 반대 방향이라는 점입니다. 테스트 도구는 보통 요청을 보내고 응답을 검증하지만, Stripe 웹훅은 Stripe가 애플리케이션으로 HTTP 요청을 보냅니다. 이 글에서는 Apidog에서 Stripe 이벤트를 직접 수신하지 않고도 CI에서 검증하는 방법을 설명합니다. 먼저 이벤트 기반 엔드포인트의 기본 테스트 전략이 필요하다면 웹훅 테스트 방법을 참고하세요. Stripe의 전달 모델은 Stripe 웹훅 문서에서 확인할 수 있습니다.

먼저 알아야 할 제약 조건

Apidog 공식 문서에 따르면 Apidog는 웹훅 수신을 기본 지원하지 않습니다. 즉, Stripe의 웹훅 엔드포인트를 Apidog URL로 지정해 인바운드 요청을 실시간으로 캡처할 수는 없습니다.

대신 다음 패턴을 사용합니다.

  1. 애플리케이션이 Stripe 웹훅을 수신한다.
  2. 애플리케이션이 이벤트와 처리 결과를 데이터베이스에 기록한다.
  3. Apidog가 데이터베이스를 쿼리한다.
  4. CI에서 저장된 이벤트와 비즈니스 상태를 단언(assert)한다.

이 방식은 웹훅 전달의 비동기성은 애플리케이션에서 처리하고, 테스트는 반복 가능한 데이터베이스 조회로 수행하게 합니다.

전체 흐름

구성 요소는 네 가지입니다.

  1. Stripe 웹훅을 수신하는 백엔드 엔드포인트
  2. 이벤트를 저장하는 stripe_event_logs 테이블
  3. Apidog 환경의 데이터베이스 연결
  4. 로그와 주문 상태를 검증하는 Post-Request Processor

핵심은 Apidog가 이벤트를 받는 것이 아니라, 애플리케이션이 이미 저장한 이벤트를 읽어 검증한다는 것입니다.

1단계: Stripe 웹훅 캡처 엔드포인트 만들기

Stripe가 POST 요청을 보낼 엔드포인트를 구현합니다. Express와 Stripe SDK를 사용한 최소 예제는 다음과 같습니다.

import express from "express";
import Stripe from "stripe";

const app = express();
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;

app.post(
  "/webhooks/stripe",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    let event;

    try {
      event = stripe.webhooks.constructEvent(
        req.body,
        req.headers["stripe-signature"],
        endpointSecret
      );
    } catch (err) {
      return res.status(400).send(`Signature check failed: ${err.message}`);
    }

    // CI 테스트가 나중에 조회할 이벤트 로그
    await db.query(
      `INSERT INTO stripe_event_logs (event_id, type, payload, handled_at)
       VALUES ($1, $2, $3, now())
       ON CONFLICT (event_id) DO NOTHING`,
      [event.id, event.type, JSON.stringify(event.data.object)]
    );

    if (event.type === "payment_intent.succeeded") {
      const intent = event.data.object;
      await markOrderPaid(intent.metadata.order_id);
    }

    res.json({ received: true });
  }
);
Enter fullscreen mode Exit fullscreen mode

구현 시 다음 두 가지를 반드시 확인하세요.

  • stripe.webhooks.constructEvent()로 서명을 검증합니다.
  • 이벤트 ID를 기준으로 중복 저장을 방지합니다.

Stripe는 동일 이벤트를 재전송할 수 있으므로 ON CONFLICT (event_id) DO NOTHING 같은 멱등성 처리가 필요합니다. 원시 요청 본문을 사용한 서명 검증이 중요한 이유는 웹훅 서명 검증 가이드에서 더 자세히 확인할 수 있습니다.

권장 로그 스키마

테스트에서 이벤트와 처리 결과를 추적할 수 있도록 최소한 다음 필드를 저장하세요.

CREATE TABLE stripe_event_logs (
  event_id TEXT PRIMARY KEY,
  type TEXT NOT NULL,
  payload JSONB NOT NULL,
  handled_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
Enter fullscreen mode Exit fullscreen mode

가능하다면 order_id, 처리 상태, 오류 정보도 별도 컬럼으로 저장하세요. JSON 전체를 매번 파싱하지 않고 테스트 쿼리를 작성할 수 있습니다.

2단계: Apidog 환경에 데이터베이스 연결 구성하기

Apidog 환경에서 테스트 대상 데이터베이스 연결을 설정합니다. 예를 들어 스테이징에서 테스트를 실행한다면, Apidog도 스테이징 데이터베이스에 연결해야 합니다.

환경을 분리할 때 다음을 점검하세요.

  • 테스트가 결제를 트리거하는 Stripe 환경이 테스트 모드인지
  • Stripe 웹훅이 올바른 스테이징 엔드포인트로 전달되는지
  • Apidog 데이터베이스 연결이 동일한 스테이징 데이터베이스를 가리키는지
  • 테스트 실행 간 로그 데이터가 섞이지 않는지

테스트가 이벤트를 생성한 데이터베이스와 Apidog가 조회하는 데이터베이스가 다르면, 웹훅 핸들러가 정상이어도 단언은 실패합니다.

3단계: Post-Request Processor로 이벤트 로그 검증하기

Apidog 시나리오에서 결제를 트리거하는 요청 뒤에 Post-Request Processor를 추가합니다. 이 단계에서 stripe_event_logs 테이블을 조회하고, 반환된 데이터에 단언을 설정합니다.

payment_intent.succeeded 이벤트를 검증하는 기본 쿼리는 다음과 같습니다.

SELECT event_id, type, payload, handled_at
FROM stripe_event_logs
WHERE type = 'payment_intent.succeeded'
ORDER BY handled_at DESC
LIMIT 1;
Enter fullscreen mode Exit fullscreen mode

하지만 최신 이벤트 하나만 조회하는 방식은 이전 테스트 실행의 데이터를 읽을 수 있습니다. 가능하면 테스트가 생성한 이벤트 ID 또는 주문 ID로 범위를 좁히세요.

SELECT event_id, type, payload, handled_at
FROM stripe_event_logs
WHERE event_id = :stripe_event_id
  AND type = 'payment_intent.succeeded';
Enter fullscreen mode Exit fullscreen mode

검증 항목은 다음처럼 구성할 수 있습니다.

  • typepayment_intent.succeeded인지
  • event_id가 테스트에서 생성한 PaymentIntent의 이벤트 ID와 일치하는지
  • payload의 금액이 기대값과 일치하는지
  • handled_at이 비어 있지 않은지
  • 연결된 주문이 결제 완료 상태인지

주문 상태까지 검증하기

이벤트 로그만 확인하면 “이벤트를 기록했다”까지만 증명합니다. 결제 처리 전체를 검증하려면 주문 테이블도 조회하세요.

SELECT id, status, paid_at
FROM orders
WHERE id = :order_id;
Enter fullscreen mode Exit fullscreen mode

여기서 다음을 단언합니다.

status = 'paid'
paid_at IS NOT NULL
Enter fullscreen mode Exit fullscreen mode

이렇게 하면 다음 전체 흐름을 검증할 수 있습니다.

결제 트리거
→ Stripe 이벤트 전달
→ 서명 검증
→ 이벤트 로그 저장
→ 주문 상태 업데이트
→ Apidog 데이터베이스 단언
Enter fullscreen mode Exit fullscreen mode

비동기 전달 지연 처리하기

Stripe 웹훅은 즉시 도착한다고 보장할 수 없습니다. 결제를 생성한 직후 데이터베이스를 조회하면 이벤트가 아직 저장되지 않았을 수 있습니다.

따라서 테스트 시나리오에 다음 중 하나를 추가하세요.

  • 짧은 대기 단계
  • 일정 간격으로 재시도하는 폴링
  • 최대 재시도 횟수와 타임아웃

예를 들어 1초 간격으로 최대 5회 조회하고, 이벤트가 발견되면 즉시 다음 단언으로 진행하는 방식이 적합합니다. 무조건 긴 sleep을 사용하는 것보다 폴링이 더 빠르고 안정적입니다.

로컬 개발에서는 Stripe CLI 또는 Ngrok 사용하기

CI에서는 캡처 후 쿼리 패턴을 사용하지만, 로컬에서 핸들러를 개발할 때는 Stripe가 localhost로 직접 요청을 보낼 수 없습니다. 이때는 릴레이 도구가 필요합니다.

Stripe CLI를 사용하면 로컬 엔드포인트로 이벤트를 전달할 수 있습니다.

stripe listen --forward-to localhost:3000/webhooks/stripe
Enter fullscreen mode Exit fullscreen mode

Stripe CLI가 출력하는 웹훅 서명 시크릿을 로컬 환경 변수 STRIPE_WEBHOOK_SECRET에 설정하세요.

STRIPE_WEBHOOK_SECRET=whsec_...
Enter fullscreen mode Exit fullscreen mode

Ngrok도 공개 URL을 생성해 로컬 포트를 Stripe 웹훅 엔드포인트로 등록할 수 있습니다.

정리하면 역할은 다음과 같습니다.

목적 권장 방식
로컬 핸들러 개발 Stripe CLI 또는 Ngrok
CI 검증 이벤트 로그 저장 + Apidog DB 쿼리
배포 후 회귀 테스트 Apidog 시나리오 예약 실행

Apidog의 네이티브 웹훅 기능과 구분하기

Apidog에는 Webhook 기능이 있지만, 이것은 Stripe에서 들어오는 인바운드 웹훅을 수신하는 기능이 아닙니다. 이 기능은 애플리케이션이 외부 URL로 보내는 아웃바운드 웹훅을 설계하고 문서화하는 용도입니다.

예를 들어 주문 상태가 변경됐을 때 고객 시스템으로 알림을 보내는 API를 문서화할 때 사용할 수 있습니다.

아웃바운드 웹훅을 정의하려면 다음 순서로 진행합니다.

  1. 왼쪽 사이드바에서 +를 클릭합니다.
  2. 새로운 기타 프로토콜 API(New Other Protocol APIs)를 선택합니다.
  3. 웹훅(Webhook)을 선택합니다.
  4. 요청 메서드, 웹훅 이름, 요청 본문, 헤더를 설정합니다.
  5. 필요하면 디버그 URL(Debug URL)로 호출을 테스트합니다.
  6. 저장(Save)을 클릭합니다.

디버그 URL(Debug URL)은 테스트 전용이며 게시된 문서나 OpenAPI 내보내기에는 포함되지 않습니다. 아웃바운드 콜백 설계는 API 설계의 웹훅에서 더 자세히 다룹니다.

이 글의 테스트 전략과 구분하세요.

  • Apidog 네이티브 Webhook: 애플리케이션이 외부로 보내는 이벤트 문서화
  • 캡처 후 쿼리: Stripe가 애플리케이션으로 보내는 이벤트 검증

테스트를 더 견고하게 만드는 방법

기본 흐름이 동작하면 다음 항목을 추가하세요.

1. 이벤트 유형만 검증하지 않기

payment_intent.succeeded 이벤트가 존재하는지만 확인하면 이전 테스트에서 남은 데이터를 읽을 수 있습니다. 이벤트 ID, 주문 ID 또는 테스트 실행 ID를 사용해 범위를 좁히세요.

SELECT *
FROM stripe_event_logs
WHERE event_id = :event_id;
Enter fullscreen mode Exit fullscreen mode

테스트 전용 데이터베이스라면 실행 시작 시 로그 테이블을 정리하는 방법도 사용할 수 있습니다.

TRUNCATE TABLE stripe_event_logs;
Enter fullscreen mode Exit fullscreen mode

단, 공유 스테이징 환경에서는 다른 테스트나 실제 검증 데이터를 삭제하지 않도록 주의해야 합니다.

2. 실패 경로 테스트하기

해피 패스만 검증하지 마세요. 다음 케이스도 테스트 대상에 포함하세요.

  • 잘못된 stripe-signature
  • 지원하지 않는 이벤트 타입
  • 이미 처리된 event_id
  • 주문 ID가 없는 이벤트
  • 주문 업데이트 중 데이터베이스 오류

예를 들어 잘못된 서명은 HTTP 400을 반환하고 이벤트 로그를 생성하지 않아야 합니다.

SELECT COUNT(*)
FROM stripe_event_logs
WHERE event_id = :invalid_event_id;
Enter fullscreen mode Exit fullscreen mode

기대값은 0입니다.

결제 웹훅의 멱등성과 재시도 전략은 결제 웹훅 모범 사례도 참고하세요.

3. 전달 성공이 아니라 비즈니스 결과 검증하기

“이벤트가 도착했다”는 충분하지 않습니다. 실제 요구 사항은 보통 다음과 같습니다.

결제 성공 → 주문 결제 완료 → 사용자 권한 활성화 → 영수증 발송
Enter fullscreen mode Exit fullscreen mode

따라서 이벤트 로그, 주문 상태, 권한 상태 등 실제 비즈니스 결과를 함께 검증하세요.

Apidog CLI로 CI에 연결하기

저장한 Apidog 시나리오는 CLI로 헤드리스 실행할 수 있습니다. 먼저 CLI를 설치하고 토큰으로 인증합니다.

npm install -g apidog-cli
apidog login --with-token <YOUR_ACCESS_TOKEN>
Enter fullscreen mode Exit fullscreen mode

그런 다음 테스트 시나리오와 환경을 지정해 실행합니다.

apidog run \
  --access-token $APIDOG_ACCESS_TOKEN \
  -t <SCENARIO_ID> \
  -e <ENV_ID> \
  -r cli
Enter fullscreen mode Exit fullscreen mode

옵션은 다음과 같습니다.

옵션 설명
-t 테스트 시나리오 ID
-e 실행 환경 ID
-r 리포터 형식

HTML 리포트와 CLI 출력을 모두 만들려면 다음처럼 실행할 수 있습니다.

apidog run \
  --access-token $APIDOG_ACCESS_TOKEN \
  -t <SCENARIO_ID> \
  -e <ENV_ID> \
  -r html,cli
Enter fullscreen mode Exit fullscreen mode

시나리오의 단언이 실패하면 CLI는 0이 아닌 종료 코드를 반환합니다. 따라서 GitHub Actions, GitLab CI, Jenkins 등의 파이프라인에서 이 명령을 병합 게이트로 사용할 수 있습니다.

Apidog CLI 설정은 Apidog CLI 설치 가이드, CI 연결 예시는 CI/CD 파이프라인 둘러보기에서 확인할 수 있습니다.

자주 묻는 질문

Apidog가 Stripe 웹훅을 직접 수신할 수 있나요?

아니요. Apidog는 웹훅 수신을 기본 지원하지 않습니다. Stripe 이벤트는 애플리케이션 엔드포인트에서 수신하고 데이터베이스에 저장한 뒤, Apidog가 Post-Request Processor로 저장된 데이터를 검증해야 합니다.

단언은 어디에서 실행되나요?

Apidog 테스트 시나리오의 Post-Request Processor에서 실행됩니다. 이 단계가 환경에 구성된 데이터베이스 연결을 사용해 stripe_event_logsorders 같은 테이블을 조회합니다.

전달 지연은 어떻게 처리하나요?

짧은 대기나 폴링 재시도를 추가하세요. 이벤트가 확인될 때까지 일정 간격으로 데이터베이스를 조회하고, 최대 재시도 횟수를 넘으면 테스트를 실패 처리합니다.

데이터베이스 검증 기능에 특정 플랜이 필요한가요?

이 워크플로우에 대한 문서만으로는 플랜 제한을 단정할 수 없습니다. 현재 지원 범위는 가격 페이지와 제품 환경에서 확인하세요. Apidog 다운로드 후 테스트 프로젝트에서 데이터베이스 연결과 Post-Request Processor를 직접 검증할 수 있습니다.

마무리

Stripe 웹훅을 Apidog가 직접 수신하도록 구성할 수는 없습니다. 대신 애플리케이션이 Stripe 이벤트를 캡처하고, 서명을 검증하며, 이벤트 로그와 주문 상태를 데이터베이스에 저장하세요. 이후 Apidog의 Post-Request Processor가 해당 데이터를 쿼리하고 단언하도록 구성하면 됩니다.

이 패턴을 apidog run으로 CI에 연결하면 매 배포와 병합마다 다음을 자동으로 증명할 수 있습니다.

Stripe 결제 성공
→ 웹훅 수신
→ 이벤트 처리
→ 주문 결제 완료 상태 전환
Enter fullscreen mode Exit fullscreen mode

Apidog에서 시나리오를 저장한 뒤, API 테스트 예약 실행까지 설정하면 배포 이후의 회귀도 지속적으로 확인할 수 있습니다.

Top comments (0)