[FE-09] 회사 목록 화면 (홈)
작업 내용 (설계 의도)
근거 설계: 20260722-공고알림앱-design-fe-web.md — “S-01 회사 목록”, “방안 1 내비게이션 축”
근거 요구: FR-50(회사 단위 분류)
변경 사항
앱의 홈이자 회사 우선 내비게이션의 출발점입니다. BE 계약에 전 회사 통합 공고 조회 엔드포인트가 없고(계약 요청 #3), FR-50이 “회사 단위 분류”를 P0으로 요구하므로 회사 목록을 최상위에 둡니다.
핵심 설계 의도:
- **이 화면의 유일한 과업은 “회사를 고르거나 새로 등록하는 것”**입니다(토스 1 thing per 1 page). 카드에 공고 건수·최근 수집 시각 같은 정보를 얹지 않습니다 — 얹는 순간 이 화면이 대시보드가 되고 선택이라는 과업이 흐려집니다.
- 주 CTA는 “회사 추가하기” 하나이고 하단 고정입니다. accent를 이 한 곳에만 씁니다.
- empty 상태에서는 CTA를 화면 중앙에도 배치합니다 — 빈 화면에서 하단 고정 CTA는 시선에서 멀고, 첫 사용자가 반드시 통과해야 하는 관문이기 때문입니다.
MANUAL_ONLY회사에 중립 칩(“수동 등록 전용”)을 표시합니다. 자동 수집이 안 되는 회사임을 목록에서 바로 알 수 있어야 사용자가 “왜 공고가 안 늘지”를 자문하지 않습니다.- 소스 고장 배지를 danger 톤으로 표시합니다(시나리오 4). 알림을 놓쳤어도 홈에서 이상을 인지할 수 있는 경로입니다.
- 조회 실패 시에도 하단 CTA는 유지합니다 — 목록을 못 불러와도 등록은 가능해야 합니다.
- 회사 수정·삭제 UI를 만들지 않습니다 — BE 계약에 없어 P0 범위 밖입니다(설계 Open Questions #1).
계약 리스크: 소스 고장 배지가 GET /api/companies 응답의 고장 상태 필드에 의존합니다. TDD 계약 표에는 없고 BE-17 테스트 케이스에는 있어 문서 간 불일치입니다 → 계약 요청 #5. 필드가 없으면 배지를 렌더하지 않는 폴백으로 동작합니다(화면 자체는 성립).
범위: src/pages/company/CompanyListPage.tsx(스텁 대체), CompanyCard(이 화면 전용 — 공용화하지 않습니다), src/api/company/query.ts, src/hooks/company/useCompanies.ts.
Single Writer 주의: src/api/company/discovery.ts·registration.ts와 등록 훅은 FE-08이 소유합니다. 이 티켓은 건드리지 않습니다.
의존
- FE-02, FE-03, FE-04
- BE 의존: BE-17 (회사 목록 조회 API)
다이어그램
처리 흐름
sequenceDiagram participant U as 사용자 participant P as CompanyListPage participant H as useCompanies participant A as api/company/query participant S as 서버 U->>P: / 진입 P->>H: 조회 H->>A: GET /api/companies A->>S: 요청 alt 성공 · 1건 이상 S-->>P: 회사 목록 P-->>U: 카드 목록 + 하단 CTA U->>P: 카드 클릭 P-->>U: /companies/{id} 이동 else 성공 · 0건 P-->>U: empty 안내 + 중앙 CTA else 실패 P-->>U: 에러 대체 + 재시도 (하단 CTA 유지) end
클래스 의존
flowchart LR subgraph Page["pages/company"] List[CompanyListPage] Card[CompanyCard] end subgraph Hooks["hooks/company"] H[useCompanies] end subgraph Api["api/company"] Q[query.ts] end subgraph Ui["components/ui · domain"] C[Card] Chip Btn[Button] Empty[EmptyState] Err[ErrorState] Health[SourceHealthBadge] end List --> H List --> Card List --> Btn List --> Empty List --> Err Card --> C Card --> Chip Card --> Health H --> Q
테스트 케이스
- 등록된 회사 목록이 이름과 소스 개수와 함께 렌더된다
- 회사 카드 클릭 시
/companies/{id}로 이동한다 MANUAL_ONLY회사에 “수동 등록 전용” 중립 칩이 렌더된다- 소스 고장 상태인 회사에 danger 배지가 렌더된다
- 고장 상태 필드가 응답에 없어도 크래시 없이 렌더된다 (계약 요청 #5 폴백)
- 회사가 0건이면 empty 안내와 중앙 CTA가 렌더된다
- 하단 “회사 추가하기” 클릭 시
/companies/new로 이동한다 - 조회 실패 시 에러 대체가 렌더되고 하단 CTA는 유지된다
- 에러 상태에서
[다시 시도]클릭이 재요청을 발생시킨다 - 로딩 중 카드 스켈레톤이 렌더되고 헤더·CTA는 즉시 보인다
- 회사 수정·삭제 버튼이 존재하지 않는다
- 다크 모드에서 배지·칩이 오류 없이 렌더된다
2차 갱신 반영 (2026-07-22) — 관심/발견 세그먼트 · 페이지네이션 · 승격
애그리게이터 편입으로 홈이 크게 확장됩니다. 파일 소유 경계(company/query.ts·useCompanies)는 유지하고 company/promotion.ts·useCompanyPromotion을 추가로 이 티켓이 소유합니다(FE-08의 discovery.ts·registration.ts와 파일이 달라 충돌 없음).
관심/발견 세그먼트 (방안 8)
- 홈에
관심 N/발견 N세그먼트. 기본은 관심 탭(이 앱의 본질은 타깃 구직). 상태는 URL이 SSOT(?companyOrigin=WATCHED|DISCOVERED). - 관심 탭은 10~20곳이라 페이지네이션 없이 전건, 발견 탭만 서버 페이지네이션(
?page=&size=, 수백 곳). - 발견 탭 카드는
DiscoveredCompanyCard(이 티켓 전용) — 인라인[관심 등록]승격 버튼 포함. 관심 탭 카드는 기존CompanyCard.
승격 액션 (FR-61, 시나리오 9.5)
[관심 등록]→POST /api/companies/{id}/promotion→ 성공 토스트 +['companies']접두사 전체 무효화(관심·발견 두 탭 갱신). 낙관적 업데이트 미적용(목록 간 이동이라 되돌림 애니메이션이 혼란).
brokenSourceCount 배지 (계약 응답 #5)
- 소스 고장 배지는
brokenSourceCount > 0조건. “소스 N개 고장”으로 개수를 표기(복수 소스 회사의 “2개 중 1개 고장” 표현).
발견 소스 진입점
- 발견 탭 헤더에
[+ 발견 소스 추가]→/aggregator-sources/new(FE-18). 홈 주 CTA “회사 추가하기”는 관심 회사 등록 전용으로 유지.
추가 테스트 케이스
- 홈 진입 시 관심 탭이 기본 선택되고 관심 회사만 렌더된다
- 발견 탭으로 전환하면 URL
companyOrigin=DISCOVERED가 갱신되고 발견 회사가 페이지네이션과 함께 렌더된다 - 발견 탭에서 페이지를 넘기면 URL
page가 갱신되고 다음 페이지가 조회된다 - 발견 회사 카드의
[관심 등록]클릭 시 승격 API가 호출되고 성공 시 관심 탭으로 이동한다 - 승격 실패 시 회사가 발견 탭에 유지되고 에러 토스트가 표시된다
brokenSourceCount가 2면 “소스 2개 고장” 배지가 렌더된다brokenSourceCount가 0이면 고장 배지가 렌더되지 않는다- 발견 탭 헤더의
[+ 발견 소스 추가]클릭 시/aggregator-sources/new로 이동한다 - 관심 탭 0건과 발견 탭 0건이 서로 다른 empty 문구로 렌더된다
- URL에
companyOrigin=DISCOVERED를 직접 넣고 진입하면 발견 탭이 선택된 상태로 렌더된다
3차 갱신 반영 (2026-07-22)
- 발견 소스 진입점을 **
[발견 소스 관리]→/aggregator-sources(S-13 관리 화면, FE-18)**로 변경합니다. 등록(S-12)은 관리 화면의 CTA로 진입하므로, 홈 발견 탭 헤더는 관리 화면으로 연결합니다(기존 “발견 소스 추가 → /aggregator-sources/new” 직결에서 변경).
정정 테스트 케이스
- 발견 탭 헤더의
[발견 소스 관리]클릭 시/aggregator-sources(관리 화면)로 이동한다