[BE-11] 마감일 경과 판정 경량 배치
작업 내용 (설계 의도)
근거 TDD: 20260722-타깃-공고-알림-및-지원-히스토리-tdd.md — “방안 3 마감 판정”, “상태 전이 표 / JobPosting”
변경 사항
마감일이 이미 지난 공고를 DB 데이터만으로 CLOSED 전환하는 경량 배치입니다(FR-17). 수집 배치(BE-10)와 의도적으로 분리합니다 — 이 판정은 외부 소스 호출이 필요 없으므로 수집이 며칠 깨져 있어도 계속 정확히 동작합니다. 수집 실패에 마감 판정이 종속되지 않게 하는 것이 분리의 목적입니다.
핵심 설계 의도:
- 상시채용(
deadlineAt == null)은 판정 대상에서 제외합니다(FR-17). 도메인의isPermanentPosting()으로 판정하고, 조회 쿼리에서도deadline_at IS NOT NULL조건을 걸어 이중 방어합니다. 어댑터의 마감일 정규화(FR-13)가 제대로 동작하지 않아9999-12-31이 들어오면 이 배치가 영원히 동작하지 않으므로, 정규화와 이 배치는 짝을 이룹니다. - origin과 무관하게 동작합니다 — 수동 등록 공고(FR-20)도 마감일을 입력했으면 경과 시 CLOSED됩니다. 델타 비교와 달리 이 판정은 소스 존재 여부에 의존하지 않으므로 수동 공고를 포함해도 안전합니다.
- 이미 CLOSED인 공고는 no-op입니다. 물리 삭제는 하지 않습니다(FR-18, NFR-5).
- 피처 플래그
posting.auto-close를 동일하게 존중합니다 — 마감 판정을 멈춰야 할 때 두 경로(델타·마감일)가 함께 멈춰야 합니다. - 실행 시각은 00:30 KST로, 자정 수집(00:00) 이후에 배치합니다.
TaskScheduler풀 크기가 1이라 수집이 길어져도 직렬 실행됩니다.
범위: CloseDeadlinePassedJobPostingsUseCase, JobPostingDeadlineDomainService, JobPostingDeadlineSweepScheduler(cron 0 30 0 * * *).
롤백: 피처 플래그 OFF. 오전환된 공고는 소프트 삭제라 상태 복구로 되돌립니다.
의존
- BE-02
다이어그램
처리 흐름
sequenceDiagram participant S as DeadlineSweepScheduler participant U as CloseDeadlinePassedUseCase participant D as DeadlineDomainService participant F as FeatureFlagGateway participant R as JobPostingRepository participant P as JobPosting S->>U: execute() U->>D: closePassed(now) D->>F: isEnabled("posting.auto-close") alt 플래그 OFF F-->>D: false → 0건 처리 후 종료 else 플래그 ON D->>R: findAllOpenWithDeadlineBefore(now) loop 대상 공고 D->>P: closeByDeadline() P-->>D: DEADLINE_PASSED 전이 (이미 CLOSED면 no-op) end D->>R: saveAll(전환 공고) end D-->>U: 전환 건수
클래스 의존
flowchart LR subgraph Presentation["presentation/posting"] Sched[DeadlineSweepScheduler] end subgraph Application["application/posting"] UC[CloseDeadlinePassedJobPostingsUseCase] end subgraph Domain["domain/posting"] DS[JobPostingDeadlineDomainService] Posting[JobPosting] Repo[JobPostingRepository] Flag[FeatureFlagGateway] end Sched --> UC UC --> DS DS --> Posting DS --> Repo DS --> Flag
테스트 케이스
- 마감일이 어제인 OPEN 공고가 CLOSED +
closedReason=DEADLINE_PASSED로 전이한다 - 마감일이 내일인 공고는 전이하지 않는다
deadlineAt == null(상시채용) 공고는 판정 대상에서 제외된다- 이미 CLOSED인 공고는 다시 처리되지 않고
closedAt이 덮어써지지 않는다 - 수동 등록 공고도 마감일이 지났으면 CLOSED로 전이한다
- 소스 수집이 전부 실패한 상태에서도 이 배치는 정상 동작한다
- 피처 플래그
posting.auto-close=false면 전이 건수가 0이다 - 마감일 경계값(현재 시각과 동일)에서는 아직 전이하지 않는다
- 전환 건수가 반환값으로 집계된다