## 한 줄 목표 자동 재시도를 멈춘 `REVIEW_REQUIRED` Outbox 이벤트를 운영자가 DB를 직접 수정하지 않고, 권한·사유·버전 검증과 감사로그를 거쳐 안전하게 다시 처리합니다. ## 쉽게 설명하면 현재는 반복 실패한 이벤트를 서버가 안전을 위해 멈춥니다. 이후 재처리 기능이 없으므로 DB 값을 사람이 직접 바꾸면 누가 왜 다시 실행했는지 남지 않고 중복 실행 위험이 생깁니다. 이 이슈는 승인된 운영 절차를 API로 만드는 후속 작업입니다. ## 시작 전 결정 - [x] 재처리 허용 역할을 `ADMIN`으로 확정했습니다. - [x] 운영 API를 `POST /api/v1/admin/outbox-events/{eventId}/retry`로 확정하고 OpenAPI에 노출했습니다. - [x] 재처리 사유를 10~300자 필수값으로 정하고 변경 불가 재처리 이력에 보존합니다. ## 구현 범위 - [x] `REVIEW_REQUIRED` 상태의 이벤트만 재처리할 수 있습니다. - [x] 요청에 `expected_version`과 재처리 사유를 필수로 받습니다. - [x] 현재 상태·버전·lease 보유 여부를 transaction 안에서 다시 검증합니다. - [x] 상태를 `PENDING`으로 바꾸고 다음 실행시각·오류 정보·시도 횟수를 일관되게 갱신합니다. - [x] 누가, 언제, 어떤 이벤트를, 어떤 사유로 재처리했는지 Audit Event와 별도 재처리 이력에 남깁니다. - [x] payload·개인정보·token·예외 원문은 응답과 감사로그에 남기지 않습니다. - [x] `Idempotency-Key`, version, transaction 잠금으로 중복 제출과 동시 재처리를 방지합니다. - [x] 성공·권한 없음·상태 충돌·버전 충돌·중복·동시 요청 통합 테스트를 작성했습니다. - [x] `docs/reliability/transactional-outbox.md` 운영 runbook과 OpenAPI를 갱신했습니다. ## 하지 않는 것 - 일반 사용자의 이벤트 조회 화면 - DB에서 상태를 직접 `PENDING`으로 변경하는 절차 - 실패 payload나 개인정보 원문 노출 - heartbeat 또는 장시간 handler lease 연장 구현 ## 완료 조건 - [x] 승인된 운영자만 사유와 함께 재처리할 수 있습니다. - [x] 잘못된 상태·오래된 version·동시 요청은 안전하게 거부됩니다. - [x] 재처리 전후 상태와 actor·requestId가 감사로그로 추적됩니다. - [x] DB 직접 수정 없이 장애 복구 절차를 수행할 수 있습니다. ## 완료 검증 - 구현 PR: #102 - 검증 기준: `origin/main` `2516f3a` - 2026-08-07 `./gradlew clean test` 통과 - PostgreSQL 17 환경에서 전용 migration·RLS 테스트를 포함한 `./gradlew clean test` 통과 - `OutboxManualRetryApiIntegrationTest`에서 ADMIN 권한, tenant 격리, 필수 헤더, version·상태 충돌, 멱등성, 동시 요청, 감사로그, 최대 횟수 이후 실제 handler 재실행을 검증했습니다. ## 관계 - 선행: #25 Transactional Outbox, #11 Approval·Audit - RLS 연동: #34 tenant-safe Outbox 접근 - 관련 리뷰: #50
한 줄 목표
자동 재시도를 멈춘
REVIEW_REQUIREDOutbox 이벤트를 운영자가 DB를 직접 수정하지 않고, 권한·사유·버전 검증과 감사로그를 거쳐 안전하게 다시 처리합니다.쉽게 설명하면
현재는 반복 실패한 이벤트를 서버가 안전을 위해 멈춥니다. 이후 재처리 기능이 없으므로 DB 값을 사람이 직접 바꾸면 누가 왜 다시 실행했는지 남지 않고 중복 실행 위험이 생깁니다. 이 이슈는 승인된 운영 절차를 API로 만드는 후속 작업입니다.
시작 전 결정
ADMIN으로 확정했습니다.POST /api/v1/admin/outbox-events/{eventId}/retry로 확정하고 OpenAPI에 노출했습니다.구현 범위
REVIEW_REQUIRED상태의 이벤트만 재처리할 수 있습니다.expected_version과 재처리 사유를 필수로 받습니다.PENDING으로 바꾸고 다음 실행시각·오류 정보·시도 횟수를 일관되게 갱신합니다.Idempotency-Key, version, transaction 잠금으로 중복 제출과 동시 재처리를 방지합니다.docs/reliability/transactional-outbox.md운영 runbook과 OpenAPI를 갱신했습니다.하지 않는 것
PENDING으로 변경하는 절차완료 조건
완료 검증
origin/main2516f3a./gradlew clean test통과./gradlew clean test통과OutboxManualRetryApiIntegrationTest에서 ADMIN 권한, tenant 격리, 필수 헤더, version·상태 충돌, 멱등성, 동시 요청, 감사로그, 최대 횟수 이후 실제 handler 재실행을 검증했습니다.관계