타깃 공고 알림 및 지원 히스토리 PRD
Background
구직 활동 중 관심 회사의 채용 공고를 사람이 직접 주기적으로 확인하는 방식은 두 가지 비효율을 만듭니다. 첫째, 확인 주기가 사람 손에 달려 있어 신규 공고·마감 임박을 놓치기 쉽습니다. 둘째, 지원 이력(어느 회사에 언제 지원했고 지금 어느 단계인지)을 스프레드시트 등으로 수동 관리하면 최신 상태를 놓치거나 회사별 기록이 흩어집니다.
본 프로젝트는 1인 사용자가 지정한 회사·직무 조건에 맞는 공고를 매일 자동 수집해 디스코드(Discord)로 알리고, 근무형태(재택·원격 등)는 라벨로 표기해 판단을 돕습니다. 또한 지원 여부와 무관하게 공고 이력을 보관하며, 지원한 건은 상태 전이·면접 회차·자소서 진행 상황까지 한곳에서 추적하는 개인용 도구입니다.
사전 조사(20260722-채용소스-조사-브리프.md)로 실제 4개 회사(당근·배민·우리은행·수협은행)의 채용 데이터 소스를 HTTP 호출로 검증했고, 이를 근거로 요구사항을 확정했습니다.
1단계(P0) 범위는 이후 애그리게이터(통합 채용 플랫폼) 수집을 포함하도록 확장되었습니다. 회사 종속형 소스와 달리 애그리게이터는 특정 회사에 속하지 않는 전역 소스이며 검색 조건으로 수집합니다. 플랫폼 11종의 실측 조사 결과(기술적 가능 여부와 규약상 허용 여부 판정 포함)는 조사 브리프의 “애그리게이터 소스 조사” 절에 있습니다.
Problem Definition
AS-IS (현재의 불편)
- 관심 회사 채용 페이지를 사람이 직접 주기적으로 방문해 신규 공고·마감일을 확인합니다. 확인을 거를 경우 지원 기회를 놓칩니다.
- 상시채용 공고(마감일 없음)는 “이미 확인했다”는 착각으로 재확인 주기를 놓치기 쉽습니다.
- 지원 이력은 개인 메모·스프레드시트 등에 흩어져 있어, 특정 회사에 대한 지원 상태·면접 회차·자소서 진행 여부를 한눈에 파악하기 어렵습니다.
- 직무 키워드를 나중에 바꾸면(예: “백엔드”만 보다가 “서버 개발”도 포함하기로 결정) 과거에 스쳐 지나간 공고를 다시 찾을 방법이 없습니다.
- 회사마다 채용 사이트 구조가 달라(공식 API/내부 API/HTML/로그인 필요) 수동 확인 난이도가 회사마다 다릅니다.
문제 정의
“등록한 회사의 원하는 조건 공고를 놓치지 않고, 지원한 뒤에는 그 진행 상황을 잊지 않고 추적할 수 있는가”가 핵심 문제입니다. 회사별로 채용 사이트 구조가 제각각이라는 기술적 제약(브리프 확인)과, 매칭 조건이 계속 바뀔 수 있다는 사용자 행태를 함께 다뤄야 합니다.
Goals / Non-Goals
Goals
- 등록된 회사·직무 조건에 맞는 신규 공고를 매일 자동 수집해 디스코드로 알리고, 근무형태는 라벨로 표기해 판단을 돕는다(필터·알림 조건이 아니다).
- 직무 키워드·근무형태 조건에 맞는 공고와 그렇지 않은 공고를 모두 저장해, 조건을 나중에 바꿔도 과거 공고를 재매칭할 수 있게 한다.
- 마감(상태 변화)을 감지해 마감일이 없는 상시채용 공고는 7일 주기로 반복 리마인드하고, 마감일이 있는 공고는 D-1에 별도 알림한다.
- 지원한 공고는 상태 전이(서류전형 → 면접 → 처우협의 → 최종 합격/불합격/사퇴/오퍼 거절)와 면접 회차를 개별 기록으로 남긴다.
- 자소서를 지원 전 초안 작성 → 제출 흐름으로 관리한다.
- 소스(채용 사이트)가 고장(파서 실패·스키마 변경)났을 때 이를 감지해 알린다.
- 자동 수집이 불가능한 공고(로그인 필요·자소서 필수·매핑 후보 0건)는 수동 등록으로 동일하게 지원 관리 대상에 포함한다.
- 애그리게이터(통합 채용 플랫폼)를 통해 사용자가 아직 등록하지 않은 회사의 공고도 발견한다. 다만 이 도구의 본질은 “타깃 구직”이므로, 사용자가 직접 등록한 관심 회사가 항상 우선이며 애그리게이터로 자동 등록된 발견된 회사는 보조적으로 취급한다(개별 알림이 아닌 일일 요약으로 구분).
한글 단계명 ↔ 상태값 매핑
| 한글 표기 | 상태값 |
|---|---|
| 지원 완료 | APPLIED |
| 서류전형 | DOCUMENT_SCREENING |
| 면접 | INTERVIEWING |
| 처우협의 | OFFERED |
| 최종 합격 | ACCEPTED |
| 불합격 | REJECTED |
| 지원 사퇴 | WITHDRAWN |
| 오퍼 거절 | OFFER_DECLINED |
Non-Goals
- 멀티유저·회원가입 미지원 — 1인용 도구로, 계정 체계·권한 분리를 만들지 않는다.
- 자동 지원서 제출 미지원 — 지원 버튼을 자동으로 누르거나 폼을 자동 제출하지 않는다. 지원은 사용자가 직접 수행하고, 이 도구는 상태 기록만 담당한다.
- 헤드리스 브라우저 도입 안 함 — 현재 검증된 소스(Greenhouse·배민 내부 API·인크루트 HTML) 모두 서버 렌더링 또는 JSON API로 충분하다. 헤드리스 브라우저는 향후 SPA 전용 소스가 실제로 등장했을 때 검토할 최후 수단(브리프의 ④단계)이며, 이번 범위에 포함하지 않는다. FR-86(매칭 범위 확대로 기업 자사 채용 사이트 수집이 늘어남)와 무관하게 SPA 채용 사이트는 계속 수집 대상에서 제외하고 소스 상태
미지원(FR-88)으로 남긴다. - 메시지 브로커·별도 캐시 계층·별도 워커 프로세스를 도입하지 않는다 — 이 규모(회사 10
20곳, 1일 1회 배치)에서는 과잉 설계다. 구체적인 실행 방식은 기술 설계 문서(TDD)에서 다룬다. FR-7097(2026-08-08 편입) 이후에도 처리 규모(단일 사용자, 배치 1일 1~2회)는 바뀌지 않으므로 이 제약을 그대로 유지한다. - 로그인 필요 공고의 자동 수집 미지원 — 로그인이 필요한 비공개 공고(예: 우리은행 인크루트 비공개 공고)는 자동 수집 대상에서 제외하고 수동 등록(FR-20)으로 처리한다. FR-89~92(이메일·문자 연락 확인형 반영)은 로그인 필요 채용 사이트를 자동 수집하는 것이 아니라 사용자 본인 메일함의 연락 이벤트를 다루므로 이 비목표와 무관하다.
- 전체 공고 일괄 적합도 평가 미지원 — 이력서 적합도 평가(FR-53~58)는 공고 1건 × 이력서 1건 단위로 사용자가 수동 실행할 때만 동작한다. “전체 공고 한 번에 평가” 같은 일괄 실행 버튼은 만들지 않는다 — 외부 API 호출 비용이 공고 수에 비례해 폭증하고, 수동 실행 원칙(FR-54)과 어긋난다.
[폐기] FR-53~58 폐기(아래 “이력서 기반 서류 적합도 평가” 절 참조)로 이 비목표의 기각 사유(외부 API 비용 폭증)가 해소되었습니다. FR-97는 관심 공고(관리 상태
INTERESTED/PLANNED) 단위 일괄 재평가를 규칙 기반으로 지원합니다(B-20, 2026-08-08). 단 “관심 아닌 전체 공고 일괄 평가”는 계속 비목표입니다(오버스펙 방지). - 외부 LLM API 호출은 2단계(P1)에서 온디맨드로 도입한다 — 위 “메시지 브로커·캐시 계층·별도 워커 프로세스 미도입”과는 별개다. 이력서 적합도 평가에 한해, 사용자가 평가 버튼을 누른 시점에만 외부 LLM API를 동기 호출하며, 상시 구동되는 인프라를 추가하지 않는다.
[수정 — 규칙 기반 전환] 지원 추천도(FR-95~97, 2026-08-08 편입)는 설명 가능한 규칙 기반으로 산출하며 외부 LLM API 동기 호출을 전제하지 않습니다(B-23). AI 보조 추출은 이력서 텍스트 → 프로필 초안 단계의 선택 기능으로만 남고, 사용자의 명시적 활성화 전에는 실행하지 않습니다(2단계 이후 후속, P2).
- 채용담당자용 ATS(Applicant Tracking System)·다중 사용자 협업 미지원 — 이 도구는 구직자 개인의 지원 관리 도구이며, 채용담당자가 지원자를 심사·관리하는 기능이나 여러 사용자가 동시에 협업하는 기능은 제공하지 않는다. (기본값 채택 —
docs/PRD.md§26-30 비목표 편입, 2026-08-08) - 수신 연락 자동 상태 변경 미지원 — 이메일·문자 연락 이벤트 수신만으로 지원 상태를 자동 변경하지 않는다. FR-89~91은 후보를 계산해 Discord로 검토를 요청할 뿐, 사용자가
반영을 선택해야만 상태가 바뀐다. (기본값 채택 —docs/PRD.md§30·수용 기준 3 편입, 2026-08-08)
User Scenarios
페르소나
단일 페르소나: 여러 회사에 동시에 지원을 준비하는 구직자 본인(시스템 유일 사용자).
시나리오 1 — 회사 등록 (해피 패스)
- 사용자가 UI에서 회사명 “당근”을 입력한다.
- 시스템이 slug 프로빙으로 후보 소스를 탐색하고, 후보 목록과 각 후보의 샘플 공고 3건을 미리보기로 제시한다.
- 사용자가 Greenhouse 소스(
daangn)를 확정한다. - 시스템이 회사-소스 매핑을 저장하고, 다음 자정 수집 대상에 포함한다.
시나리오 2 — 일일 수집과 신규 공고 알림 (해피 패스)
- 자정 배치가 등록된 모든 소스를 수집한다.
- 신규 공고를 발견하면 저장하고, 사용자가 설정한 발송 시각(09:00 KST)에 디스코드로 알린다.
- 직무 키워드에 매칭되지 않는 공고도 함께 저장하되 알림은 보내지 않는다.
- 최초 등록 직후 첫 수집은 시딩으로만 처리하고 알림을 보내지 않는다 (기존 공고 전체가 “신규”로 오인되어 알림이 쏟아지는 것을 방지).
시나리오 3 — 지원 및 상태 추적 (해피 패스, P0 최소 경로)
- (P0) 사용자가 저장된 공고 중 하나에 지원 기록(Application)을 생성하고 상태를
APPLIED로 기록한다. - (P0) 서류 합격 통보를 받으면 상태를
DOCUMENT_SCREENING으로 전이하고, 전이 이력(이전 상태·다음 상태·시각·메모)을 남긴다. - (P0) 면접이 잡히면 별도 면접 회차 기록(1차, 일정, 결과)을 추가한다.
- (P0) 서류·면접 단계에서 탈락하면
REJECTED로 전이하며 탈락 시점 단계를 함께 기록한다. 계속 진행되면INTERVIEWING→OFFERED(처우협의)를 거쳐ACCEPTED(최종 합격) 또는OFFER_DECLINED(오퍼 거절)로 전이한다.
P1에서 추가되는 흐름(위 최소 경로에 얹히는 확장):
- (P1) 지원 전 자소서 초안을 작성하고, 제출 완료 시 위 1번의
APPLIED지원 기록과 연결한다(FR-49). - (P1) 지원 시점부터 해당 공고의 D-1·리마인드 알림이 자동 중단된다(FR-39).
시나리오 4 — 예외: 소스 고장
- 배민 소스가 비공식 API 스키마 변경으로 3일 연속 0건을 반환한다.
- 마감 판정 로직은 이 소스에 대한 마감 판정을 건너뛴다 (소스 가드) — 정상 공고가 스키마 오류로 무더기 마감 처리되는 것을 막는다.
- 시스템이 “배민 소스 3일 연속 수집 실패” 알림을 디스코드로 발송한다.
- 사용자가 어댑터를 점검하고 수정할 때까지 해당 소스의 신규 공고 알림은 발생하지 않지만, 기존 저장 데이터는 보존된다.
시나리오 5 — 예외: 매핑 후보 0건
- 사용자가 UI에서 소규모 지역 스타트업명을 입력한다.
- slug 프로빙 결과 후보가 0건이다.
- 시스템이 “자동 매핑 실패 — 수동 등록 전용으로 저장” 안내를 표시한다.
- 회사는 등록되지만 자동 수집 대상에서 제외되고, 사용자가 채용 공고를 발견하면 수동 공고 등록 기능(FR-20)으로 회사·공고명·링크·마감일을 입력해 히스토리에 추가할 수 있다.
시나리오 6 — 예외: 로그인 필요 공고 발견
- 우리은행 인크루트 소스 수집 중 특정 공고 URL이 로그인 페이지로 리다이렉트된다.
- 어댑터가 이 공고를 “접근 제한”으로 표시하고 자동 매칭·자동 마감 판정 대상에서 제외한다.
- 시스템이 “로그인 필요 공고 발견 — 수동 확인 필요” 알림을 발송한다.
- 사용자가 직접 로그인해 공고 내용을 확인하고, 수동 공고 등록 기능(FR-20)으로 공고를 등록해 지원 기록을 연결할 수 있다(FR-21).
시나리오 7 — 예외: 알림 발송 실패
- 디스코드 웹훅이 5xx 또는 rate limit(429) 응답을 반환한다.
- 시스템이 지수 백오프로 재시도한다 (최대 3회).
- 3회 모두 실패하면 실패 이력을 저장한다. 시스템은 다음 정상 발송 시점에 재발송을 시도하는 동시에, 실패 이력을 조회 화면에서 확인할 수 있게 한다 — 재시도와 조회 수단을 함께 제공한다(알림 자체가 실패하는 상황이므로 알림에 의존하지 않는 확인 수단이 필요하다).
시나리오 8 — 예외: 이력서 적합도 평가 실패 (P1)
[폐기 — 하위 흐름별 승계 처리] 이 시나리오가 전제하는 FR-53
58(이력서 적합도 평가)은 2026-08-08 FR-9597(지원 추천도)로 대체되어 폐기되었습니다(상세는 “이력서 기반 서류 적합도 평가” 절 참조). 하위 흐름 처리: 8-a(PDF 텍스트 추출 실패)는 시나리오 13으로 승계, 8-c(외부 API 실패)·8-d(처리 한도 초과)는 시나리오 17·18로 재정의 승계(선택 기능 AI 보조 추출 P2에 한정), 8-b(URL 접근 불가·형식 미지원)는 URL 입력 경로 폐지와 함께 폐기되어 승계 대상이 아닙니다. 아래 8-a~8-d 본문은 원본 그대로 보존하며 실제 구현 기준은 위 승계 시나리오를 따릅니다.
8-a. PDF 텍스트 추출 실패
- 사용자가 스캔 이미지 PDF(텍스트 레이어 없음)를 업로드한다.
- 시스템이 텍스트 추출 실패를 감지하고 “이력서에서 텍스트를 읽을 수 없습니다 — 다른 파일로 다시 업로드해 주세요”를 표시하며 등록을 저장하지 않는다.
- 사용자는 다른 PDF를 재업로드하거나 URL 경로로 대체 등록할 수 있다.
8-b. URL 접근 불가·형식 미지원
- 사용자가 이력서 URL을 입력한다.
- 접근 실패(404·타임아웃) 또는 지원하지 않는 형식(로그인 필요 페이지 등)이면 “이 URL에서 이력서를 가져올 수 없습니다”를 표시하고 등록을 저장하지 않는다.
- 사용자는 URL을 수정하거나 PDF 업로드로 전환할 수 있다.
8-c. 외부 API 호출 실패·타임아웃
- 사용자가 공고·이력서 버전을 선택하고 평가 버튼을 클릭한다.
- 외부 LLM API가 오류를 반환하거나 응답이 지연된다.
- 시스템은 “평가에 실패했습니다 — 잠시 후 다시 시도해 주세요”를 표시하고, 부분 결과를 저장하지 않는다.
- 사용자는 재시도 버튼으로 동일한 공고·이력서 조합을 다시 평가할 수 있다.
8-d. 처리 한도 초과
- 이력서 또는 공고 본문(JD)이 외부 API가 처리 가능한 길이를 초과한다.
- 시스템은 “내용이 너무 길어 평가할 수 없습니다”를 표시하고 평가를 실행하지 않는다.
- 원문 자체를 줄이지 않는 한 재시도해도 동일하게 실패하므로, 사용자에게 이력서 또는 JD 원문 축약이 필요함을 함께 안내한다.
시나리오 9 — 애그리게이터에서 미등록 회사 공고 발견 (해피 패스)
- 애그리게이터 소스(예: 사람인의 특정 직무 카테고리)를 수집한다.
- 수집된 공고의 회사가 아직 등록되지 않은 회사다.
- 시스템이 해당 회사를 “발견된 회사”로 자동 등록하고, 공고를 저장한다(FR-61).
- 이 공고가 직무 키워드에 매칭되면 개별 알림이 아니라 그날의 일일 요약 알림에 포함되어 발송된다(FR-62) — 관심 회사의 개별 알림과 구분된다.
- 사용자는 회사 목록에서 “관심 회사”와 “발견된 회사”를 구분해 조회할 수 있고, 마음에 드는 발견된 회사는 이후 관심 회사로 승격할 수 있다.
시나리오 10 — 크로스 소스 중복 공고 처리 (특수 흐름)
- 당근의 같은 채용 공고가 Greenhouse(회사 직접 소스)와 원티드(애그리게이터) 양쪽에서 수집된다.
- 시스템이 회사명·공고 제목·마감일을 근거로 두 공고를 동일 공고로 판정한다(FR-63).
- 회사 직접 소스인 Greenhouse 공고를 대표 공고로 선정한다(FR-64) — 애그리게이터보다 원문이 정확하고 필드가 풍부하기 때문이다.
- 신규 공고 알림은 먼저 발견된 소스 기준으로 1회만 발송한다. 원티드 쪽에서 같은 공고가 이후 수집돼도 중복 알림을 보내지 않는다(FR-65). 1단계에서는 이 판정을 사용자가 수동으로 정정할 수 없다.
시나리오 11 — 공고를 관심 → 지원 예정 → 지원으로 옮기는 흐름 (해피 패스, 2026-08-08 편입)
- 사용자가 매칭된 공고를 관리 상태
INTERESTED(관심)로 저장하고 우선순위HIGH, 지원 목표일, 개인 태그를 입력한다(FR-77). - 이력서를 검토한 뒤
PLANNED(지원 예정)로 전환한다. - 실제 지원을 마치면 지원 레코드(Application)를 생성한다 — 관리 상태는
APPLIED로 저장되지 않고, 지원 레코드 존재 여부로 파생 표시된다(FR-73). - 관리 상태 변경 시각이 이력으로 남아(FR-76) 대시보드의 “장기 미변경 지원” 집계에 활용된다.
시나리오 12 — 이력서 업로드 → 프로필 확정 → 추천도 확인 (해피 패스)
- 사용자가 PDF 이력서를 업로드한다(FR-82·95).
- 시스템이 텍스트를 추출해 기술 스택·경력·직무 선호 프로필 초안을 만든다.
- 사용자가 항목을 검토·수정하고
프로필 확정을 누른다 — 확정 전 초안은 추천도 계산에 사용되지 않는다. - 확정 즉시 새 평가 기준 버전이 생성되고 관심 공고 전체가 재평가되어, 각 공고에 지원 추천도(점수·등급·축별 근거)가 표시된다(FR-96).
시나리오 13 — 예외: 이력서 텍스트 추출 실패 (기존 시나리오 8-a 승계)
- 사용자가 스캔 이미지 PDF(텍스트 레이어 없음)를 업로드한다.
- 시스템이 텍스트 추출 실패를 감지하고 프로필 초안을 생성하지 않은 채 “이력서에서 텍스트를 읽을 수 없습니다 — 항목을 직접 입력해 주세요”를 표시한다(FR-95, B-22).
- 이력서 등록 자체는 저장되며, 사용자는 프로필 항목(기술명·경력·근거)을 수동으로 입력해 프로필을 완성할 수 있다.
시나리오 14 — 연락 이벤트 수신 → Discord 알림 → 검토 화면 → 반영/무시/대상 변경 (해피 패스)
- Gmail Apps Script가
recruitment-app/inbox라벨이 붙은 서류 합격 메일을 앱 웹훅으로 전달한다(FR-89·92). - 시스템이 회사명·공고명·전형 키워드를 추출하고 후보 지원 건 신뢰도를 계산한다(FR-91).
- 상태를 즉시 변경하지 않고 Discord로 “연락 검토 요청” 알림을 발송한다(FR-94).
- 사용자가 알림의 링크로 검토 화면을 열어 후보·제안 상태·면접 일정을 확인하고
반영을 선택한다 — 검토 화면은 터널을 통해 외부 네트워크에서도 열리므로(FR-71), 외출 중에도 알림을 받아 즉시 처리할 수 있다. 반영선택 시에만 지원 상태가 전이되고, 원본 이벤트·파싱 결과·사용자 결정이 이력으로 저장된다(FR-90).
시나리오 15 — 예외: 종료된 지원에 연락이 도착한 경우
- 이미
REJECTED로 종료된 지원 건과 관련된 회사에서 새 연락(다른 공고 관련 안내 등)이 도착한다. - 시스템이 후보를 계산하되, 종료 상태(
ACCEPTED/REJECTED/WITHDRAWN/OFFER_DECLINED)로의 반영은ApplicationStatus.canTransitTo()제약상 허용되지 않음을 검토 화면에 표시한다(FR-90,ApplicationStatus.kt:37-44). - 사용자는
무시또는다른 지원 건 선택만 할 수 있다 — 종료된 지원에는 상태 변경도 면접 추가도 되지 않는다.
시나리오 16 — 예외: 후보 신뢰도가 낮아 수동 선택으로 떨어지는 경우
- 연락 이벤트에서 회사명만 추출되고 공고명·담당자 정보가 일치하지 않는다.
- 후보 매칭 점수가 40점(회사 일치만)으로 신뢰도 임계치 55점 미만이다(FR-91).
- 시스템은 대상 지원 건을 자동 선택하지 않고, 신뢰도 순으로 정렬된 후보 목록에서 사용자가 직접 대상을 선택하도록 요구한다 — 오탐 방지를 위한 의도된 동작이다(B-15).
시나리오 17 — 예외: AI 보조 추출 호출 실패·타임아웃 (선택 기능 P2, 기존 시나리오 8-c 승계)
- 사용자가 이력서 프로필 초안 생성 시 AI 보조 추출(선택 기능, FR-95 후속·P2)을 명시적으로 활성화한다.
- 외부 AI 제공자 API가 오류를 반환하거나 응답이 지연된다.
- 시스템은 “AI 보조 추출에 실패했습니다 — 다시 시도하거나 직접 입력해 주세요”를 표시하고, 부분 결과를 프로필 초안에 반영하지 않는다.
- 사용자는 재시도하거나 기본(로컬) 텍스트 추출 결과를 바탕으로 항목을 직접 입력해 프로필을 완성할 수 있다 — FR-96(규칙 기반 추천도 계산) 자체는 AI 보조 추출 없이도 정상 동작한다.
시나리오 18 — 예외: AI 보조 추출 처리 한도 초과 (선택 기능 P2, 기존 시나리오 8-d 승계)
- 이력서 원문이 AI 제공자가 처리 가능한 길이를 초과한다.
- 시스템은 “내용이 너무 길어 AI 보조 추출을 실행할 수 없습니다”를 표시하고 기본(로컬) 텍스트 추출 결과만으로 프로필 초안을 만든다.
- 규칙 기반 지원 추천도 계산(FR-96)은 AI 보조 추출 성공 여부와 무관하게 동작한다 — 처리 한도 초과가 핵심 추천도 산출을 막지 않는다.
Benchmarking
| 제품명 | 카테고리 | 참조 패턴 | URL |
|---|---|---|---|
| Huntr | 개인 구직 지원 트래커(CRM) | Kanban 기반 지원 상태 관리(Saved→Applied→Interview→Offer), 마감일·면접 일정 리마인더를 지원별로 개별 설정 | https://huntr.co/product/job-tracker |
| Simplify Jobs Copilot | 자동입력 + 지원 트래커 | Greenhouse·Lever·Workday 등 복수 ATS(Applicant Tracking System)에서 공고를 수집해 트래커에 자동 등록, 지원 후 자동 기록 | https://simplify.jobs/copilot |
| 잡코리아 커리어에이전트(CA) | 채용 플랫폼 AI 매칭 | 사용자 이력·관심 분야·클릭 로그 기반 맞춤 공고 추천 — 매칭 실패 데이터도 재학습에 활용하는 방향성이 본 프로젝트의 “매칭 실패 공고도 저장” 요구사항과 유사 | https://www.jobkorea.co.kr/starter/ |
| Teal HQ (신규, 2026-08-08) | 이력서 빌더 + 지원 트래커 | 이력서와 JD를 대조해 Match Score(퍼센트)를 산출하고, 일치·누락·제안 키워드를 하드/소프트 스킬로 나눠 표시. 이력서를 수정하면 Match Score가 실시간으로 갱신됨 — 본 프로젝트의 FR-96(지원 추천도 축별 근거 표시) 설계와 동일한 방향 | https://www.tealhq.com |
| Jobscan | 이력서-JD 매칭 스캐너(ATS 최적화) | 이력서-JD 대조로 0~100 Match Rate 산출 + 하드 스킬·소프트 스킬 항목별 충족 여부 분리 표시, 75% 이상을 권장 기준으로 제시 — 본 프로젝트의 FR-96 필수/우대 기술 축 분리·등급 기준 설계에 참고 | https://www.jobscan.co |
참조 패턴 채택: Huntr의 Kanban 상태 모델(카드가 이전 단계로 되돌아가지 않는 단방향 전이 + 예외 종료 상태)을 상태 전이 설계에 참고합니다. Simplify Jobs의 “복수 플랫폼 소스 통합”은 본 프로젝트의 “회사가 아닌 채용 플랫폼 단위 어댑터” 설계와 동일한 방향입니다. 두 제품 모두 자동 지원서 제출 기능이 있으나 본 프로젝트는 Non-Goals로 명시적으로 제외합니다. Teal HQ·Jobscan의 “점수 + 항목별 근거 분리 표시” 패턴은 FR-96(지원 추천도)이 “점수만 보여주지 않고 축별 근거·부족한 조건을 함께 노출”하도록 설계한 근거입니다. 다만 두 제품 모두 이력서 자체를 최적화(문구 재작성)하는 방향이고, 본 프로젝트는 이력서 최적화가 아니라 공고와의 적합도 판단·의사결정 지원에 한정합니다.
Functional Requirements
회사·소스 등록
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-1 | 사용자는 UI에서 회사명을 직접 입력해 등록을 시작할 수 있다. | P0 |
| FR-2 | 시스템은 slug 프로빙으로 후보 소스를 탐색하거나, 사용자가 채용 사이트 URL을 직접 붙여넣어 후보로 지정할 수 있다. 후보에는 샘플 공고 3건 미리보기를 함께 제시한다. | P0 |
| FR-3 | slug 프로빙과 URL 직접 입력으로 후보를 찾지 못하면, 웹 검색으로 후보를 보조 탐색한다. | P1 |
| FR-4 | 사용자는 제시된 후보 중 하나(또는 복수)를 확정해 회사-소스 매핑을 저장할 수 있다. 한 회사는 복수 소스를 가질 수 있다(1:N). | P0 |
| FR-5 | 탐색 결과 후보가 0건이면 해당 회사를 “수동 등록 전용”으로 저장하고, 자동 수집 대상에서 제외한다. | P0 |
| FR-6 | 소스 탐색(discovery)은 회사 등록 시 1회 수행하고, 공고 수집(collection)은 매일 자정 별도 배치로 수행한다 — 두 작업의 실행 주기를 분리한다. | P0 |
| FR-7 | 시스템은 최소 3종의 채용 플랫폼 어댑터(Greenhouse 공개 API, 배민형 내부 API, 인크루트 HTML)를 제공한다. 어댑터는 회사 단위가 아닌 플랫폼 단위로 구현해, 하나의 어댑터가 해당 플랫폼을 사용하는 여러 회사를 커버한다(근거: 조사 브리프 “어댑터 개발 표준 절차”). 초기 등록 대상은 당근·배민·우리은행·수협은행 4곳이다. | P0 |
| FR-8 | 시스템은 잡코리아 채용대행 소스 어댑터를 추가로 제공한다. | P1 |
공고 수집·변경·마감 감지
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-9 | 시스템은 매일 자정 등록된 모든 소스에서 공고 목록을 수집한다. | P0 |
| FR-10 | 신규 발견 공고는 저장하며, 회사 등록 후 최초 수집 시점의 공고는 알림 없이 시딩만 한다. 이후 수집부터 신규 발견 시 알림 대상이 된다. | P0 |
| FR-11 | 공고 식별은 소스ID + 소스별 고유 공고 ID를 기본 키로 한다. 소스가 고유 ID를 제공하지 않으면 어댑터가 판단하는 안정적인 대체 식별자를 사용한다. | P0 |
| FR-12 | 공고 변경 감지 근거는 소스별로 다르다 — 배민형 소스는 소스가 제공하는 버전 필드 증가, Greenhouse는 최종 수정 시각 필드를 근거로 사용한다. 인크루트처럼 목록 페이지에 변경 감지용 필드가 없는 소스는 상세 페이지를 전건 조회해 본문을 비교하는 방식으로 변경을 판단한다(요청량 관리는 Operations 참조). 마감일 연장은 알림 대상, 그 외 사소한 본문 수정은 알림 대상에서 제외한다. | P0 |
| FR-13 | 각 소스 어댑터는 수집한 마감일을 정규화한다 — 마감일 필드 자체가 없거나(Greenhouse) 상시채용을 의미하는 소스별 특수값(예: 배민 9999-12-31)이면 마감일을 없음으로 통일해 저장한다. 마감일이 없음인 공고는 상시채용으로 취급한다. | P0 |
| FR-14 | 마감 판정은 별도 배치가 아니라 수집 배치 안에서 델타 비교로 수행한다 — 이번 수집 결과에 없는 공고는 미발견 카운터를 증가시키고, 연속 2회 미발견 시 CLOSED로 전환한다. | P0 |
| FR-15 | 수집이 실패했거나 0건을 반환한 소스는 해당 회차의 마감 판정 자체를 건너뛴다(소스 가드) — 파서 고장으로 정상 공고가 무더기 마감 처리되는 사고를 방지한다. | P0 |
| FR-16 | 마감(CLOSED)된 공고가 이후 수집에서 다시 발견되면 재오픈(OPEN)을 허용한다. 단, 재오픈 시 신규 공고 알림은 발생시키지 않는다. | P0 |
| FR-17 | 마감일이 있는 공고 중 마감일이 이미 지난 공고는 별도 경량 배치가 DB 데이터만으로 마감 여부를 판정할 수 있다(수집 실패와 무관하게 동작). 마감일이 없음(상시채용)인 공고는 이 판정에서 제외한다. | P0 |
| FR-18 | 마감(CLOSED) 처리는 소프트 삭제이며, 물리 삭제하지 않는다 — 지원 이력이 참조하는 공고가 보존되어야 한다. | P0 |
| FR-19 | 특정 소스가 3일 연속으로 수집 자체가 실패하거나, 3일 연속으로 0건을 반환하면 소스 고장으로 판단해 디스코드로 알린다(FR-15 소스 가드가 걸리는 조건과 동일하다). | P0 |
수동 공고 등록
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-20 | 사용자는 등록된 회사를 선택하고 공고명·공고 링크·마감일(선택)을 직접 입력해 공고를 수동으로 등록할 수 있다. 자동 수집으로 확보할 수 없는 로그인 필요 공고, 자소서 필수 공고, 매핑 후보 0건 회사의 공고가 대상이다. | P0 |
| FR-21 | 수동 등록한 공고에도 지원 기록(Application)을 연결할 수 있다 — 자동 수집 공고와 동일한 지원 관리·히스토리 기능을 적용한다. | P0 |
매칭 — 직무
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-22 | 사용자는 관심 직무 키워드를 등록할 수 있고, 동의어 그룹(예: 백엔드/Backend/서버/Server)을 하나의 매칭 조건으로 묶을 수 있다. | P0 |
| FR-23 | 사용자는 제외 키워드(예: 인턴/계약직)를 등록할 수 있고, 제외 키워드가 포함된 공고는 매칭에서 제외한다. | P0 |
| FR-24 | 직무 키워드 매칭은 대소문자·공백 차이를 정규화한 뒤 비교한다. | P0 |
| FR-25 | 매칭에 실패한 공고도 저장한다 — 매칭은 알림 발송 조건일 뿐 저장 조건이 아니다. 키워드 조건을 변경하면 과거 저장된 공고를 다시 매칭할 수 있어야 한다. | P0 |
매칭 — 근무형태
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-26 | 사용자는 근무형태 키워드(재택·원격 등)를 등록한다. 이 키워드는 두 가지 용도로 쓰인다 — ① 근무형태 정보를 추출할 대상 지정, ② 공고 목록의 정렬·표시 기준. | P0 |
| FR-27 | 근무형태 근거는 2단계로 탐색한다 — ① 공고의 구조화 필드/태그, ② 공고 본문(JD) 키워드 매칭. 소스별로 판정 가능 범위가 다르다: Greenhouse는 구조화 필드(metadata[])를 보유해 ①+② 모두 가능하고, 배민형·인크루트는 근무형태 구조화 필드가 없어 ②만 가능하다. | P0 |
| FR-28 | 같은 회사의 다른 공고·복리후생 페이지(③)를 근거로 근무형태를 추론한다. | P2 |
| FR-29 | 근무형태 판정 결과는 근거와 확신도를 함께 저장한다. 근거 단계와 확신도는 아래와 같이 고정 매핑한다. | 근거 단계 |
| FR-30 | 확신도가 CONFIRMED 또는 LIKELY인 공고만 근무형태 정렬 기준으로 사용한다. 확신도가 INFERRED 또는 UNKNOWN인 공고는 정렬 기준에서 제외하고, 근거와 함께 참고용으로만 표시한다 — 판단은 근거 단계가 아니라 저장된 확신도 값을 기준으로 한다. 근무형태 확신도는 알림 발송 여부에 관여하지 않는다(알림 조건은 직무 키워드 단독 — FR-25). | P0 |
| FR-31 | 확신도 INFERRED인 공고는 “이 회사 다른 공고에 이렇게 적혀 있었어요 (공고 #123)” 형태로 근거 공고를 함께 링크해 레퍼런스로 표시한다. | P2 |
| FR-32 | 근무형태 판정에는 부정어 처리를 포함한다 — “재택근무 불가”, “전면 출근 전환” 같은 표현이 “재택 가능”으로 오탐되지 않아야 한다. ①·②(구조화 필드·JD 본문) 근거의 부정어 처리는 P0, ③(같은 회사 다른 공고) 근거의 부정어 처리는 P2다. | P0(①,②) / P2(③) |
| FR-33 | 근무형태는 어떤 확신도에서도 공고를 목록에서 제외하는 필터로 사용하지 않는다 — 라벨 표기와 정렬 기준으로만 사용한다. | P0 |
| FR-34 | 확신도가 UNKNOWN인 공고는 UI에 “근무형태 정보 없음”으로 표기한다. | P0 |
알림 (디스코드)
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-35 | 시스템은 신규 공고, 마감 D-1, 상시채용 7일 주기 반복 리마인드, 소스 고장 4종의 알림을 디스코드 웹훅으로 발송한다. | P0(신규 공고, 소스 고장) / P1(D-1, 리마인드) |
| FR-36 | 마감 알림(공고가 마감되었다는 사실 자체를 알리는 알림)은 발송하지 않는다 — 사용자 요구에 따른 의도적 제외. | P0 |
| FR-37 | 공고 수집은 자정에 실행하되, 알림 발송 시각은 09:00 KST로 분리한다. | P0 |
| FR-38 | 알림은 (공고ID + 알림종류 + 발송회차) 조합으로 유니크하게 관리해 중복 발송을 방지한다(멱등). 신규 공고·D-1·소스 고장 알림은 발송회차가 항상 1이라 사실상 1회성 발송이고, 상시채용 7일 반복 리마인드는 7일마다 발송회차가 1씩 증가해 매 회차가 서로 다른 유니크 키를 가지므로 반복 발송이 가능하다. | P0 |
| FR-39 | 이미 지원(Application 존재)한 공고에는 D-1 알림과 리마인드 알림을 발송하지 않는다. | P1 |
| FR-40 | 상시채용 리마인드는 마감일이 없음인 공고만 대상으로 7일 간격으로 반복하며, 지원 완료·공고 CLOSED·사용자의 “관심 없음” 처리 중 하나가 발생하면 중단한다. 마감일이 존재하는 공고는 D-1 알림 대상이며 리마인드 대상이 아니다. | P1 |
| FR-41 | 웹훅 응답이 5xx 또는 rate limit(429)이면 지수 백오프로 재시도한다(최대 3회). | P0 |
[관계 정리] FR-36이 제외하는 대상은 “공고가 CLOSED로 전환됐다는 사실 자체”를 알리는 알림입니다. FR-35의 D-1 알림(마감 하루 전 미리 알림)과 상시채용 7일 리마인드는 마감 사실 통보가 아니라 마감 전 행동 촉구 알림이므로 FR-36과 모순되지 않습니다. FR-93(변경 공고 알림, 2026-08-08 편입)도 마감 통보가 아닌 내용 변경 통보이므로 동일하게 FR-36 범위 밖입니다.
[유지 확정] FR-35(마감 D-1)·FR-39(지원 시 D-1·리마인드 중단)·FR-40(상시채용 7일 리마인드)은 이미 DB 스키마에 반영되어 있습니다 —
notification_dispatches.notification_type컬럼 주석에DEADLINE_D1·PERMANENT_REMINDER가 P1 항목으로 명시되어 있고(V202607220000__baseline_schema_and_feature_flags.sql:312),dispatch_sequence(발송 회차) 컬럼이 7일 반복 리마인드의 회차 증가를 지원합니다(:313).docs/PRD.md§8 “마감 임박 기준”이 이를 미결로 되돌린 서술은 반영하지 않습니다 — 위 확정을 그대로 유지합니다.
지원 관리·히스토리
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-42 | 공고와 지원 기록은 별개 개념으로 분리 관리한다 — “미지원” 상태는 별도 값이 아니라 지원 기록이 존재하지 않는 것으로 표현한다. | P0 |
| FR-43 | 지원 상태 전이는 아래 표를 따른다. ACCEPTED·REJECTED·WITHDRAWN·OFFER_DECLINED는 종료(terminal) 상태이며, 이 상태에서는 추가 전이가 불가하다. REJECTED 전이 시 탈락 시점의 단계를 함께 기록한다. | 현재 상태 |
| FR-44 | OFFERED 상태의 허용 전이는 ACCEPTED와 OFFER_DECLINED 2개뿐이다 — 처우협의 단계에서의 사용자 포기는 WITHDRAWN이 아니라 OFFER_DECLINED로 표현한다. | P0 |
| FR-45 | WITHDRAWN(본인 사퇴)은 APPLIED·DOCUMENT_SCREENING·INTERVIEWING 3개 단계에서만 가능하다. | P0 |
| FR-46 | 면접 회차는 지원 상태값이 아니라 별도 기록(회차 번호, 사용자 지정 레이블, 일정, 결과)으로 관리하며, 한 지원 건에 여러 면접 회차를 추가할 수 있다. | P0 |
| FR-47 | 모든 상태 전이는 이전 상태·다음 상태·전이 시각·메모를 포함한 이력으로 남기며, 이 이력이 히스토리 조회의 기준 데이터다. | P0 |
| FR-48 | 로그인이 필요하거나 자소서 제출이 필수인 공고는 자동 수집 대상에서 제외하고, 수동 공고 등록(FR-20)으로 처리한다. | P0 |
| FR-49 | 자소서는 지원 전 초안 작성 상태를 지원하고, 초안 작성 후 제출 완료로 전환하는 흐름을 지원한다(지원 완료 후에만 기록하는 방식은 지원하지 않는다). | P2 |
| FR-50 | 공고와 지원 이력은 회사 단위로 분류해 조회할 수 있다. | P0 |
| FR-51 | 자소서 문항·답변은 과거 작성 내용을 검색해 재사용할 수 있어야 한다. | P2 |
| FR-52 | 지원 현황을 회사별·단계별 건수, 단계 전환율(서류 합격률·면접 전환율 등), 단계별 평균 소요 일수로 집계해 조회할 수 있다. | P2 |
[수정 — 우선순위 조정] FR-49(자소서 초안→제출 흐름)는 P1 → P2로 조정합니다(2026-08-08). FR-81
85(문서 버전 관리, 파일 업로드 기반, 2026-08-08 편입)가 문서 파일 자체의 버전·지원 건 연결을 담당하지만, 자소서 문항·답변 단위의 구조화 관리(FR-49·51)는 파일 업로드로 대체되지 않는 별개 기능으로 존속합니다 — FR-8185는 “파일 한 벌”을 다루고 FR-49·51은 “문항·답변 텍스트 단위”를 다룹니다. 기존 Goals의 “자소서를 지원 전 초안 작성 → 제출 흐름으로 관리한다”는 FR-49와 정합하므로 변경하지 않습니다.
이력서 기반 서류 적합도 평가
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-53 | 사용자는 이력서를 PDF 파일 업로드 또는 URL 입력 두 경로로 등록할 수 있다. 이력서는 복수 버전으로 관리하며, 새 이력서를 등록해도 기존 버전은 삭제되지 않고 계속 조회할 수 있다. | P1 |
| FR-54 | 사용자는 저장된 공고 1건과 이력서 버전 1건을 선택해 “적합도 평가” 버튼을 눌렀을 때만 평가를 실행할 수 있다. 자동 실행·배치 실행·전체 공고 일괄 평가는 지원하지 않는다 — 외부 API 호출 비용·부하를 사용자 실행으로 통제하는 것이 목적이다. | P1 |
| FR-55 | 평가는 공고 본문(JD)과 이력서 내용을 대조해 산출하는 0~100% 적합도 점수, 장점, 단점, 총평 4요소로 구성한다. “합격률”·“합격 예측”이라는 표현은 쓰지 않는다 — 실제 합격 데이터 없이 JD 요구사항 대비로만 산출되는 값이기 때문이다. | P1 |
| FR-56 | 평가 결과는 평가에 사용된 이력서 버전과 함께 저장하고 재조회할 수 있다. 재평가는 이전 결과를 덮어쓰지 않고 새 평가 이력으로 남긴다 — 이력서를 고치면 과거 점수의 근거가 달라지므로, 각 결과가 어느 이력서 버전으로 계산됐는지 항상 구분할 수 있어야 한다. | P1 |
| FR-57 | 평가에는 공고 본문(JD)이 필요하다. 본문이 없는 공고(예: 본문을 기재하지 않은 수동 등록 공고)는 평가 대상에서 제외하고, “공고 본문이 없어 평가할 수 없습니다” 등 제외 사유를 화면에 표시한다. | P1 |
| FR-58 | 적합도 점수를 표시하는 화면에는 “이 점수는 합격 예측이 아니라 공고 요구사항 대비 적합도입니다”라는 안내 문구를 항상 함께 표시한다. | P1 |
| FR-59 | 지원 이력(서류전형 통과·탈락 결과)이 누적되면 지원 추천도 점수 구간별 실제 서류 통과율을 산출해 함께 표시한다. 구간당 표본이 5건 미만이면 해당 구간의 통과율은 표시하지 않는다. | P2 |
[폐기 — FR-95~97로 대체] FR-53
58은 2026-08-08 편입된 FR-9597(현재 이력서 프로필·지원 추천도)로 대체되어 폐기합니다. 충돌 5축: ① 명칭(적합도 점수 → 지원 추천도) ② 산출 방식(외부 LLM API 동기 호출 → 설명 가능한 규칙 기반, B-23) ③ 결과 구성(장점·단점·총평 → 평가 축별 점수·근거) ④ 실행 단위(공고 1건 × 이력서 1건 수동 실행 → 관심 공고 일괄 재평가, FR-97·B-20) ⑤ 입력 경로(PDF 업로드 또는 URL 입력 → PDF/DOCX/Markdown 업로드만, URL 경로 폐지, B-21).위 시나리오 8(이력서 적합도 평가 실패)의 예외 흐름 중 8-a(PDF 텍스트 추출 실패)는 시나리오 13으로 승계합니다. 8-c(외부 API 실패)·8-d(처리 한도 초과)는 시나리오 17·18로 승계하되, 핵심 추천도 계산(FR-96)이 규칙 기반으로 바뀌어 외부 API 호출이 사라졌으므로 두 흐름은 선택 기능인 AI 보조 추출(P2)에 한정된 예외로 재정의합니다. 8-b(URL 접근 불가·형식 미지원)는 URL 입력 경로 폐지와 함께 폐기합니다.
FR-59(점수 구간별 실제 서류 통과율 사후 보정)는 폐기하지 않고 P2로 유지합니다 —
docs/PRD.md§198-200 “합격 확률의 후속 원칙”(충분한 본인 과거 결과가 축적되면 별도 실험 기능으로 확률 추정을 검토한다)이 FR-59와 같은 개념이므로, FR-95~97(지원 추천도) 도입 이후에도 유효한 후속 실험 기능으로 존속합니다.
애그리게이터 소스 (통합 채용 플랫폼)
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-60 | 시스템은 소스를 회사 종속형과 애그리게이터형 2종으로 구분한다. 애그리게이터형 소스는 특정 회사에 종속되지 않는 전역 소스이며, 등록 시 플랫폼 선택과 검색 조건(직무 카테고리·키워드 등)을 지정한다 — 회사 slug가 아니라 검색 조건이 수집 파라미터다. 초기 도입 대상은 조사 브리프의 “전체 플랫폼 조사 결과” 중 규약상 채택 가능으로 분류된 플랫폼(예: 사람인 등)을 우선하며, 확정 목록은 이 문서에 고정하지 않고 조사 브리프를 참조한다. | P0 |
| FR-61 | 애그리게이터 공고의 회사가 아직 등록돼 있지 않으면 회사를 자동 등록한다. 이때 사용자가 직접 등록한 “관심 회사”와 애그리게이터로 자동 등록된 “발견된 회사”를 구분하는 표시를 부여하고, 회사 목록·필터에서 이 구분을 항상 노출한다. | P0 |
| FR-62 | 관심 회사의 신규 공고는 기존과 동일하게 개별 알림을 발송한다(FR-10). 발견된 회사의 매칭 공고는 개별 알림 대신 하루 1건의 일일 요약 알림으로 묶어 발송한다 — 애그리게이터 도입으로 매칭 공고가 하루 수백 건이 될 수 있어 개별 알림은 소음이 된다. | P0 |
| FR-63 | 같은 공고가 회사 직접 소스와 애그리게이터에 동시에 존재할 수 있다. 회사명·공고 제목(정규화 후)·마감일을 근거로 두 공고가 동일한지 판정한다. 구체적인 판정 알고리즘(문자열 유사도 계산 방식 등)은 기술 설계 문서(TDD)에서 정한다. | P0 |
| FR-64 | 동일 공고로 판정된 건은 대표 공고 1건을 선정한다. 선정 우선순위는 회사 직접 소스 > 애그리게이터다 — 직접 소스가 원문이 정확하고 구조화 필드가 더 풍부하기 때문이다. 매칭·마감 판정·지원 연결은 대표 공고를 기준으로 하고, 나머지는 같은 공고의 다른 소스 출처로만 연결한다. | P0 |
| FR-65 | 크로스 소스 중복으로 묶인 공고는 신규 공고 알림을 1회만 발송한다 — 대표 공고가 먼저 발견되어 알림이 나간 뒤 다른 소스에서 같은 공고가 뒤늦게 발견돼도 추가 알림을 보내지 않는다. 1단계 범위에서는 자동 판정 결과만 적용하며, 사용자가 오판정을 수동으로 정정하는 기능은 지원하지 않는다(2단계 이후 검토). | P0 |
| FR-66 | 애그리게이터 소스도 매칭 실패 공고까지 전부 저장한다(FR-25 원칙을 예외 없이 적용) — 수집 단계에서 매칭 여부로 저장을 거르지 않는다. | P0 |
| FR-67 | 마감일 정규화(FR-13) 대상을 애그리게이터 소스까지 확장한다. 연도 없는 ~MM/DD 표기, “채용시”·“상시채용” 같은 문자열, 불리언 값, 별도 필드로 명시된 마감일, ISO 타임스탬프 등 소스마다 표현 방식이 달라도 전부 “마감일 또는 없음(상시채용)“으로 정규화해 저장한다. | P0 |
| FR-68 | 변경 감지 수단이 아예 없는 소스는 공고의 주요 필드(제목·마감일 등)를 비교하는 방식으로 변경 여부를 판단한다(FR-12 확장). 회사 식별자가 목록 응답에 없고 상세 조회에만 존재하는 소스는 상세 조회 결과로 회사를 식별한다. | P0 |
| FR-69 | 애그리게이터를 포함한 모든 외부 소스 수집은 다음 규약을 준수한다 — (1) robots.txt에서 금지된 경로는 호출하지 않는다, (2) 소스당 1일 1회를 초과해 호출하지 않는다, (3) 플랫폼이 명시한 페이지 크기 상한을 준수한다, (4) 요청 사이에 지연을 둔다, (5) 식별 가능한 User-Agent를 명시한다, (6) 수집한 데이터를 재배포하거나 상업적으로 이용하지 않는다(개인 열람 전용). | P0 |
[유지 확정] FR-62(발견 회사
DISCOVERED일일 요약 알림)는 변경하지 않습니다. FR-93(변경 공고 알림, 2026-08-08 편입)는 이 규칙과 별개로 신규·변경 감지 알림의 발송 빈도 제한(24시간 1회)만 추가합니다.[유지 확정 + 보강] FR-63(중복 판정 축: 정규화 회사명 + 정규화 제목)·FR-64(대표 공고 선정 우선순위)는 현재 구현과 일치합니다 —
JobPostingDedupKey.of()가 정규화(회사명) +|+ 정규화(제목)로 키를 계산하고(JobPostingDedupKey.kt:12-13),JobPostingDeduplicationDomainService가 마감일 30일 이상 차이 시 별개 그룹으로 분리합니다(JobPostingDeduplicationDomainService.kt:89-107, B-24).docs/PRD.md§115의 “회사·직무” 표현은 실제 판정 축(회사·제목)과 다르므로 이 문서로 편입하며 “회사·제목”으로 정정합니다.대표 선정 tie-break에 접근 제한 후순위 축을 추가합니다(B-25, 2026-08-08) — 현재 comparator는 ① 회사 직접 > 애그리게이터 ②
OPEN>CLOSED③ 이른firstSeenAt④ PK 오름차순 순서만 가집니다(JobPostingDeduplicationDomainService.kt:71-75). 여기에 ①과 ② 사이에 “접근 가능 > 접근 제한(access_restricted,baseline...sql:91)” 축을 추가해 최종 순서를 ① 접근 가능 > 접근 제한 ② 회사 직접 > 애그리게이터 ③OPEN>CLOSED④ 이른firstSeenAt⑤ PK 오름차순으로 확정합니다 —docs/PRD.md§115 “대표 공고가 접근 불가여도 다른 출처의 링크를 제공한다”를 대표 선정 단계에서 먼저 해소하기 위함입니다. FR-74의 MANUAL 공고 dedup_key 부여(A-2, 2026-08-08)로 MANUAL 공고가 이 tie-break에 참여하게 되면, ②축에서posting_origin=MANUAL은COMPANY_BOUND와 동급으로 취급합니다 — 사용자가 직접 등록한 공고이므로 애그리게이터보다 우선하되 회사 직접 소스와는 동순위입니다.
신규 기능 편입 (2026-08-08 — docs/PRD.md 통합)
이하 FR-70
97은69 번호를 직접 참조하므로(docs/PRD.md(개인 채용 지원 통합 관리, 282줄)의 신규 요구 중 이 문서에 없거나 확장되는 부분만 편입한 것입니다. 코드 주석이 기존 FR-1MatchCriteria.kt:4,JobPostingDedupKey.kt:6,ApplicationStatus.kt:5등) 이 문서를 SSOT로 유지하고 신규 번호를 이어 붙입니다.
인증·외부 노출 (신규 — FR-7 계열의 선행 조건)
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-70 | 시스템은 단일 사용자 기준 최소 인증(로그인)을 앱 전체 API에 도입한다. 현재는 Spring Security 의존성 자체가 없어 전 API가 무인증으로 공개되어 있다(AS-IS: build.gradle.kts:34-59). FE(React SPA, web/) 몫: 로그인 화면을 신설하고, 토큰(또는 세션 쿠키) 보관 방식을 정하며, 401 응답 수신 시 재로그인 화면으로 유도한다. 기존 화면(공고 목록·대시보드 등 API 연동 화면)의 API 호출 경로(web/src/api/* → nginx /api 프록시 → app 컨테이너)에 인증 헤더 또는 쿠키를 반영해야 한다(C-1, 2026-08-08). | P0 |
| FR-71 | 외부 인터넷 노출은 터널링(예: Cloudflare Tunnel)으로 한다. 터널로 공개하는 대상은 두 가지다 — ① HMAC 서명으로 검증하는 웹훅 수신 경로(FR-72), ② FR-70 인증을 통과해야만 열리는 나머지 앱 화면·API(연락 검토 화면 등 포함). 인증 없이 열리는 것은 ①웹훅 경로뿐이며, ②는 인증이 유일한 방어선이다(A-3, 2026-08-08 정정 — 기존 “웹훅 경로만 공개”는 인증 뒤 화면·API의 외부 접근 자체를 막는 것으로 오독될 수 있어 정정). | P0 |
| FR-72 | 웹훅 수신 경로는 세션 인증 대신 HMAC 서명으로 인증한다. 허용 시간 범위는 ±5분, 재전송 방지 nonce는 24시간 보존한다. (기본값 채택 — B-10, 근거: 웹훅 발신 측이 사용자 로그인 세션을 가질 수 없어 서명 기반 인증이 필요) | P0 |
관심 공고 관리 강화 (FR-2 보강)
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-73 | 공고별 사용자 관리 상태는 INTERESTED(관심)·PLANNED(지원 예정)·EXCLUDED(제외) 3종만 저장한다. APPLIED(지원 완료)는 저장하지 않고 지원(Application) 레코드 존재 여부로 파생한다. (기본값 채택 — B-1, 근거: 기존 FR-42 및 “미지원은 상태값이 아니라 이 행이 없는 것으로 표현한다”는 기존 설계 원칙과 정합) | P0 |
| FR-74 | 관리 상태는 개별 공고가 아니라 중복 그룹에 귀속한다. 귀속 단위는 dedup_key 단독이 아니라 dedup_key + 마감일 그룹이다(마감일 30일 이상 차이 시 분리 — JobPostingDeduplicationDomainService.kt:90-107의 splitByDeadlineGap이 실제로 만드는 그룹과 일치시킨다). 같은 회사·같은 제목이라도 마감일이 30일 이상 벌어진 재공고는 별개 그룹으로 보아 이전 회차의 관리 상태(EXCLUDED 포함)를 승계하지 않고 새로 판단한다 — 회차마다 처우·직무 범위·본인 상황이 달라질 수 있으므로 조용히 승계하면 지원 기회를 잃는다(A-1, 2026-08-08). 아래 “대표 교체 시 승계” 규칙(B-2)은 같은 그룹 안에서만 적용된다.MANUAL(수동 등록) 공고에도 수집 공고와 동일한 규칙(정규화 회사명 + 정규화 제목)으로 dedup_key를 계산해 부여한다(A-2, 2026-08-08) — 현재 JobPosting.kt:279는 MANUAL 공고의 dedupKey를 null로 생성해 관리 상태를 붙일 대상이 없다. 부여 이후 같은 공고가 수집 경로로 발견되면 같은 그룹으로 자연 병합되어, 사용자가 수동 등록 시점에 남긴 관리 상태·메모가 유지된다. 이 변경은 MANUAL 공고를 중복 그룹 후보에 편입시킨다 — 대표 선정 tie-break(B-25 5단계)에서 posting_origin=MANUAL은 사용자가 직접 등록한 것이므로 COMPANY_BOUND와 동급으로 취급한다(애그리게이터보다는 우선, 회사 직접 소스와는 동순위).(기본값 채택 — B-2, 근거: 08:30 배치가 매 실행마다 대표 공고를 재선정하므로( JobPostingDeduplicationDomainService.kt:22) 개별 공고에 상태를 붙이면 대표 교체 시 제외 결정이 유실되고, docs/PRD.md §114 “제외 결정은 다른 플랫폼에서 재발견돼도 중복 노출하지 않는다”를 만족할 수 없다) | P0 |
| FR-75 | 관리 상태 전이 규칙: ① EXCLUDED 공고를 지원 처리하면 관리 상태를 INTERESTED로 되돌리고 지원 레코드를 생성한다 ② 지원을 철회·삭제하면 직전 관리 상태로 복귀하되 이력이 없으면 INTERESTED로 복귀한다 ③ PLANNED 공고가 마감되면 관리 상태는 유지하고 공개 상태만 CLOSED로 전환한다(마감돼도 관리 목록에서 사라지지 않는다). (기본값 채택 — B-3) | P0 |
| FR-76 | 관리 상태 변경 이력(변경 전·후 상태, 변경 시각)을 저장한다. (기본값 채택 — B-4, 근거: Success Metrics “마감 전 처리율”이 “마감 전에 결정했는가”를 계산하려면 변경 타임스탬프가 필요) | P0 |
| FR-77 | 사용자는 관심·지원 예정 공고에 우선순위(HIGH/NORMAL/LOW), 지원 목표일, 개인 태그(복수), 메모를 등록할 수 있다. EXCLUDED 공고에는 제외 사유를 등록할 수 있다. | P0 |
| FR-78 | 시스템은 회사 경계를 넘어 관리 상태·플랫폼·마감일·키워드로 필터·정렬 가능한 교차 회사 공고 목록 API를 제공한다. 플랫폼 필터는 중복 그룹 내 원본 중 하나라도 일치하면 포함한다(OR 매칭). (기본값 채택 — B-5, 근거: 한 중복 그룹에 플랫폼이 여러 개 존재) | P0 |
지원 대시보드·담당자 관리 (FR-3 보강)
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-79 | 지원 대시보드는 ① 상태별 칸반/목록 ② 다가오는 면접 ③ 장기 미변경 지원(최근 14일 이상 상태 변경 없음) 3요소를 제공한다. | P0 |
| FR-80 | 지원 건에 담당자(이름·소속·이메일·전화번호·연락 메모)를 등록·조회할 수 있다. 담당자는 회사가 아니라 지원 건에 귀속한다 — 같은 회사의 다른 공고·다른 전형에 잘못 연결되는 것을 막기 위해서다. 이 기능은 FR-91(담당자 일치 20점 축)의 전제다. (기본값 채택 — B-14, 근거: 담당자 저장소가 없으면 20점 축이 항상 0점이 됨) | P0 |
지원 서류 버전 관리 (FR-4)
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-81 | 지원 서류 유형은 이력서·자소서·포트폴리오·기타 4종을 지원한다. 같은 이력서의 서로 다른 버전을 묶는 단위로 문서 계열(series)을 데이터 모델에 신설하고, 버전 번호는 계열별로 연속 증가한다. (기본값 채택 — B-8, 근거: 현재 데이터 모델에 “같은 이력서의 v2”를 표현할 대상이 없음) | P0 |
| FR-82 | 서류 원본 파일은 다음 경로에 저장한다. DB에는 파일 바이너리가 아닌 경로·메타데이터만 저장한다. 파일은 한 벌만 존재하고, 어느 지원 건에 제출했는지는 DB 연결로만 표현한다(회사별 폴더는 만들지 않는다)./Users/biuea/Desktop/dpdpdndn/private/이직/지원서류/{문서유형}/{YYYY-MM-DD}_{문서제목}_v{버전}_{원본파일명}(기본값 채택 — D-2, 근거: docs/PRD.md §93의 {회사명}/지원서류/ 경로는 §88 “여러 지원 건에 재사용”과 모순되므로 폐기하고 회사 비종속 경로로 확정) | P0 |
| FR-83 | 허용 확장자는 pdf·docx·md·hwp, 최대 업로드 용량은 20MB다. (기본값 채택 — B-6, 근거: docs/PRD.md §8 후속 항목을 §4 확정 요구로 끌어올림 — 수치 없이는 업로드 엔드포인트를 구현할 수 없음) | P0 |
| FR-84 | 파일명·문서제목의 경로 구분자(/, \)와 상위 참조(..)를 제거·치환하고, 최종 경로가 저장 루트 하위인지 정규화 후 검증한다. 저장 루트는 설정값(환경 변수)으로 추상화하고 Docker 컨테이너에는 볼륨 마운트로 노출한다. (기본값 채택 — B-7·B-9, 근거: 경로 새니타이즈 없이는 docs/PRD.md §98 요구를 실제로 강제할 수 없고, 컨테이너 배포(docker-compose.yml:21-44) 전제상 호스트 절대 경로가 컨테이너 내부에서 도달 불가) | P0 |
| FR-85 | 하나의 서류 버전은 여러 지원 건에 재사용할 수 있다. 지원 건에는 제출 시점의 서류 버전을 연결하며, 이후 새 버전이 생겨도 과거 연결은 바뀌지 않는다. | P0 |
매칭 범위 확대 (FR-5 확장)
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-86 | 직무 키워드 매칭 범위를 제목 단독에서 제목(강한 신호)·JD 본문(약한 신호)·구조화 태그·직무 분류(중간 신호) 필드별 가중치로 확대하고, 가중치 합산이 임계치 이상일 때 매칭이 성립한다. 어느 필드에서 일치했는지 근거를 저장·표시한다. 오탐 억제(제목 단독 매칭 시 대비 오탐률 증가 없음)를 성공 기준으로 한다. 구체 가중치·임계치 수치는 기술 설계 문서(TDD)에서 확정한다. (기본값 채택 — D-1, 근거: 기존 TDD가 “본문 매칭은 ‘백엔드와 협업’ 같은 문장에서 대량 오탐” 사유로 기각했던 결정을 이번에 번복함 — 현재 구현은 MatchCriteria.matches(title)이 제목만 정규화 비교한다, MatchCriteria.kt:14-30) | P1 |
| FR-87 | 매칭 범위 확대(FR-86) 적용 시 이미 저장된 전체 공고를 새 기준으로 1회 소급 재평가한다. 소급 재평가로 새로 매칭된 공고는 알림을 발송하지 않고 조용히 반영한다 — 대량 알림 폭주를 방지한다. (기본값 채택 — D-1) | P1 |
| FR-88 | 소스 레지스트리는 발견됨·활성·일시 실패·접근 제한·미지원·비활성 6개 상태를 가지며, 각 상태 전이의 트리거 사건을 명시한다: 등록만 되고 미검증 → 발견됨 / 최근 수집 성공 → 활성 / 연속 실패 N회 미만 → 일시 실패 / 401·403·robots 차단 → 접근 제한 / 어댑터 없음 → 미지원 / 사용자가 끔(disabled_at) → 비활성. (기본값 채택 — B-29, 근거: 현재 구현은 seeded_at·disabled_at 2축 + job_source_health 테이블뿐이다, baseline...sql:59-60, :152-164) | P1 |
연락 이벤트 확인형 반영 (FR-7)
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-89 | 외부 자동화가 이메일·문자 이벤트를 앱의 수신 웹훅(FR-71·72로 보호)으로 전달할 수 있다. 이벤트에서 발신자·회사명·공고명·본문 키워드·수신 시각을 추출해 후보 지원 건과 제안 상태를 계산하되, 상태를 즉시 변경하지 않고 Discord에 검토 알림을 보낸다. 원본 이벤트 식별자·파싱 결과·후보 목록·사용자 결정·반영 결과를 저장해 중복 수신과 오탐을 추적한다. 1차 구현은 이메일(Gmail)에 한정하고, 웹훅 페이로드에 channel 필드를 두어 문자(SMS) 등 다른 채널로 확장 가능하게만 한다. (기본값 채택 — B-11, 근거: SMS 수집 주체·경로가 아직 확보되지 않음) | P1 |
| FR-90 | 연락 검토 화면은 수신 채널·발신자·수신 시각·원문(또는 발췌), 추출한 회사명·공고명·전형 키워드, 신뢰도 순 후보 지원 건, 제안된 지원 상태·면접 일정, 반영 전 사용자가 수정 가능한 상태·대상 지원 건·메모를 최소 정보로 제공한다. 사용자는 반영·무시·다른 지원 건 선택 중 하나를 선택하며, 반영을 선택한 경우에만 상태 전이 또는 면접 일정 등록을 실행한다. 화면에서 상태를 직접 선택해도 ApplicationStatus.canTransitTo() 전이 제약을 그대로 적용하며, 종료 상태(ACCEPTED/REJECTED/WITHDRAWN/OFFER_DECLINED)에 도착한 연락은 반영 불가로 표시하고 무시·다른 지원 건 선택만 허용한다(ApplicationStatus.kt:37-44). 연락 반영으로 면접을 등록할 때 회차 번호는 해당 지원 건의 기존 최대 회차 + 1이며, 레이블은 추출된 전형명(없으면 {N}차 면접)을 기본값으로 제시하고 반영 전 수정할 수 있다. 종료된 지원에는 면접을 추가하지 않는다. 이 화면은 FR-71에 따라 터널을 통해 외부 네트워크에서도 접근 가능하며, FR-70 인증을 통과해야 열린다(A-3, 2026-08-08). (기본값 채택 — B-12·B-13) | P1 |
| FR-91 | 연락 이벤트 후보 매칭은 회사 일치(40점)·공고/직무 제목 일치(30점)·담당자 일치(20점, FR-80 전제)·진행 중·최근 지원(180일 내, 10점)을 합산해 신뢰도(80점 이상 높음, 55~79점 보통, 54점 이하 낮음)를 산출한다. 동일 최고점 후보가 둘 이상이거나 최고점이 55점 미만이면 대상 지원 건을 자동 선택하지 않고 사용자의 수동 선택을 요구한다 — “회사만 일치(40점)“가 수동 선택으로 떨어지는 것은 오탐 방지를 위한 의도된 동작이다. (기본값 채택 — B-15) | P1 |
| FR-92 | 첫 이메일 연동은 사용자 Gmail 계정의 Google Apps Script로 제공한다. 시간 기반 트리거가 5분마다 recruitment-app/inbox 라벨이 붙은 메일만 조회하고(자동 키워드 검색 없음), providerMessageId·스레드ID·발신자·제목·수신 시각·텍스트 본문·첨부파일 메타데이터만 전달한다(첨부 원본 미전송). 앱이 2xx로 수신을 확인한 뒤에만 recruitment-app/processed 라벨을 붙이고 inbox 라벨을 제거하며, 실패한 메일은 inbox 라벨을 유지해 재시도한다. 앱은 providerMessageId를 유일 키로 중복 이벤트를 멱등하게 처리한다. | P1 |
[용어 통일] FR-91의 연락 이벤트 후보 상태 제안은
docs/PRD.md§233의서류 진행·면접·오퍼·불합격표기를 그대로 쓰지 않고, 기존 Goals “한글 단계명 ↔ 상태값 매핑” 표(SSOT)로 통일합니다(B-28) —서류 진행→ 서류전형(DOCUMENT_SCREENING),면접→ 면접(INTERVIEWING),오퍼→ 처우협의(OFFERED),불합격→ 불합격(REJECTED). 검토 화면(FR-90)이 제안하는 상태 후보는 이 4개 값 중에서만 선택되며, 종료 상태(ACCEPTED/WITHDRAWN/OFFER_DECLINED)는 연락 이벤트로 제안하지 않고 사용자가 검토 화면에서 직접 선택해야 한다.
알림 확장 (FR-8 확장)
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-93 | ”변경 공고” 알림(공고 내용이 변경됐다는 사실을 알리는 알림)을 신설한다. 동일 공고에 대해 24시간 내 1회로 발송을 제한한다. (기본값 채택 — B-26, 근거: 변경 감지 신호가 배치마다 발생할 수 있어 빈도 제한 없이는 알림이 폭주함) | P1 |
| FR-94 | ”연락 검토 요청” 알림(FR-89·90 검토 화면으로 이동하는 링크 포함)을 신설한다. 멱등 키의 target_type은 CONTACT_EVENT로 하고, 기존 멱등 키 규칙({target_type}:{target_id}:{notification_type}:{dispatch_sequence})을 그대로 확장 적용한다. 이 알림 링크가 가리키는 검토 화면(FR-90)은 FR-71의 터널로 외부 네트워크에서도 동작한다 — 사용자가 외출 중에도 알림을 받아 즉시 처리할 수 있다(A-3, 2026-08-08). (기본값 채택 — B-27) | P1 |
이력서 프로필·지원 추천도 (FR-9)
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-95 | 사용자는 업로드한 이력서 버전 하나를 현재 프로필로 지정한다. 지원하는 파일 형식은 PDF·DOCX·Markdown이며, URL 입력 경로는 지원하지 않는다(FR-82 파일 업로드로 입력 경로를 일원화). (기본값 채택 — B-21) 앱은 텍스트를 추출해 기술 스택·경력·직무 선호·근무 조건의 프로필 초안을 만들고, 텍스트 추출에 실패하면 프로필 초안을 생성하지 않고 수동 입력으로 안내한다(이력서 등록 자체는 저장된다). (기본값 채택 — B-22, 시나리오 8-a 승계) 사용자가 항목을 검토·수정한 뒤 프로필 확정해야 새 기준 버전이 생성되고 관심 공고가 재평가된다 — 확정 전 초안은 추천도 계산에 사용하지 않는다. | P0 |
| FR-96 | 지원 추천도는 필수 기술(45점)·우대 기술·도메인(20점)·경력·직무(20점)·근무 조건·선호(15점) 4축을 규칙 기반으로 계산해 0강력 추천 80추천 65검토 45비추천 0~44)으로 표시한다. 적용 순서는 ① 사용자 가중치 합계 100 검증 → ② 판단 불가 축 제외 → ③ 남은 축의 비중을 합계 100으로 정규화 → ④ 점수 계산 → ⑤ 필수 기술 미충족 시 59점 상한 적용이다. 판단 불가 축 처리는 모든 축에 동일하게 나머지 축 비중으로 정규화하며, 필수 기술 축(45점)이 판단 불가여도 예외 없이 정규화한다. “일부 충족 60%“는 기술명이 일치하되 최근 사용 시점이 3년 초과 또는 사용 기간이 요구 기간의 절반 미만인 경우이며, 둘 다 아니면 100%, 기술명 자체가 없으면 0%로 판정한다. (기본값 채택 — B-16·B-17·B-18, 근거: docs/PRD.md §215가 경력·직무 축에만 정규화를 규정했으나 §183은 모든 축에 판단 불가를 허용해 45점 축 처리가 비어 있었음) | P0 |
| FR-97 | 필수 기술 미충족으로 인한 59점 상한은 사용자가 공고 단위로 해제할 수 있으며, 해제 여부를 영속화해 재평가 시에도 유지한다. 해제된 결과에는 “상한 해제됨” 표시와 원래 상한 사유를 함께 노출한다. 사용자는 평가 축별 가중치(합계 100 검증)를 수정할 수 있고, 변경은 평가 기준 버전을 증가시킨다. 이 평가 기준 버전은 기존 match_criteria_revisions(키워드 매칭 기준)와는 별개 축으로 관리한다. 재평가 대상은 관심 공고(관리 상태 INTERESTED 또는 PLANNED)로 한정하고, 전체 공고 일괄 평가는 지원하지 않는다. (기본값 채택 — B-19·B-20·B-23) | P1 |
AI 보조 추출(자동 추출값의 사용자 수정 가능한 형태로 제시)과 민감 정보의 외부 AI 제공자 전송은 사용자의 명시적 활성화 전에는 실행하지 않는 선택 기능으로 2단계 이후 후속 범위(P2)다.
Non-Functional Requirements
| ID | 항목 | 요구사항 |
|---|---|---|
| NFR-1 | 처리 규모 | 사용자가 직접 등록하는 관심 회사는 10~20곳, 회사 종속형 소스도 30개 미만을 전제로 한다. 애그리게이터로 자동 등록되는 발견된 회사는 이 전제와 별개로 수백 곳까지 늘어날 수 있으며, 회사 목록·필터·조회 화면은 이 규모를 전제로 설계한다. 애그리게이터 소스 자체(플랫폼 개수)는 1단계에서 소수(조사 브리프에서 채택 가능으로 분류된 것 위주)만 운용한다. |
| NFR-2 | 배치 소요 시간 | 회사 종속형 소스(약 20 |
| NFR-3 | 알림 지연 | 신규 공고 발견 시점부터 지정된 발송 시각까지의 대기를 제외하면, 알림 발송 자체는 배치 완료 후 5분 이내 처리한다. |
| NFR-4 | 인코딩 안정성 | 인크루트 등 EUC-KR 인코딩 소스를 파싱할 때 charset을 명시적으로 지정해 문자 깨짐이 0건이어야 한다. |
| NFR-5 | 데이터 보존 | 마감(CLOSED)된 공고, REJECTED/WITHDRAWN/OFFER_DECLINED된 지원 이력을 포함해 모든 데이터를 삭제하지 않고 무기한 보존한다(개인 이력 보관 목적). |
| NFR-6 | 재시도 정책 | 디스코드 웹훅 발송 실패(5xx·429)는 지수 백오프로 최대 3회 재시도한다. 소스 수집 실패는 재시도하지 않고 다음 배치(다음 날 자정)로 넘긴다. |
| NFR-7 | 보안 | 1인용 도구이며 로컬 Docker 환경에서만 구동한다. 외부 인터넷에 노출하지 않는 것을 기본 전제로 하며, 별도 인증 체계(로그인)는 두지 않는다. |
| NFR-8 | 확장 제약 | 이 규모(회사 10~20곳, 1일 1회 배치)에서는 메시지 브로커·별도 캐시 계층·별도 워커 프로세스가 필요하지 않으며 도입하지 않는다. 구체적인 실행 구조(프로세스·스케줄러 방식)는 기술 설계 문서(TDD)에서 결정한다. |
| NFR-9 | 소스 준수 | 신규 어댑터 추가 전 해당 사이트의 robots.txt와 이용약관을 확인하는 절차를 거친다(Operations 참조). |
| NFR-10 | 개인정보 처리 (2단계) | |
| NFR-11 | 플랫폼 규약 준수 | 애그리게이터를 포함한 모든 외부 소스 수집은 FR-69의 6개 규약(robots.txt 준수·1일 1회 제한·페이지 크기 상한·요청 간 지연·식별 가능한 User-Agent·재배포 및 상업적 이용 금지)을 지킨다. 사용자가 애그리게이터 포함에 따르는 규약 리스크(일부 플랫폼은 회색지대)를 인지하고 도입을 결정했으며, 위 제약은 그 위험을 낮추기 위한 최소 준수 기준이다. |
| NFR-12 | 응답 시간 (신규) | 교차 회사 공고 목록 조회 API(FR-78)는 관심·발견 회사 합산 수천 건 규모에서 P95 500ms 이내 응답한다. |
| NFR-13 | 추천도 재평가 처리량 (신규) | 관심 공고(재평가 대상, FR-97) 200건 기준 일괄 재평가는 5분 이내 완료한다 — 외부 API 호출 없는 규칙 기반 계산(FR-96)이 전제다. |
| NFR-14 | 업로드 제약 (신규) | 지원 서류 업로드는 최대 20MB, 허용 확장자 pdf·docx·md·hwp를 벗어나면 즉시 거부한다(FR-83). |
| NFR-15 | 웹훅 인증 시간 범위 (신규) | 웹훅 HMAC 서명의 허용 시간 범위는 ±5분, 재전송 방지 nonce 보존은 24시간이다(FR-72). |
| NFR-16 | 동시성 (신규) | 단일 사용자 전제이므로 동시 다중 사용자 쓰기 충돌 방지 설계는 필요하지 않다. 다만 08:30 dedup 배치·09:00 알림 발송·사용자 수동 조작이 겹치는 짧은 창(수 분)의 순서 보장은 배치를 순차 실행 큐로 처리해 해결한다. |
| NFR-17 | 인증 강도 (신규, A-3, 2026-08-08) | FR-71에 따라 웹훅 경로 외 모든 앱 화면·API가 터널로 외부에 노출되고 인증이 유일한 방어선이므로, 세션·토큰 만료 시간(최대 24시간), 로그인 실패 5회 연속 시 일정 시간(예: 15분) 잠금, 자격 증명(비밀번호 해시·토큰 시크릿)의 서버 비밀 설정(환경 변수·비밀 관리자) 보관을 최소 기준으로 한다. |
[수정 — FR-70~72 반영] FR-89(이메일·문자 연락 확인형 반영) 도입으로 웹훅 수신을 위한 외부 노출이 불가피해졌습니다. 이에 따라 FR-70(단일 사용자 최소 인증)·FR-71(터널링, 웹훅 경로+인증 통과 화면·API 공개)·FR-72(웹훅 HMAC 서명 인증)를 신설했으며, NFR-7의 “별도 인증 체계(로그인)는 두지 않는다”는 전제는 더 이상 유효하지 않습니다. 2026-08-08부터 앱 전체 API는 최소 인증을 전제로 하고, 인증 없이 열리는 것은 웹훅 경로뿐입니다(A-3). 인증 강도 요구는 NFR-17을 따릅니다.
Operations
| 항목 | 내용 |
|---|---|
| 소스 고장 감지 | 특정 소스가 3일 연속으로 수집 실패 또는 0건을 반환하면 디스코드로 “소스 고장 의심” 알림을 발송한다(FR-19). 소스 가드가 활성화된 동안 해당 소스는 마감 판정에서 제외된 상태임을 함께 표기한다. |
| 수집 성공/실패 이력 조회 | 소스별 최근 수집 실행 결과(성공/실패, 수집 건수, 실행 시각, 오류 메시지)를 조회할 수 있는 화면 또는 로그 조회 수단을 제공한다. 최소 30일 이력을 유지한다. |
| 디스코드 웹훅 발송 실패 추적 | 웹훅 발송 실패(재시도 3회 모두 실패 포함)는 실패 이력으로 저장하고 조회할 수 있어야 한다 — 알림 자체가 실패하는 상황이므로, 알림에 의존하지 않는 별도 확인 수단(조회 화면)이 필요하다(시나리오 7). |
| 인크루트 요청량 관리 | 인크루트 소스는 변경 감지를 위해 상세 페이지를 전건 조회해야 하므로(FR-12), 요청 간 지연을 두고 1일 1회 수집 주기 내에서만 상세 페이지를 조회한다. 목표 사이트에 과도한 요청을 보내지 않도록 요청 수·간격 기준을 설계 단계(TDD)에서 정한다. |
| robots.txt·ToS 확인 절차 | 신규 어댑터를 추가하기 전 다음을 확인하고 기록한다: (1) 대상 사이트 robots.txt에서 목록·상세 페이지 수집 대상 경로의 차단 여부, (2) 이용약관상 자동 수집(크롤링) 금지 조항 여부, (3) 공개 API 사용 시 API 이용 정책의 요청 빈도 제한. 확인 결과를 어댑터 코드 또는 설계 문서에 근거로 남긴다. 애그리게이터 플랫폼도 동일한 절차를 따르며, 조사 브리프의 “채택 가능/규약 회색지대/불가” 분류를 1차 판단 근거로 삼는다. |
| 애그리게이터 규약 변경 모니터링 | 규약 회색지대로 분류된 플랫폼(예: 리멤버·잡코리아)은 이용약관 변경 여부를 주기적으로 재확인한다. robots·WAF 정책이 예고 없이 바뀔 수 있는 소스(예: 원티드 — robots.txt 자체가 WAF로 403)는 소스 고장 감지(FR-19)로 이상 징후를 포착하고, 감지 즉시 해당 소스 수집을 중단할 수 있어야 한다. |
| [폐기 — FR-54(수동 실행 원칙)·외부 LLM API 동기 호출 전제가 규칙 기반 전환(FR-96)으로 무효화됨] 아래 “추천도 재평가 실패 추적 (신규)” 행으로 대체됩니다. | |
| [폐기 — FR-54 수동 실행 원칙 폐기로 대체] 규칙 기반 추천도 계산 자체는 외부 API를 호출하지 않으므로 이 항목은 선택 기능인 AI 보조 추출(P2)에만 적용된다. AI 보조 추출을 활성화한 경우, 일자별 실행 횟수를 집계해 조회할 수 있는 수단을 제공한다 — 사용자가 외부 전송 빈도를 파악할 수 있어야 한다. | |
| 소스 상태 레지스트리 가시성 (신규) | 소스별 발견됨/활성/일시 실패/접근 제한/미지원/비활성(FR-88) 현재 상태와 마지막 전이 시각을 조회할 수 있는 화면 또는 API를 제공한다. |
| 웹훅 수신·서명 검증 실패 추적 (신규) | 연락 이벤트 웹훅(FR-89) 수신 건수, HMAC 서명 검증 실패 건수·사유(서명 불일치·시간 범위 초과·nonce 재사용)를 이력으로 저장하고 조회할 수 있다. |
| 추천도 재평가 실패 추적 (신규) | 관심 공고 일괄 재평가(FR-97) 실행 시 실패한 공고 수와 실패 사유(프로필 미확정·JD 없음 등)를 집계해 조회할 수 있다. |
| 서류 업로드 실패·저장 루트 접근 거부 추적 (신규) | 지원 서류 업로드 실패(확장자·용량 초과) 건수와 경로 새니타이즈(FR-84)에 의한 저장 루트 밖 접근 거부 건수를 이력으로 저장하고 조회할 수 있다. |
| 터널 연결 상태 확인 (신규) | 외부 노출 터널(FR-71)의 연결 상태(연결됨/끊김)와 마지막 연결 확인 시각을 확인할 수 있는 수단을 제공한다 — 터널이 끊기면 웹훅 수신 자체가 불가능하므로 별도 확인 수단이 필요하다. |
Success Metrics
| 지표 | 목표 | 측정 방법 |
|---|---|---|
| 수집 배치 성공률 | 등록 소스 기준 30일 롤링 평균 95% 이상 | 수집 성공/실패 이력 테이블 집계 |
| 신규 공고 알림 누락 | 사용자가 수동 확인으로 발견했으나 시스템이 놓친 신규 공고 월 0건 | 사용자 수동 리포트 기반 회고(월 1회) |
| 소스 고장 감지 지연 | 고장 발생 후 3일(연속 실패 또는 연속 0건 기준) 이내 알림 발송 | 소스 고장 알림 발송 시각과 실제 마지막 정상 수집 시각 비교 |
| 마감 오판정 | 정상 진행 중인 공고가 소스 가드 미작동으로 CLOSED 오판정되는 사고 월 0건 | 마감 이력과 실제 공고 페이지 재확인 대조(월 1회 샘플링) |
| 알림 중복 발송 | (공고ID + 알림종류 + 발송회차) 기준 동일 키 중복 발송 0건 | 알림 발송 이력에서 동일 키 재발송 여부 점검 |
| 크로스 소스 중복 알림 | 동일 공고가 회사 직접 소스·애그리게이터 양쪽에서 발견돼 신규 공고 알림이 2회 이상 발송되는 사고 월 0건 | 알림 발송 이력을 대표 공고 기준으로 집계해 동일 대표 공고에 대한 신규 공고 알림 발송 횟수 점검 |
| 마감 임박 지원 누락 | 마감일을 놓쳐 지원하지 못한 채 CLOSED된 공고 월 0건 (2단계 이후 적용 — D-1 알림이 P1이라 1단계에서는 측정하지 않는다) | 모수를 직무 키워드 매칭 + 마감일 존재 + 미지원 상태로 CLOSED된 공고로 한정하고, 그중 D-1 알림 발송 이력이 없는 케이스 집계 |
| 알림 인지 비율 | 사용자가 수동 확인 없이 알림만으로 신규 공고를 인지한 비율 90% 이상 | 월 1회 사용자 자가 응답(알림으로 알았는지 직접 확인했는지) 집계 |
| 웹훅 발송 실패율 | 30일 롤링 발송 실패율(3회 재시도 후 최종 실패 기준) 2% 이하 | 알림 발송 이력의 성공/실패 카운트 |
| 마감 전 처리율 (신규) | 마감된 관심·지원 예정 공고 중 마감 전 INTERESTED/PLANNED에서 지원함(Application 생성) 또는 EXCLUDED로 결정한 비율. 목표치는 4주 기준선 수집 후 확정 | 공고의 마감 시각과 관리 상태 변경 이력(FR-76)의 마지막 변경 시각 비교 |
| 지원 현황 최신성 (신규) | 진행 중 지원 중 최근 14일 내 상태 변경·면접·연락 검토 활동이 있는 비율. 목표치는 4주 기준선 수집 후 확정 | 지원 이력과 연락 이벤트 시각 집계 |
| 연락 후보 정확도 (신규) | 사용자가 확인한 연락 이벤트(FR-89·90)에서 시스템의 최상위 후보(FR-91)를 그대로 선택한 비율. 목표치는 4주 기준선 수집 후 확정 | 확인 결과와 1순위 후보 비교 |
| 추천 이상 등급 저장 비율 (신규) | 사용자가 관심 또는 지원 예정으로 저장한 공고 중 추천 이상 등급(FR-96)의 비율. 목표치는 4주 기준선 수집 후 확정 | 분모: 저장된 관심·지원 예정 공고 전체. 분자: 그중 등급이 추천 또는 강력 추천인 공고 수 |
| 등급별 저장률 (신규) | 등급(강력 추천/추천/검토/비추천)별로, 그 등급을 받은 공고 중 사용자가 실제로 관심/지원 예정으로 저장한 비율. 목표치는 4주 기준선 수집 후 확정 | 분모: 등급별 전체 평가 건수. 분자: 그중 관리 상태가 INTERESTED/PLANNED로 전환된 건수 |
| 추천도 재평가 완료율 (신규) | 현재 프로필/평가 기준 버전(FR-95·97)으로 평가된 관심 공고의 비율. 목표치는 4주 기준선 수집 후 확정 | 최신 기준 버전과 공고별 평가 버전 비교 |
| 활성 소스 수집 성공률 (신규) | 최근 7일 기준, 활성 상태 소스 중 예정된 수집 회차에 성공한 비율. 목표치는 4주 기준선 수집 후 확정 | 소스별 수집 실행 결과 집계(최근 7일 롤링) |
| 소스 커버리지 가시성 (신규) | 등록·발견된 소스 중 상태(FR-88)와 마지막 확인 시각이 있는 비율. 목표치는 4주 기준선 수집 후 확정 | 소스 레지스트리 집계 |
| 매칭 오탐률 (신규) | 매칭 범위 확대(FR-86) 이후 매칭된 공고 중 사용자가 EXCLUDED로 처리한 비율 — 필드별 가중치 도입이 오탐을 늘리지 않았는지 확인하는 지표. 목표치는 4주 기준선 수집 후 확정하며, 제목 단독 매칭 시절 대비 증가하지 않는 것을 1차 기준으로 한다 | 분모: 기간 내 매칭 성립 공고 수. 분자: 그중 관리 상태가 EXCLUDED로 전환된 공고 수 |
Milestones
| 단계 | 범위 | 포함 우선순위 |
|---|---|---|
| 1단계 | Greenhouse·인크루트·배민 어댑터 3종(초기 등록 회사: 당근·배민·우리은행·수협은행), 회사 반자동 등록(slug 프로빙 + URL 직접 입력), 수동 공고 등록, 수집·변경·마감 감지(소스 가드·마감일 정규화 포함), 신규 공고·소스 고장 알림, 근무형태 ①+② 추출(확신도 포함), 회사별 공고 목록 조회, 지원 상태 기록 및 전이 이력(면접 회차 포함), 애그리게이터 소스 수집(소스 유형 2종 분리, 미등록 회사 자동 등록 및 관심/발견 구분, 발견된 회사 일일 요약 알림, 크로스 소스 중복 판정·대표 공고 선정, 플랫폼 규약 준수) | P0 전부 |
| 2단계 | 마감 D-1 알림, 상시채용 7일 반복 리마인드, 자소서 문항·답변 수동 관리(초안→제출 흐름), 잡코리아 어댑터, 웹 검색 보조 소스 탐색, | P1 전부 |
| 3단계 | 같은 회사 다른 공고 기반 근무형태 추론(③단계, 레퍼런스 전용) 및 해당 근거의 부정어 처리, 자소서 답변 재사용 검색, 지원 통계, 적합도 점수 사후 보정(구간별 실제 서류 통과율) | P2 전부 |
[폐기 — FR-81
85·9596(신규 2단계)로 대체] 위 2단계 행의 “이력서 등록(PDF·URL, 버전 관리)·수동 실행 적합도 평가(점수+장단점+총평)“는 FR-5358 폐기·B-21(URL 입력 경로 폐지)과 정면 충돌하는 구 서술입니다. 실제 2단계 범위는 아래 “신규 기능(FR-7097) 3단계 분할”의 **1단계(FR-8185, 서류 버전 관리)·2단계(FR-9596, 이력서 프로필·지원 추천도 기본 산출)**로 대체되었습니다 — URL 입력 경로 없이 PDF/DOCX/Markdown 업로드만 지원하고, 점수·등급·축별 근거를 규칙 기반으로 산출합니다(장단점·총평 정성 텍스트는 산출하지 않음).
신규 기능(FR-70~97) 3단계 분할 (2026-08-08, D-4)
위 13단계는 기존 FR-169 범위입니다. FR-70~97은 아래 별도 3단계로 진행하며, 각 단계는 독립 배포 단위입니다.
| 단계 | 범위 | 완료 판정 기준 |
|---|---|---|
| 1단계 — 기반 | 인증·외부 노출 기반(FR-70 | 인증 없이 API 호출 시 401, 웹훅 경로만 터널로 접근 가능, 관리 상태 3종 CRUD·교차 회사 목록 API·대시보드 3요소·담당자 CRUD가 실 DB로 동작 |
| 2단계 — 서류·추천도 | FR-4 서류 버전 관리(FR-81 | 서류 업로드→저장 경로 생성→지원 건 연결까지 1회 왕복 성공, 이력서 업로드→프로필 확정→관심 공고 1건 이상 추천도(점수+등급+축별 근거) 산출 성공 |
| 3단계 — 연동 | FR-7 연락 확인형 반영·Gmail 연동(FR-89 | Gmail 웹훅 1건 수신→검토 화면 반영까지 1회 왕복 성공, 소급 재평가 후 신규 매칭 공고에 알림이 발송되지 않음을 확인, 사용자 가중치 수정 후 재평가 결과가 반영됨 |
Open Questions
이전 초안의 미결 항목(디스코드 웹훅 채널 분리 여부, 소스 고장 판정 임계값, 마감 판정 연속 미발견 횟수, 알림 발송 시각, 초기 등록 회사)은 모두 사용자 확정을 받아 본문에 반영했으므로 FR-1~69 범위에서는 미결 사항이 없습니다.
FR-70~97 편입(2026-08-08)에 따른 미결 사항
| 항목 | 내용 |
|---|---|
| Success Metrics 목표치 | 신규 편입된 7개 지표(마감 전 처리율·지원 현황 최신성·연락 후보 정확도·추천 이상 등급 저장 비율·등급별 저장률·추천도 재평가 완료율·활성 소스 수집 성공률)와 매칭 오탐률은 목표치를 정하지 않았습니다. 4주 기준선 수집 후 확정합니다. |
| SMS 수신 경로 | FR-89는 웹훅 페이로드에 channel 필드를 두어 문자(SMS) 확장을 열어두었지만, 실제 수집 주체·경로(통신사 API·특정 앱 연동 등)는 확보하지 않았습니다(B-11). 1차는 Gmail 전용으로 구현합니다. |
| 매칭 필드별 가중치·임계치 수치 | FR-86의 제목/JD 본문/구조화 태그 가중치와 매칭 성립 임계치의 구체 수치는 이 PRD에서 확정하지 않고 기술 설계 문서(TDD)에서 정합니다. |
| 서류 저장소 백업·복구·보존 정책 | FR-82 저장 경로의 백업 주기, 복구 절차, 보존 기간은 정하지 않았습니다(docs/PRD.md §278 후속 우선순위와 동일 미결). |
| 웹훅 비밀값 회전 주기 | FR-72 HMAC 서명에 쓰이는 공유 비밀의 회전 주기·절차는 정하지 않았습니다. |
Document History
| 날짜 | 변경 내용 |
|---|---|
| 2026-07-22 | 최초 작성 — 4라운드 요구사항 합의 및 채용 소스 조사 브리프 반영 |
| 2026-07-22 | 1차 검수(NEEDS_REVISION) 반영 — 근무형태 ②단계 P0 승격, 알림 멱등 키에 발송회차 추가, 상태 전이 종료 상태·OFFERED 허용 전이 명확화, 확신도 매핑 표 추가, 수동 공고 등록 FR 신설, 지원 통계 FR 신설, 마감일 정규화(null=상시채용) FR 신설, 회사 수 4곳으로 정정, 기술 선택 서술을 스코프 선언 수준으로 완화, Open Questions 전항목 확정 반영 |
| 2026-07-22 | 신규 기능 추가 — 이력서 기반 서류 적합도 평가(P1, FR-53 |
| 2026-07-22 | 애그리게이터(통합 채용 플랫폼) 수집을 1단계(P0)로 편입 — FR-60 |
| 2026-08-08 | docs/PRD.md(개인 채용 지원 통합 관리, 282줄) 내용을 FR-70 |
| 2026-08-08 | 재공고 비승계·MANUAL dedup_key 부여·검토 화면 인증 노출 확정, 폐기 잔재 정리, FR-70 FE 범위 명시 |