TIL - 20260702

juni·2026년 7월 2일

TIL

목록 보기
391/470

0702 백엔드 실무 심화 (9/N): Webhook/Callback 수신과 이벤트 처리 설계


✅ 1. Webhook이란 무엇인가?

  • Webhook은 외부 서비스가 어떤 이벤트가 발생했을 때 우리 서버의 특정 URL로 결과를 알려주는 방식입니다.
  • 우리가 외부 API를 호출하고 응답을 기다리는 방식과 달리, 외부 서비스가 나중에 우리 서버로 요청을 보내줍니다.
  • 결제 결과, 본인인증 결과, 알림톡 발송 결과, 배송 상태 변경, 광고 전환 처리 결과 같은 기능에서 자주 사용됩니다.
우리 서버
  ↓ 외부 API 요청
외부 서비스
  ↓ 비동기 처리
우리 서버의 Webhook URL 호출
  ↓
결과 저장 및 후속 처리

➕ 1-1. Webhook과 일반 API 호출의 차이

구분일반 API 호출Webhook
요청 주체우리 서버외부 서비스
처리 방식우리가 호출하고 응답 받음외부 서비스가 나중에 알려줌
사용 예시결제 승인 요청, SMS 발송 요청결제 완료 알림, 발송 결과 알림
중요 포인트timeout, 재시도검증, 중복 처리, 이력 저장
  • Webhook은 우리 서버가 받는 요청이지만, 일반 사용자 요청과 다르게 외부 서비스에서 들어오는 시스템 요청입니다.
  • 그래서 인증, 서명 검증, 중복 처리, 상태 전이 검증이 특히 중요합니다.

✅ 2. Callback이란 무엇인가?

  • Callback은 외부 서비스가 처리 결과를 다시 알려주는 방식입니다.
  • Webhook과 거의 비슷한 의미로 사용되며, 서비스나 문서에 따라 Callback URL, Webhook URL, Notification URL이라는 표현을 사용합니다.
본인인증 요청
  ↓
사용자가 인증 완료
  ↓
인증 업체가 callbackUrl로 결과 전달
  ↓
우리 서버가 인증 결과 저장
  • 실무에서는 Webhook과 Callback을 엄격히 구분하기보다, “외부 서비스가 우리 서버로 결과를 알려주는 구조”로 이해하면 됩니다.

✅ 3. Webhook이 필요한 대표 상황

➕ 3-1. 결제 결과 수신

  • 결제 승인, 결제 취소, 가상계좌 입금, 환불 결과 등을 Webhook으로 받을 수 있습니다.
주문 생성
  ↓
결제 요청
  ↓
외부 결제사 처리
  ↓
결제 완료 Webhook 수신
  ↓
주문 상태 PAID 변경
  ↓
결제 이력 저장

➕ 3-2. 본인인증 결과 수신

  • 휴대폰 본인인증이나 간편인증 결과를 Callback으로 받을 수 있습니다.
본인인증 시작
  ↓
사용자 인증 완료
  ↓
인증 업체 callback 호출
  ↓
인증 성공/실패 저장
  ↓
다음 가입 단계 진행 가능

➕ 3-3. 알림톡/SMS 발송 결과 수신

  • 알림톡 발송 요청은 성공했지만, 실제 고객에게 도달했는지는 나중에 결과로 올 수 있습니다.
알림톡 발송 요청 성공
  ↓
외부 업체가 실제 발송 처리
  ↓
성공/실패 결과 Webhook 전달
  ↓
발송 로그 상태 업데이트

➕ 3-4. 광고/외부 시스템 연동 결과

  • 광고 전환 API, CRM, ERP, 상담 시스템 연동에서도 처리 결과를 Webhook으로 받을 수 있습니다.
상담 신청 정보 CRM 전송
  ↓
CRM 시스템에서 접수 처리
  ↓
처리 결과 Callback 수신
  ↓
우리 DB에 외부 접수번호 저장

✅ 4. Webhook URL 설계

  • Webhook URL은 외부 서비스가 호출하는 엔드포인트입니다.
  • 일반 사용자 API와 구분하기 쉽게 설계하는 것이 좋습니다.

➕ 4-1. URL 예시

POST /api/webhooks/payment
POST /api/webhooks/identity
POST /api/webhooks/kakao-alimtalk
POST /api/webhooks/sms
POST /api/webhooks/crm

