[BE-68] 단계 3 공통 계약 — 에러 코드·알림 enum 확장·설정·플래그 시드

작업 내용 (설계 의도)

근거 TDD: 20260808-지원관리-확장-tdd.md — “신규 에러 코드”, “notification enum 확장”, “Release Scenario 3단계”

변경 사항

단계 3의 유일한 병목입니다. 후행 5개 티켓이 참조하는 공통 산출물을 한 wave에 확정합니다. 특히 NotificationType·TargetType enum 확장을 여기서 끝내야 알림 2종 티켓(BE-73·76)이 병렬로 열립니다.

  1. 신규 예외·에러 코드ContactEventNotFoundException·ContactEventAlreadyDecidedException·ContactEventTargetTerminalException(domain/contact) 정의 + GlobalExceptionHandler 매핑(404·409·409).
  2. NotificationType 확장JOB_POSTING_CHANGED(FR-93), CONTACT_REVIEW_REQUEST(FR-94) 추가. TargetType 확장CONTACT_EVENT(FR-94 B-27) 추가. 기존 멱등 키 규칙 {target_type}:{target_id}:{notification_type}:{dispatch_sequence}를 그대로 확장 적용합니다(IdempotencyKey.kt:31).
  3. notification_dispatches 컬럼 COMMENT 갱신 — 코드값 목록에 신규 4종(타입 2 + 대상 1)을 반영합니다. DDL 구조 변경이 아니라 주석 갱신이므로 락 영향이 없습니다.
  4. 설정recruitment.contact.review-base-url(Discord 알림 링크가 가리킬 검토 화면 URL — 터널 공개 호스트명 기준), recruitment.matching.field-weight-*(가중치·임계치를 상수 대신 설정으로 뺄지 여부는 미채택이므로 설정하지 않고 도메인 상수 유지 — 이 판단을 주석으로 남김).
  5. 피처 플래그 시드 5행matching.field-weighted-scope·contact.inbound-webhook·notification.job-posting-changed·notification.contact-review-request·source.registry-status 전부 enabled=0.
  6. WEBHOOK_HMAC_SECRET 환경 변수를 docker compose(dev·prod)에 추가합니다. BE-47이 만든 WebhookSecretGateway가 이 값을 읽습니다.

롤백: 플래그 5행 DELETE + compose 되돌리기. enum 확장은 값 추가뿐이라 기존 코드가 깨지지 않습니다.

의존

  • DB-04 (단계 3 스키마 마이그레이션 — senior-dba 몫)

다이어그램

처리 흐름

sequenceDiagram
    participant D as NotificationDispatchDomainService
    participant K as IdempotencyKey
    participant R as NotificationDispatchRepository
    D->>K: of(CONTACT_EVENT, eventId, CONTACT_REVIEW_REQUEST, 1)
    K-->>D: "CONTACT_EVENT:42:CONTACT_REVIEW_REQUEST:1"
    D->>R: findSucceededBy(key)
    alt 이미 발송
        R-->>D: 기존 dispatch → SKIPPED
    else 미발송
        D->>R: save(attempt)
    end

클래스 의존

flowchart LR
    subgraph Config["config"]
        Handler[GlobalExceptionHandler]
    end
    subgraph Domain["domain"]
        NotiType[NotificationType]
        TargetType[TargetType]
        Key[IdempotencyKey]
        CNF[ContactEventNotFoundException]
        CAD[ContactEventAlreadyDecidedException]
        CTT[ContactEventTargetTerminalException]
    end
    Handler --> CNF
    Handler --> CAD
    Handler --> CTT
    Key --> NotiType
    Key --> TargetType

테스트 케이스

  • ContactEventNotFoundException이 404 CONTACT_EVENT_NOT_FOUND로 매핑된다
  • ContactEventAlreadyDecidedException이 409 CONTACT_EVENT_ALREADY_DECIDED로 매핑된다
  • ContactEventTargetTerminalException이 409 CONTACT_EVENT_TARGET_TERMINAL로 매핑된다
  • NotificationType.JOB_POSTING_CHANGED·CONTACT_REVIEW_REQUEST가 enum에 존재한다
  • TargetType.CONTACT_EVENT가 enum에 존재한다
  • IdempotencyKey.of(CONTACT_EVENT, 42, CONTACT_REVIEW_REQUEST, 1)"CONTACT_EVENT:42:CONTACT_REVIEW_REQUEST:1"을 만든다
  • 기존 알림 3종의 멱등 키 생성이 변경되지 않는다 (회귀)
  • 기존 예외 매핑이 변경되지 않는다 (회귀)
  • 피처 플래그 5행이 enabled=0으로 시드된다
  • WEBHOOK_HMAC_SECRET 미설정 시 웹훅 검증이 기동은 되지만 항상 실패한다 (fail-safe)