# feat(webhook): webhook-dispatcher 추가 + payout 중복 이벤트 멱등 처리 보강 - #11
Merged
Merged
Conversation
## Summary
`webhook-dispatcher` 모듈을 신규 추가해 payout 결과 이벤트(`remittance.payout.completed/failed`)를 Kafka에서 소비하고 외부 webhook으로 전달하는 비동기 파이프라인을 구현했습니다.
또한 `payout-worker`의 중복 이벤트 처리 로직을 UNIQUE 예외 catch 방식에서 사전 조회 방식으로 바꿔 `UnexpectedRollbackException`으로 인한 partition 정체(stuck) 가능성을 제거했습니다.
---
### Changes
#### 1) webhook-dispatcher 신규 구현
- `webhook-dispatcher/build.gradle.kts`
- Spring Boot/JPA/Flyway/Kafka/WebMVC/Validation/Testcontainers 의존성 구성
- `webhook-dispatcher/src/main/kotlin/com/openremit/webhook/WebhookDispatcherApplication.kt` (신규)
- `@EnableKafka`, `@EnableScheduling`, `@EnableConfigurationProperties` 적용
- `webhook-dispatcher/src/main/kotlin/com/openremit/webhook/infrastructure/kafka/RemittancePayoutResultConsumer.kt` (신규)
- `remittance.payout.completed/failed` 소비
- `eventId = {remittanceId}:{topic}` 멱등 키로 enqueue
- `webhook-dispatcher/src/main/kotlin/com/openremit/webhook/application/WebhookEnqueueService.kt` (신규)
- `event_id` 사전 조회 기반 enqueue 멱등 처리
- `webhook-dispatcher/src/main/kotlin/com/openremit/webhook/application/WebhookDispatcher.kt` (신규)
- 스케줄러로 due webhook 배치 폴링/디스패치
- `webhook-dispatcher/src/main/kotlin/com/openremit/webhook/application/WebhookSender.kt` (신규)
- 단건 트랜잭션 단위 발송 시도 및 상태 전이
- `webhook-dispatcher/src/main/kotlin/com/openremit/webhook/application/WebhookBackoff.kt` (신규)
- exponential backoff 계산 (`base/multiplier/maxAttempts`)
- `webhook-dispatcher/src/main/kotlin/com/openremit/webhook/domain/Webhook.kt` (신규)
- `PENDING/SUCCESS/FAILED`, attempt/next_retry_at/실패 이력 모델링
- `webhook-dispatcher/src/main/kotlin/com/openremit/webhook/infrastructure/client/WebhookClient.kt` (신규)
- `RestClient` 기반 webhook POST 클라이언트 + 예외 래핑
- `webhook-dispatcher/src/main/kotlin/com/openremit/webhook/infrastructure/persistence/WebhookRepository.kt` (신규)
- due webhook 조회 쿼리 추가
- `webhook-dispatcher/src/main/resources/db/migration/V1__webhook_tables.sql` (신규)
- `webhooks` 테이블/인덱스/`event_id` UNIQUE 추가
- `webhook-dispatcher/src/main/resources/application.yaml` (신규)
- datasource/flyway/kafka/webhook/backoff/polling 설정 추가
#### 2) payout-worker 멱등 처리 보강
- `payout-worker/src/main/kotlin/com/openremit/payout/application/PayoutProcessor.kt`
- `DataIntegrityViolationException` catch 제거
- `findByRemittanceId` 사전 조회 후 없을 때만 insert로 변경
- `payout-worker/src/test/kotlin/com/openremit/payout/PayoutWorkerIntegrationTest.kt`
- 동일 remittance 이벤트 3회 수신 시
- attempt 1건
- outbox 1건
- payout API 호출 1회
검증 테스트 추가
#### 3) 로컬 mock webhook 인프라 추가
- `docker-compose.yml`
- `mock-webhook` (WireMock, `9997`) 서비스 추가
- `mock-webhook/mappings/webhook-success.json` (신규)
- `/webhook` 200 응답 매핑 추가
#### 4) webhook-dispatcher 통합 테스트 추가
- `webhook-dispatcher/src/test/kotlin/com/openremit/webhook/WebhookDispatcherIntegrationTest.kt` (신규)
- 성공 전송 시 `SUCCESS` 전이 검증
- 502 응답 시 백오프 재시도 후 `FAILED` 전이 검증
- 중복 이벤트 enqueue/발송 멱등 검증
- `webhook-dispatcher/src/test/kotlin/com/openremit/webhook/WebhookDispatcherTestcontainersConfig.kt` (신규)
- MySQL/Kafka Testcontainers 설정
---
### Checklist
- [x] webhook-dispatcher 모듈 및 실행 엔트리포인트 추가
- [x] payout 결과 Kafka consumer + webhook enqueue 구현
- [x] webhook 발송/재시도/상태 전이 로직 구현
- [x] webhook 테이블 마이그레이션 및 설정 추가
- [x] payout-worker 중복 이벤트 멱등 처리 보강
- [x] mock-webhook docker-compose 서비스 추가
- [x] payout-worker/webhook-dispatcher 통합 테스트 추가
---
### How to Test
```bash
# payout-worker 중복 이벤트 멱등 테스트
./gradlew :payout-worker:test --tests "*PayoutWorkerIntegrationTest"
# webhook-dispatcher 통합 테스트
./gradlew :webhook-dispatcher:test --tests "*WebhookDispatcherIntegrationTest"
# 전체 테스트
./gradlew test
# 로컬 mock webhook 포함 인프라 실행
docker compose up -d
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
webhook-dispatcher모듈을 신규 추가해 payout 결과 이벤트(remittance.payout.completed/failed)를 Kafka에서 소비하고 외부 webhook으로 전달하는 비동기 파이프라인을 구현했습니다. 또한payout-worker의 중복 이벤트 처리 로직을 UNIQUE 예외 catch 방식에서 사전 조회 방식으로 바꿔UnexpectedRollbackException으로 인한 partition 정체(stuck) 가능성을 제거했습니다.Changes
1) webhook-dispatcher 신규 구현
webhook-dispatcher/build.gradle.ktswebhook-dispatcher/src/main/kotlin/com/openremit/webhook/WebhookDispatcherApplication.kt(신규)@EnableKafka,@EnableScheduling,@EnableConfigurationProperties적용webhook-dispatcher/src/main/kotlin/com/openremit/webhook/infrastructure/kafka/RemittancePayoutResultConsumer.kt(신규)remittance.payout.completed/failed소비eventId = {remittanceId}:{topic}멱등 키로 enqueuewebhook-dispatcher/src/main/kotlin/com/openremit/webhook/application/WebhookEnqueueService.kt(신규)event_id사전 조회 기반 enqueue 멱등 처리webhook-dispatcher/src/main/kotlin/com/openremit/webhook/application/WebhookDispatcher.kt(신규)webhook-dispatcher/src/main/kotlin/com/openremit/webhook/application/WebhookSender.kt(신규)webhook-dispatcher/src/main/kotlin/com/openremit/webhook/application/WebhookBackoff.kt(신규)base/multiplier/maxAttempts)webhook-dispatcher/src/main/kotlin/com/openremit/webhook/domain/Webhook.kt(신규)PENDING/SUCCESS/FAILED, attempt/next_retry_at/실패 이력 모델링webhook-dispatcher/src/main/kotlin/com/openremit/webhook/infrastructure/client/WebhookClient.kt(신규)RestClient기반 webhook POST 클라이언트 + 예외 래핑webhook-dispatcher/src/main/kotlin/com/openremit/webhook/infrastructure/persistence/WebhookRepository.kt(신규)webhook-dispatcher/src/main/resources/db/migration/V1__webhook_tables.sql(신규)webhooks테이블/인덱스/event_idUNIQUE 추가webhook-dispatcher/src/main/resources/application.yaml(신규)2) payout-worker 멱등 처리 보강
payout-worker/src/main/kotlin/com/openremit/payout/application/PayoutProcessor.ktDataIntegrityViolationExceptioncatch 제거findByRemittanceId사전 조회 후 없을 때만 insert로 변경payout-worker/src/test/kotlin/com/openremit/payout/PayoutWorkerIntegrationTest.kt3) 로컬 mock webhook 인프라 추가
docker-compose.ymlmock-webhook(WireMock,9997) 서비스 추가mock-webhook/mappings/webhook-success.json(신규)/webhook200 응답 매핑 추가4) webhook-dispatcher 통합 테스트 추가
webhook-dispatcher/src/test/kotlin/com/openremit/webhook/WebhookDispatcherIntegrationTest.kt(신규)SUCCESS전이 검증FAILED전이 검증webhook-dispatcher/src/test/kotlin/com/openremit/webhook/WebhookDispatcherTestcontainersConfig.kt(신규)Checklist
How to Test