➕ 4-2. 설계 기준

  1. 외부 서비스별로 URL을 분리한다.
  2. 가능하면 POST를 사용한다.
  3. URL만으로 인증을 대신하지 않는다.
  4. 요청 Body 원본을 로그로 남길지 신중히 결정한다.
  5. 외부 서비스 문서의 요구사항과 맞춘다.
  6. 운영/개발 Webhook URL을 분리한다.
개발:
https://dev-api.example.com/api/webhooks/payment

운영:
https://api.example.com/api/webhooks/payment
  • 개발용 Webhook과 운영용 Webhook이 섞이면 실제 주문이나 인증 결과가 잘못 저장될 수 있습니다.

✅ 5. Webhook 보안이 중요한 이유

  • Webhook URL은 외부에서 접근 가능한 API입니다.
  • 아무나 Webhook URL을 호출할 수 있다면 결제 완료, 인증 완료, 발송 성공 같은 상태를 임의로 만들 수 있습니다.
공격자가 Webhook URL을 알아냄
  ↓
임의로 결제 완료 요청 전송
  ↓
서명 검증이 없으면 주문이 PAID로 변경될 수 있음
  • Webhook은 반드시 외부 서비스에서 보낸 요청인지 검증해야 합니다.

✅ 6. Webhook 검증 방법

➕ 6-1. Secret Token 검증

  • 외부 서비스와 약속한 Secret 값을 Header나 Query에 포함해 검증하는 방식입니다.
Header:
x-webhook-secret: shared-secret-value
const secret = request.headers['x-webhook-secret'];

if (secret !== this.configService.get('WEBHOOK_SECRET')) {
  throw new UnauthorizedException('Invalid webhook secret');
}
  • 구현은 단순하지만 Secret이 유출되면 위험합니다.
  • Secret은 반드시 환경변수로 관리하고 Git에 올리면 안 됩니다.

➕ 6-2. Signature 검증

  • 더 안전한 방식은 요청 Body를 Secret으로 서명한 값과 Header의 서명값을 비교하는 것입니다.
  • 결제사나 인증 업체 Webhook에서 자주 사용됩니다.
외부 서비스:
request body + secret으로 signature 생성
  ↓
Header에 signature 포함

우리 서버:
받은 body + secret으로 signature 재계산
  ↓
Header signature와 비교

➕ 6-3. HMAC 검증 예시

import * as crypto from 'crypto';

function verifySignature(rawBody: string, signature: string, secret: string) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature),
  );
}
  • 단순 문자열 비교보다 timingSafeEqual 같은 안전한 비교 방식을 사용하는 것이 좋습니다.
  • Signature 검증에는 원본 Raw Body가 필요한 경우가 많습니다.

✅ 7. Raw Body가 필요한 이유

  • 일부 Webhook 서명 검증은 JSON으로 파싱된 Body가 아니라 원본 문자열 Body를 기준으로 계산됩니다.
  • JSON 파싱 과정에서 공백, 순서, 인코딩이 바뀌면 서명 검증이 실패할 수 있습니다.
외부 서비스가 서명한 대상:
원본 요청 Body 문자열

우리 서버가 검증할 대상:
JSON 파싱 후 객체

결과:
서명 불일치 가능
  • NestJS에서 Webhook을 처리할 때는 해당 라우트에서 Raw Body를 받을 수 있도록 설정해야 하는 경우가 있습니다.
  • 결제사 문서를 보고 어떤 방식으로 서명 검증을 해야 하는지 반드시 확인해야 합니다.

✅ 8. Webhook 응답 기준

  • 외부 서비스는 우리 서버의 응답 상태 코드를 보고 Webhook 전달 성공 여부를 판단합니다.
  • 보통 정상 처리 시 200 OK 또는 204 No Content를 반환합니다.

➕ 8-1. 응답 예시

정상 처리:
200 OK

잘못된 서명:
401 Unauthorized 또는 403 Forbidden

잘못된 요청 형식:
400 Bad Request

서버 오류:
500 Internal Server Error

➕ 8-2. 주의점

  • Webhook 처리 시간이 너무 길면 외부 서비스가 실패로 판단할 수 있습니다.
  • 오래 걸리는 후속 작업은 Queue로 분리하는 것이 좋습니다.
Webhook 수신
  ↓
검증
  ↓
기본 이력 저장
  ↓
