[BE-12] 매칭 기준 CRUD API — 직무 키워드 · 제외어 · 근무형태 키워드

작업 내용 (설계 의도)

근거 TDD: 20260722-타깃-공고-알림-및-지원-히스토리-tdd.md — “API 계약”, “클래스 역할 정의”

변경 사항

사용자가 매칭 기준을 등록·변경하는 API입니다. 기준이 바뀌면 저장된 공고 전체를 재평가할 수 있어야 하므로(FR-25), 변경 시 criteriaRevision을 1 증가시켜 재평가 대상 판별의 기준점을 만듭니다. 실제 재평가 수행은 BE-13이 담당하고, 이 티켓은 기준 소유와 revision 관리만 책임집니다.

핵심 설계 의도:

  • 동의어 그룹(FR-22): “백엔드 / Backend / 서버 / Server”를 하나의 매칭 조건으로 묶습니다. 그룹 단위로 등록·삭제하며, 동의어 추가도 그룹 수정으로 처리합니다.
  • 제외 키워드(FR-23): “인턴 / 계약직” 등. 제외가 매칭을 이깁니다 — 판정 로직 자체는 BE-03의 JobKeywordMatcher에 있고 여기서는 등록만 합니다.
  • 근무형태 키워드(FR-26): “재택 / 원격” 등. 이 키워드는 ① 근무형태 정보를 추출할 대상 지정 ② 목록 정렬·표시 기준의 두 용도로 쓰입니다. 알림 조건도 필터도 아닙니다(FR-33).
  • 등록 시 NormalizedKeyword.of(raw)로 정규화 값을 함께 저장합니다 — 매 평가마다 정규화를 재계산하지 않기 위함입니다(FR-24).
  • 응답에 항상 criteriaRevision을 포함해 사용자가 “재평가가 필요한 상태”임을 인지할 수 있게 합니다.
  • 같은 키워드 중복 등록은 멱등하게 처리합니다(기존 반환, revision 미증가).

FE 계약 요청 수용분 (blocking 2건)

  • GET /api/matching/criteria 신설 — 현재 기준 전체(그룹+동의어, 제외어, 근무형태 키워드, criteriaRevision)를 한 응답으로 반환합니다. 조회 수단이 없으면 매칭 설정 화면이 “무엇이 등록돼 있는지” 표시할 수 없어 화면 자체가 성립하지 않습니다.
  • 제외어·근무형태 키워드 DELETE 신설DELETE /api/matching/exclusion-keywords/{id}, DELETE /api/matching/work-arrangement-keywords/{id}. 등록만 되고 삭제가 안 되면 오타·변심을 되돌릴 수 없고, 그룹만 삭제 가능한 비대칭이 됩니다.

삭제는 전부 소프트 삭제(deleted_at 기록)입니다. 과거 평가 결과가 job_keyword_group_id·excluded_keyword_id·work_arrangement_keyword_id를 참조하므로 물리 삭제하면 이력 참조가 끊깁니다. 조회(loadCurrent·GET criteria)는 deleted_at IS NULL만 반환하고, 삭제된 키워드를 참조하던 과거 평가 결과는 그대로 조회됩니다.

범위: MatchCriteriaApiController, 키워드 조회·등록·삭제 UseCase 7종, MatchCriteriaDomainService. 평가 로직은 BE-03(도메인)·BE-13(실행)에 있습니다.

롤백: 소프트 삭제이므로 잘못 삭제한 키워드는 deleted_at을 NULL로 되돌려 복구합니다(복구 API는 P0 범위 밖 — DB 조작).

의존

  • BE-03

다이어그램

처리 흐름

sequenceDiagram
    participant U as 사용자
    participant C as MatchCriteriaApiController
    participant UC as RegisterJobKeywordGroupUseCase
    participant D as MatchCriteriaDomainService
    participant R as MatchCriteriaRepository
    U->>C: POST /api/matching/keyword-groups
    C->>UC: execute(command)
    UC->>D: registerKeywordGroup(command)
    D->>D: NormalizedKeyword.of(각 동의어)
    alt 이미 존재하는 그룹
        D-->>UC: 기존 그룹 (revision 미증가)
    else 신규
        D->>R: save(group)
        D->>R: bumpRevision()
    end
    UC-->>C: {groupId, criteriaRevision}

클래스 의존

flowchart LR
    subgraph Presentation["presentation/matching"]
        Api[MatchCriteriaApiController]
    end
    subgraph Application["application/matching"]
        UC0[GetMatchCriteriaUseCase]
        UC1[RegisterJobKeywordGroupUseCase]
        UC2[DeleteJobKeywordGroupUseCase]
        UC3[Register 키워드 UseCase 2종]
        UC4[Delete 키워드 UseCase 2종]
    end
    subgraph Domain["domain/matching"]
        DS[MatchCriteriaDomainService]
        Group[JobKeywordGroup]
        Norm[NormalizedKeyword]
        Repo[MatchCriteriaRepository]
    end
    Api --> UC0
    Api --> UC1
    Api --> UC2
    Api --> UC3
    Api --> UC4
    UC0 --> DS
    UC1 --> DS
    UC2 --> DS
    UC3 --> DS
    UC4 --> DS
    DS --> Group
    DS --> Norm
    DS --> Repo

테스트 케이스

  • 동의어 4개를 가진 키워드 그룹을 등록하면 그룹과 동의어가 저장되고 criteriaRevision이 1 증가한다
  • 등록 시 각 동의어의 정규화 값이 함께 저장된다
  • 같은 그룹명을 재등록하면 멱등하게 기존 그룹을 반환하고 revision이 증가하지 않는다
  • 기준 조회가 그룹+동의어·제외어·근무형태 키워드·현재 revision을 한 응답으로 반환한다
  • 기준이 하나도 없으면 조회가 빈 배열과 revision을 반환한다(404가 아니다)
  • 그룹을 삭제하면 deleted_at이 기록되고 조회 결과에서 사라지며 revision이 증가한다
  • 삭제된 그룹을 참조하던 과거 평가 결과는 여전히 조회된다(참조 무결성 유지)
  • 제외 키워드를 등록하면 저장되고 revision이 증가한다
  • 제외 키워드를 삭제하면 소프트 삭제되고 조회 결과에서 제외된다
  • 근무형태 키워드를 등록하면 저장되고 revision이 증가한다
  • 근무형태 키워드를 삭제하면 소프트 삭제되고 조회 결과에서 제외된다
  • 이미 삭제된 키워드를 다시 삭제하면 멱등하게 처리된다(revision 미증가)
  • 빈 문자열·공백만 있는 키워드는 400으로 거부된다
  • 존재하지 않는 그룹 삭제는 404를 반환한다
  • 응답 본문에 항상 현재 criteriaRevision이 포함된다
  • 대소문자만 다른 동일 키워드는 정규화 후 중복으로 판정되어 멱등 처리된다