[FE-08] 회사 등록 위저드 — 소스 탐색 · 후보 확정 · 후보 0건 분기
작업 내용 (설계 의도)
근거 설계: 20260722-공고알림앱-design-fe-web.md — “S-02 회사 등록”
근거 요구: FR-1(회사명 입력) · FR-2(slug 프로빙 + URL 직접 입력 + 샘플 3건) · FR-4(복수 소스 1:N) · FR-5(후보 0건 → 수동 등록 전용) · 시나리오 1 · 시나리오 5
변경 사항
이 앱의 첫인상을 만드는 화면입니다. 회사명 입력 → 후보 탐색(동기·수 초 소요) → 후보 + 샘플 공고 3건 미리보기 → 사용자 확정의 반자동 흐름을 3스텝 위저드로 구현합니다.
핵심 설계 의도:
- 한 스텝에 질문 하나(토스 1 thing per 1 page). 회사명·URL을 받는 Step 1, 탐색 로딩 Step 2, 후보 확정 Step 3로 나눕니다. 한 화면에 다 넣으면 로딩 중 무엇을 기다리는지가 흐려집니다.
- 탐색 로딩을 전면화합니다. 수 초~수십 초가 걸리는 동기 작업이므로 스피너만 두지 않고 무엇을 하는 중인지(“채용 플랫폼 3곳을 확인하는 중”)와 예상 소요(“최대 30초”)를 함께 보여줍니다. 대기가 실패처럼 느껴지지 않게 하는 장치입니다.
- 로딩을 취소 불가로 두지 않습니다 — 헤더
‹로 Step 1 복귀가 가능하고, 진행 중 요청은AbortController로 취소합니다. - 샘플 공고 3건 미리보기가 후보 확정의 근거입니다(FR-2). 미리보기가 없으면 잘못된 slug를 확정해 엉뚱한 회사 공고를 수집하는 사고가 납니다. 후보 카드마다 제목 3건을 반드시 렌더합니다.
- 복수 선택을 지원합니다(FR-4) — 우리은행처럼 인크루트와 잡코리아를 함께 갖는 사례가 조사에서 확인됐습니다. 체크박스 다중 선택 + CTA에 선택 개수를 표시합니다.
- 후보 0건이 막다른 길이 아니라 분기가 되어야 합니다(FR-5, 시나리오 5). Step 3b에서 ① “채용 페이지 주소로 다시 찾기”(Step 1 URL 입력 복귀) ② “수동 등록 전용으로 저장”(주 CTA) 두 탈출구를 제공하고, “공고를 직접 추가하면 지원 관리는 똑같이 사용할 수 있어요”(FR-21 보장)를 명시해 등록이 헛되지 않음을 알립니다.
- 등록 성공 토스트에 시딩 사실을 담습니다 — “오늘 자정부터 공고를 확인해요”. 첫 수집은 시딩이라 알림이 오지 않는데(FR-10) 이를 모르면 사용자는 시스템이 고장 났다고 판단합니다.
- 위저드 상태는 라우트 지역 상태(
useReducer)입니다. 전역 승격 반려 근거: 라우트 이탈 후 되살아나는 것이 오히려 버그이고, 중단된 등록의 복원은 요구사항이 아닙니다.
범위: src/pages/company/RegisterCompanyPage.tsx(스텁 대체), 전용 스텝 컴포넌트 4종, src/api/company/discovery.ts, src/api/company/registration.ts, src/hooks/company/useSourceDiscovery.ts, useRegisterCompany.ts.
Single Writer 주의: src/api/company/query.ts와 useCompanies는 FE-09가 소유합니다. 이 티켓은 건드리지 않습니다.
의존
- FE-02, FE-03, FE-04
- BE 의존: BE-17 (회사 등록·소스 탐색 API)
다이어그램
처리 흐름
sequenceDiagram participant U as 사용자 participant W as RegisterCompanyPage participant D as useSourceDiscovery participant R as useRegisterCompany participant S as 서버 U->>W: 회사명 입력 후 "공고 찾아보기" W->>D: mutate(companyName, siteUrl?) D->>S: POST /api/companies/source-discoveries alt 후보 1건 이상 S-->>D: candidates + samplePostings[3] W-->>U: Step 3a 후보 목록 (복수 선택) U->>W: 후보 확정 W->>R: mutate(name, sources) R->>S: POST /api/companies S-->>R: {companyId} W-->>U: 공고 목록으로 이동 + 시딩 안내 토스트 else 후보 0건 S-->>D: candidates 빈 배열 W-->>U: Step 3b — URL 재시도 / 수동 등록 전용 저장 U->>W: 수동 등록 전용 저장 W->>R: mutate(name, sources=[]) S-->>R: {registrationType: MANUAL_ONLY} end
클래스 의존
flowchart LR subgraph Page["pages/company"] Wiz[RegisterCompanyPage] S1[CompanyNameStep] S2[DiscoveringStep] S3[CandidateSelectStep] S4[NoCandidateStep] CC[SourceCandidateCard] end subgraph Hooks["hooks/company"] HD[useSourceDiscovery] HR[useRegisterCompany] end subgraph Api["api/company"] AD[discovery.ts] AR[registration.ts] end Wiz --> S1 Wiz --> S2 Wiz --> S3 Wiz --> S4 S3 --> CC Wiz --> HD Wiz --> HR HD --> AD HR --> AR
테스트 케이스
- 회사명이 비어 있으면 “공고 찾아보기” 버튼이 비활성이다
- 회사명을 입력하고 제출하면 탐색 로딩 스텝으로 전환되고 소요 시간 안내가 보인다
- 탐색이 성공하면 후보 목록과 각 후보의 샘플 공고 3건이 렌더된다
- 후보를 2개 선택하면 CTA에 선택 개수가 반영되고 등록 시 소스 2건이 전송된다
- 아무 후보도 선택하지 않으면 등록 CTA가 비활성이다
- 탐색 결과가 0건이면 “수동 등록 전용으로 저장” 경로와 URL 재시도 경로가 모두 보인다
- 후보 0건 상태로 저장하면 빈 소스 배열로 등록 요청이 전송된다
- 후보 0건 화면에 “지원 관리는 똑같이 사용할 수 있어요” 안내가 렌더된다
- URL을 직접 입력하고 탐색하면 URL이 요청에 포함된다
- 잘못된 형식의 URL을 입력하면 인라인 검증 에러가 보이고 요청이 전송되지 않는다
- 탐색이 실패(500)하면 재시도 버튼과 수동 등록 전용 분기가 함께 렌더된다
- 로딩 중 뒤로가기를 누르면 Step 1로 복귀하고 진행 중 요청이 취소된다
- 회사명 중복(409)이면 인라인 에러와 기존 회사로 이동하는 링크가 보인다
- 등록에 성공하면 회사 공고 목록으로 이동하고 시딩 안내 토스트가 표시된다
- 등록 실패(500) 시 후보 선택 상태가 유지된 채 에러 토스트가 표시된다