Queue에 후속 작업 등록
  ↓
빠르게 200 응답

✅ 9. Webhook 중복 수신

  • 외부 서비스는 Webhook 전달에 실패했다고 판단하면 같은 이벤트를 다시 보낼 수 있습니다.
  • 네트워크 문제로 실제로는 우리가 처리했지만, 외부 서비스는 응답을 받지 못해 재전송할 수도 있습니다.
외부 서비스가 Webhook 전송
  ↓
우리 서버가 처리 성공
  ↓
응답 중 네트워크 오류
  ↓
외부 서비스는 실패로 판단
  ↓
같은 Webhook 재전송
  • 따라서 Webhook 처리에는 멱등성이 필수입니다.

✅ 10. Webhook 멱등성 처리

  • 같은 이벤트가 여러 번 와도 결과가 중복되지 않게 처리해야 합니다.
  • 외부 이벤트 ID, 결제 ID, 발송 ID 같은 고유값을 기준으로 이미 처리한 이벤트인지 확인합니다.

➕ 10-1. 이벤트 ID 기준 처리

externalEventId = evt_12345

이미 처리한 evt_12345가 있으면:
중복 처리하지 않음

➕ 10-2. Prisma 모델 예시

model WebhookEvent {
  id              Int      @id @default(autoincrement())
  provider        String
  eventId         String
  eventType       String
  status          String
  relatedType     String?
  relatedId       Int?
  payload         Json?
  errorMessage    String?
  receivedAt      DateTime @default(now())
  processedAt     DateTime?

  @@unique([provider, eventId])
  @@index([provider, eventType])
  @@index([status, receivedAt])
}
  • @@unique([provider, eventId])로 같은 이벤트가 중복 저장되지 않게 막을 수 있습니다.
  • 이벤트를 먼저 저장하고, 이미 있으면 중복으로 판단하는 방식이 안전합니다.

➕ 10-3. 처리 예시

async handleWebhook(payload: PaymentWebhookPayload) {
  const event = await this.prisma.webhookEvent.create({
    data: {
      provider: 'PAYMENT_PROVIDER',
      eventId: payload.eventId,
      eventType: payload.type,
      status: 'RECEIVED',
      payload,
    },
  }).catch((error) => {
    if (error.code === 'P2002') {
      return null;
    }

    throw error;
  });

  if (!event) {
    return {
      duplicated: true,
    };
  }

  // 실제 처리 로직 진행
}
  • unique constraint로 DB 레벨에서 중복 수신을 막습니다.
  • 동시 중복 요청에도 비교적 안전합니다.

✅ 11. 상태 전이 검증

  • Webhook은 외부에서 상태를 변경하는 요청입니다.
  • 따라서 현재 상태에서 Webhook이 요청한 상태로 변경 가능한지 검증해야 합니다.

➕ 11-1. 결제 상태 예시

PENDING → PAID
PENDING → FAILED
PAID → REFUNDED
PAID → PARTIAL_REFUNDED

불가능한 흐름:
FAILED → PAID
REFUNDED → PAID

➕ 11-2. 검증이 필요한 이유

주문이 이미 CANCELLED 상태
  ↓
뒤늦게 결제 완료 Webhook 수신
  ↓
검증 없이 PAID 변경
  ↓
취소된 주문이 결제 완료로 바뀜
  • Webhook은 늦게 오거나 순서가 뒤바뀌어 올 수 있습니다.
  • 상태 변경 전 현재 상태와 이벤트 시간을 확인해야 합니다.

✅ 12. Webhook 수신 순서 문제

  • 외부 이벤트가 항상 순서대로 도착한다고 믿으면 안 됩니다.
  • 네트워크 지연, 재전송, 외부 서비스 처리 방식 때문에 순서가 바뀔 수 있습니다.
정상 순서:
PAYMENT_APPROVED → PAYMENT_CANCELLED

실제 수신:
PAYMENT_CANCELLED 먼저 도착
PAYMENT_APPROVED 나중에 도착
  • 이런 경우 단순히 마지막에 온 이벤트로 상태를 덮어쓰면 잘못된 상태가 될 수 있습니다.

➕ 12-1. 대응 방법

  1. 이벤트 발생 시간을 확인한다.
  2. 현재 상태에서 변경 가능한지 검증한다.
  3. 이미 더 최신 이벤트가 처리되었는지 확인한다.
  4. 처리 불가능한 이벤트는 무시하거나 보류 상태로 저장한다.
  5. 관리자 확인 대상에 올린다.

