Skip to content

[FEAT] 메일 발송 내구성 및 재시도 처리 #670

Closed

Description

@023-dev

📃 요약

메일 발송 요청과 외부 SES 호출 사이의 장애 구간을 보완합니다. 서버 재기동·프로세스 장애·일시적 SES 오류가 발생해도 작업 결과를 잃거나 중복 발송하지 않도록 영속적인 실행 경계를 마련합니다.

#629는 요청 시점의 즉시 발송과 기본 결과 저장까지만 담당합니다. 이 이슈에서 내구성 있는 비동기 실행과 재시도 정책을 별도로 설계·구현합니다.

구현 범위

  • DB 저장과 외부 메일 호출 사이의 dual-write 문제를 해결하는 Outbox 또는 동등한 영속 큐 구조
  • 발송 작업과 수신자 대상의 재기동 복구·claim·lease·중복 실행 방지
  • SES 오류를 재시도 가능 오류와 영구 실패로 분류
  • 최대 재시도 횟수, backoff, 대상별 최종 실패 상태와 실패 사유 저장
  • 같은 requestedByAdminId와 Idempotency-Key로 다른 요청 본문을 보낸 경우 기존 결과를 재사용하지 않고 충돌로 거부
    • 비교 대상: scenarioId, recruitId, applyIds, 제목 덮어쓰기, 입력 변수
  • 재시도와 복구 결과를 관찰할 수 있는 로그·메트릭·알림 기준

완료 조건

  • 작업 저장 직후 프로세스가 중단되어도 재기동 후 미처리 대상을 복구한다.
  • 동시 워커가 같은 대상을 중복 처리하지 않는다.
  • 재시도 가능 오류만 정책에 따라 재시도하고 영구 실패는 즉시 종료한다.
  • 최대 재시도 횟수와 다음 시각을 저장·검증할 수 있다.
  • 같은 idempotency key의 요청 본문이 다르면 명확한 충돌 오류를 반환한다.
  • 외부 호출 성공 후 DB 기록 실패, DB 기록 후 프로세스 중단 등 장애 시나리오를 테스트한다.
  • 운영자가 작업 상태와 재시도·복구 결과를 추적할 수 있다.

이번 이슈에서 하지 않는 것

  • 메일 템플릿 CRUD 또는 대상자 조회
  • 발송 이력 조회 API 자체의 화면·검색 기능
  • 예약 시각 기반 스케줄러 정책
  • SES 외 공급자 추가

📎 참고 자료 (선택)

Activity

  1. self-assigned this
    on Sep 7, 2026
  2. 023-dev commented on Sep 9, 2026

    @023-dev
    MemberAuthor

    후속 구현 설계 기준

    #670 구현 전 다음 경계를 기준으로 확정합니다.

    1. 원자적 저장: 발송 작업·수신자 대상·실행 큐 레코드를 하나의 DB 트랜잭션으로 저장합니다. 외부 SES 호출은 커밋 이후 워커가 수행합니다.
    2. 대상 단위 claim: 대상 상태를 PENDING → PROCESSING → SENT/FAILED로 관리하고, attempt_count, next_attempt_at, lease_until, 마지막 안전 오류 코드를 저장합니다. lease가 만료된 PROCESSING은 재기동 시 회수합니다.
    3. 재시도 정책: 네트워크 오류·SES 5xx·throttling만 지수 backoff와 최대 횟수까지 재시도합니다. 주소 형식·수신 거부·4xx 영구 오류는 즉시 FAILED로 종료합니다.
    4. 멱등성: requestedByAdminId + Idempotency-Key를 unique로 유지하고, canonical JSON fingerprint가 다르면 충돌 오류를 반환합니다. 비교 대상은 scenarioId, recruitId, applyIds, 제목 덮어쓰기, 입력 변수입니다.
    5. 동시성: 워커 claim은 DB 잠금 또는 원자적 상태 변경으로 한 대상만 획득하게 합니다. 재시도·복구가 같은 대상을 동시에 처리하지 않는지 검증합니다.
    6. 관찰성: 작업·대상 ID, 시도 횟수, 결과 코드만 로그에 남기고 이메일 주소·SES 원문 오류·요청 본문은 남기지 않습니다. claim, retry, exhausted, recovery 지표를 추가합니다.
    7. 장애 테스트: 저장 직후 중단, SES 성공 후 결과 기록 전 중단, DB 기록 실패, lease 만료 복구, 동시 워커, 재시도 한도 초과를 테스트합니다.

    별도 결정 필요

    외부 SES 성공 직후 DB 기록 전에 프로세스가 중단되면, SES 자체 멱등성 키가 없어 외부 발송의 정확히 한 번 처리를 보장할 수 없습니다. 따라서 구현 PR 시작 전 at-least-once + 중복 완화를 허용할지, SES 호출 경계에 별도 중복 방지 수단을 둘지 결정해야 합니다.

    이번 단계에서는 설계 기준만 정리하고 Outbox·워커·재시도 코드는 별도 PR로 구현합니다.

  3. added 3 commits that reference this issue on Sep 22, 2026
  4. added 8 commits that reference this issue on Sep 30, 2026
  5. 17 remaining items

  6. added 6 commits that reference this issue on Oct 6, 2026
  7. added 2 commits that reference this issue on Oct 10, 2026
  8. added 6 commits that reference this issue on Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions