[FE-12] 수동 공고 등록 화면
작업 내용 (설계 의도)
근거 설계: 20260722-공고알림앱-design-fe-web.md — “S-05 수동 공고 등록”
근거 요구: FR-20(수동 등록) · FR-21(동일 지원 관리) · FR-48(로그인·자소서 필수 공고 제외) · FR-13(마감일 없음 = 상시채용) · 시나리오 5 · 시나리오 6
변경 사항
자동 수집이 불가능한 공고(로그인 필요·자소서 필수·매핑 후보 0건 회사)를 사용자가 직접 등록하는 화면입니다. 등록된 공고는 자동 수집 공고와 같은 목록·같은 지원 관리 흐름에 들어갑니다(FR-21).
핵심 설계 의도:
- Minimum Input — 필수는 공고명·링크 2개뿐입니다. 회사는 진입 경로(
?companyId=)에서 이미 결정되므로 다시 묻지 않고 상단에 고정 표시합니다. 마감일은 선택입니다. - 마감일을 비우면 상시채용임을 필드 하단에 명시합니다(FR-13). 이 규칙을 모르면 사용자는 마감일을 억지로 채웁니다.
- 자동 공고와의 차이를 안내 박스로 명시합니다 — “직접 추가한 공고는 자동 수집·마감 판정 대상이 아니에요. 지원 관리는 똑같이 사용할 수 있어요.” 앞 문장은 기대 관리, 뒤 문장은 FR-21 보장입니다. 이 안내가 없으면 사용자는 수동 공고가 자동으로 마감 처리될 것이라 기대하거나, 반대로 지원 관리가 안 될 것이라 오해합니다.
- 입력 검증은 순수 함수에 위임합니다(FE-03의
isBlank·isHttpUrl). 폼 라이브러리를 도입하지 않는 대신 검증을 테스트 가능한 형태로 분리합니다. - 제출 실패(5xx) 시 입력값을 보존합니다 — 사용자가 붙여넣은 링크를 다시 찾게 만들지 않습니다.
companyId쿼리 파라미터가 없으면 회사 선택 셀렉트를 렌더합니다(지원 목록 등 다른 경로에서 진입할 여지). 이 경우에만 회사 목록 조회의 loading 상태가 존재합니다.
범위: src/pages/posting/ManualJobPostingPage.tsx(스텁 대체), src/api/posting/manualRegistration.ts, src/hooks/posting/useRegisterManualJobPosting.ts.
Single Writer 주의: api/posting/list.ts(FE-10)·detail.ts(FE-11)를 건드리지 않습니다. 회사 목록 조회가 필요하면 FE-09의 useCompanies를 소비만 합니다.
의존
- FE-05
- BE 의존: BE-18 (수동 공고 등록 API)
다이어그램
처리 흐름
sequenceDiagram participant U as 사용자 participant P as ManualJobPostingPage participant V as validation participant M as useRegisterManualJobPosting participant S as 서버 U->>P: 공고명 · 링크 입력 후 제출 P->>V: isBlank(title) · isHttpUrl(url) alt 검증 실패 V-->>P: 실패 P-->>U: 인라인 에러 (요청 전송 없음) else 검증 통과 P->>M: mutate(companyId, title, url, deadlineAt?) M->>S: POST /api/job-postings/manual-registrations alt 성공 S-->>M: {jobPostingId} M-->>U: 공고 상세로 이동 + 토스트 else 실패 S-->>M: 404 또는 500 M-->>U: 에러 표시 + 입력값 보존 end end
클래스 의존
flowchart LR subgraph Page["pages/posting"] Manual[ManualJobPostingPage] end subgraph Hooks["hooks/posting"] H[useRegisterManualJobPosting] end subgraph Api["api/posting"] A[manualRegistration.ts] end subgraph Utils["utils"] V[validation] end subgraph Ui["components/ui"] TF[TextField] DF[DateField] Btn[Button] end Manual --> H Manual --> V Manual --> TF Manual --> DF Manual --> Btn H --> A
테스트 케이스
?companyId=1로 진입하면 회사명이 고정 표시되고 회사 입력 필드가 없다companyId없이 진입하면 회사 선택 셀렉트가 렌더된다- 공고명이 비어 있으면 제출 시 인라인 에러가 보이고 요청이 전송되지 않는다
- 잘못된 형식의 링크(
javascript:등)는 인라인 에러로 거부된다 - 마감일 필드 하단에 “비워두면 상시채용으로 저장해요” 안내가 렌더된다
- 마감일 없이 제출하면
deadlineAt이null로 전송된다 - 마감일과 함께 제출하면 해당 값이 전송된다
- 자동 공고와의 차이를 알리는 안내 박스가 렌더된다
- 등록 성공 시 공고 상세로 이동하고 토스트가 표시된다
- 등록이 404(존재하지 않는 회사)면 전면 에러와 홈 이동 수단이 렌더된다
- 등록이 500이면 입력값이 보존된 채 에러 토스트가 표시된다
- 제출 중 CTA에 로딩이 표시되고 폼이 비활성화된다
- 다크 모드에서 입력 필드와 안내 박스가 오류 없이 렌더된다