✅ 13. Webhook 처리와 트랜잭션

  • Webhook으로 상태를 변경할 때는 관련 DB 작업을 트랜잭션으로 묶어야 합니다.

➕ 13-1. 결제 완료 Webhook 처리 예시

1. WebhookEvent 저장
2. 주문 조회
3. 상태 전이 검증
4. 주문 상태 PAID 변경
5. 결제 이력 저장
6. WebhookEvent 처리 완료 변경
  • 주문 상태는 바뀌었는데 결제 이력이 없거나, WebhookEvent는 완료인데 주문 상태가 그대로인 상황을 막아야 합니다.

➕ 13-2. Prisma 예시

await this.prisma.$transaction(async (tx) => {
  const order = await tx.order.findUnique({
    where: {
      id: orderId,
    },
  });

  if (!order) {
    throw new NotFoundException('주문을 찾을 수 없습니다.');
  }

  if (!canChangePaymentStatus(order.paymentStatus, 'PAID')) {
    throw new BadRequestException('변경할 수 없는 결제 상태입니다.');
  }

  await tx.order.update({
    where: {
      id: order.id,
    },
    data: {
      paymentStatus: 'PAID',
      paidAt: new Date(),
    },
  });

  await tx.paymentHistory.create({
    data: {
      orderId: order.id,
      fromStatus: order.paymentStatus,
      toStatus: 'PAID',
      provider: 'PAYMENT_PROVIDER',
      externalPaymentId,
    },
  });

  await tx.webhookEvent.update({
    where: {
      id: webhookEventId,
    },
    data: {
      status: 'PROCESSED',
      processedAt: new Date(),
    },
  });
});
  • 상태 변경, 이력 저장, WebhookEvent 처리 완료를 하나의 트랜잭션으로 묶었습니다.
  • 이벤트 처리 중간에 실패하면 전체가 롤백됩니다.

✅ 14. Webhook 처리 후 Queue 분리

  • Webhook 수신 후 알림 발송, 외부 CRM 동기화, 통계 갱신 같은 부가 작업이 필요할 수 있습니다.
  • 이런 작업을 Webhook 요청 안에서 모두 처리하면 응답이 느려집니다.

➕ 14-1. 권장 흐름

Webhook 수신
  ↓
서명 검증
  ↓
이벤트 저장
  ↓
핵심 상태 변경
  ↓
Queue에 후속 작업 등록
  ↓
200 응답

➕ 14-2. 후속 작업 예시

  • 고객에게 결제 완료 알림톡 발송

  • 관리자에게 신규 결제 알림

  • CRM 상태 동기화

  • 광고 전환 API 전송

  • 통계 집계 갱신

  • 정산 데이터 생성

  • Webhook 자체는 빠르게 끝내고, 오래 걸리는 작업은 Worker가 처리하게 만드는 것이 안정적입니다.


✅ 15. Webhook 실패 처리

  • Webhook 처리는 실패할 수 있습니다.
  • 서명 검증 실패, 연관 데이터 없음, 상태 전이 불가, DB 오류, 중복 이벤트 등이 발생할 수 있습니다.

➕ 15-1. 실패 유형

실패대응
서명 검증 실패401/403 반환, 이력 저장 검토
요청 형식 오류400 반환
연관 데이터 없음실패 이력 저장, 관리자 확인
상태 전이 불가무시 또는 보류 처리
DB 오류500 반환, 재전송 유도
중복 이벤트200 반환 가능, 중복 처리 안 함

➕ 15-2. 중복 이벤트에 200을 반환하는 이유

  • 이미 처리한 이벤트라면 다시 처리할 필요가 없습니다.
  • 하지만 외부 서비스에 계속 실패로 응답하면 같은 이벤트를 계속 재전송할 수 있습니다.
이미 처리한 Webhook 재수신
  ↓
중복으로 판단
  ↓
추가 처리 없이 200 OK
  ↓
외부 서비스는 전달 성공으로 판단

✅ 16. Webhook 로그 설계

  • Webhook은 운영 중 문제를 추적하기 위해 로그가 중요합니다.
  • 하지만 민감정보를 그대로 저장하면 안 됩니다.

➕ 16-1. 남기면 좋은 정보

  • provider
  • eventId
  • eventType
  • status
  • relatedType
  • relatedId
  • receivedAt
  • processedAt
  • errorCode
  • errorMessage
  • 요청 IP
  • 처리 결과

➕ 16-2. 조심해야 할 정보

  • 전체 전화번호
  • 주민등록번호
  • 인증 토큰
  • 결제 카드 정보
  • API Secret
  • 원본 인증 데이터
  • 고객 상담 메모
원본 payload 저장:
디버깅에 유리하지만 개인정보 위험 있음

마스킹 payload 저장:
보안에 유리하지만 디버깅 정보가 줄어듦
  • 원본 payload 저장 여부는 서비스 성격과 보안 기준에 맞게 결정해야 합니다.
  • 최소한 민감정보는 마스킹하거나 제외해야 합니다.

✅ 17. Webhook 재처리 기능

  • Webhook 처리 실패가 발생하면 운영자가 재처리할 수 있는 구조가 있으면 좋습니다.
  • 특히 결제, 인증, CRM 연동처럼 중요한 이벤트는 수동 재처리 기능이 도움이 됩니다.

➕ 17-1. 재처리 흐름

WebhookEvent 상태 FAILED
  ↓
관리자 페이지에서 실패 원인 확인
  ↓
재처리 버튼 클릭
  ↓
Queue에 재처리 Job 등록
  ↓
Worker가 다시 처리
  ↓
성공 시 PROCESSED 변경

➕ 17-2. 재처리 시 주의점

  1. 이미 처리된 이벤트는 재처리하지 않는다.
  2. 현재 상태에서 재처리 가능한지 다시 검증한다.
  3. 재처리한 관리자 ID를 기록한다.
  4. 재처리 횟수를 제한한다.
  5. 재처리 결과를 이력으로 남긴다.

✅ 18. NestJS Webhook Controller 예시

@Controller('webhooks/payment')
export class PaymentWebhookController {
  constructor(
    private readonly paymentWebhookService: PaymentWebhookService,
  ) {}

  @Post()
  async handlePaymentWebhook(
    @Headers('x-signature') signature: string,
    @Body() body: PaymentWebhookDto,
    @Req() req: Request,
  ) {
    await this.paymentWebhookService.handle({
      signature,
      body,
      rawBody: req['rawBody'],
      ipAddress: req.ip,
      userAgent: req.headers['user-agent'],
    });

    return {
      received: true,
    };
  }
}
  • Controller는 요청 수신과 기본 값 전달만 담당합니다.
  • 검증, 중복 처리, 상태 변경은 Service에서 처리하는 것이 좋습니다.

✅ 19. Webhook Service 처리 흐름 예시

async handle(params: HandlePaymentWebhookParams) {
  this.verifySignature(params.rawBody, params.signature);

  const normalizedEvent = this.normalizeEvent(params.body);

  const webhookEvent = await this.createWebhookEventIfNotExists(normalizedEvent);

  if (!webhookEvent) {
    return {
      duplicated: true,
    };
  }

  await this.processPaymentEvent(webhookEvent.id, normalizedEvent);

  return {
    success: true,
  };
}
  • 검증 → 표준화 → 중복 체크 → 실제 처리 순서로 나누면 코드가 이해하기 쉬워집니다.
  • 외부 API별 응답 구조가 달라도 내부 이벤트 구조로 표준화하면 유지보수가 편해집니다.

✅ 20. 실무 체크리스트

➕ 20-1. Webhook 보안 체크리스트

  1. Webhook URL이 일반 API와 구분되어 있는가?
  2. Secret 또는 Signature 검증을 하는가?
  3. API Key나 Secret을 Git에 올리지 않았는가?
  4. 운영/개발 Webhook URL이 분리되어 있는가?
  5. Raw Body가 필요한 검증 방식인지 확인했는가?
  6. 요청 IP 제한이 필요한지 검토했는가?
  7. 서명 검증 실패 요청을 어떻게 기록할지 정했는가?

➕ 20-2. Webhook 처리 체크리스트

  1. 외부 이벤트 ID로 중복 수신을 막는가?
  2. 같은 이벤트가 여러 번 와도 안전한가?
  3. 현재 상태에서 변경 가능한 이벤트인지 검증하는가?
  4. 이벤트 수신 순서가 바뀌어도 안전한가?
  5. 핵심 상태 변경과 이력 저장을 트랜잭션으로 묶는가?
  6. 오래 걸리는 후속 작업은 Queue로 분리하는가?
  7. 처리 실패 이벤트를 관리자에서 확인할 수 있는가?
  8. 필요한 경우 재처리 기능이 있는가?

➕ 20-3. Webhook 로그 체크리스트

  1. provider, eventId, eventType을 저장하는가?
  2. 처리 상태 RECEIVED, PROCESSED, FAILED를 관리하는가?
  3. 연관 데이터 relatedType, relatedId를 저장하는가?
  4. 실패 원인과 에러 메시지를 남기는가?
  5. 민감정보를 payload에 그대로 저장하지 않는가?
  6. 중복 이벤트도 추적 가능한가?
  7. 처리 시간과 재처리 이력을 확인할 수 있는가?

✅ 21. AI를 활용해 Webhook 구조를 설계할 때 질문법

  • Webhook은 단순히 Controller 하나 만드는 문제가 아닙니다.
  • 보안 검증, 중복 처리, 상태 전이, 이력 저장, 재처리까지 함께 설계해야 합니다.

➕ 21-1. 좋은 질문 예시

NestJS + Prisma 서비스에서 결제사 Webhook을 수신하는 구조를 만들고 싶어.

상황:
1. 결제사는 결제 승인, 결제 실패, 결제 취소 이벤트를 Webhook으로 보냄
2. 각 이벤트에는 eventId, paymentId, orderId, eventType, occurredAt이 있음
3. 같은 Webhook이 여러 번 올 수 있음
4. 이벤트 순서가 바뀌어 도착할 수도 있음
5. 주문 상태는 PENDING, PAID, FAILED, CANCELLED, REFUNDED가 있음
6. 서명 검증에는 Raw Body와 Secret이 필요함
7. 결제 상태 변경과 결제 이력을 트랜잭션으로 저장하고 싶음
8. 실패한 Webhook은 관리자 페이지에서 확인하고 재처리하고 싶음

요청:
- Webhook URL 설계
- Signature 검증 방식
- WebhookEvent 테이블 설계
- 중복 이벤트 처리
- 상태 전이 검증
- Prisma 트랜잭션 처리 흐름
- 실패 이벤트 재처리 구조
- 로그 보안 기준
을 실무 기준으로 설명해줘.

➕ 21-2. AI 답변 검증 기준

  1. Webhook URL만 숨기는 방식으로 보안 처리하지 않는가?
  2. Signature 또는 Secret 검증을 설명하는가?
  3. Raw Body 필요성을 언급하는가?
  4. eventId unique 제약으로 중복 처리를 설명하는가?
  5. 이벤트 순서가 바뀔 수 있다고 가정하는가?
  6. 상태 전이 검증을 포함하는가?
  7. 상태 변경과 이력 저장을 트랜잭션으로 묶는가?
  8. 실패 이벤트 재처리와 민감정보 로그 주의를 설명하는가?

📌 요약

  • Webhook/Callback은 외부 서비스가 처리 결과나 이벤트를 우리 서버로 알려주는 방식입니다.
  • 결제, 본인인증, 알림톡/SMS 발송 결과, CRM 연동, 광고 전환 처리 결과에서 자주 사용됩니다.
  • Webhook URL은 외부에서 호출 가능하므로 Secret 또는 Signature 검증이 반드시 필요합니다.
  • 일부 서명 검증 방식은 JSON 파싱 전 원본 Raw Body가 필요할 수 있습니다.
  • 외부 서비스는 같은 Webhook을 여러 번 보낼 수 있으므로 eventId 기반 멱등성 처리가 필요합니다.
  • Webhook 이벤트는 순서가 바뀌어 도착할 수 있으므로 현재 상태와 이벤트 시간을 기준으로 상태 전이를 검증해야 합니다.
  • 핵심 상태 변경, 이력 저장, WebhookEvent 처리 상태 변경은 트랜잭션으로 묶어야 데이터 정합성을 지킬 수 있습니다.
  • 오래 걸리는 후속 작업은 Webhook 요청 안에서 직접 처리하지 말고 Queue로 분리하는 것이 안정적입니다.
  • 실패한 Webhook은 이력으로 남기고, 필요한 경우 관리자 페이지에서 재처리할 수 있게 설계하는 것이 좋습니다.

0개의 댓글