[FE-18] 애그리게이터 소스 등록 화면
작업 내용 (설계 의도)
근거 설계: 20260722-공고알림앱-design-fe-web.md — “S-12 애그리게이터 소스 등록”, “방안 8”
근거 요구: FR-60(애그리게이터 소스 = 플랫폼 + 검색 조건) · 시나리오 9
변경 사항
애그리게이터(사람인·점핏·원티드·리멤버·잡코리아·서핏 — 4차 갱신으로 6종) 소스를 등록하는 화면입니다. 회사 등록(회사명 → 탐색 → 확정)과 흐름이 다릅니다 — 탐색 단계가 없고, 플랫폼을 고르고 검색 조건(직무 카테고리·키워드)을 지정하는 단일 폼입니다.
핵심 설계 의도:
- 위저드가 아니라 단일 폼입니다(토스 1 thing per 1 page + Minimum Input). 탐색·확정 단계가 없어 회사 등록 위저드(FE-08)와 컴포넌트를 공유하지 않습니다.
- 직무 카테고리는 플랫폼별 코드(사람인
cat_kewd, 점핏jobCategory)입니다. 사용자에게 코드를 노출하지 않고 한글 라벨 Select로 제시하며, 플랫폼 전환 시 선택지가 바뀝니다. 매핑은 FE-03의aggregatorCategories상수를 소비합니다(계약 요청 12로 BE 확정 대기). - 키워드는 선택입니다 — 비우면 카테고리 전체 수집(Minimum Input).
- 발견 회사의 성격을 등록 시점에 안내합니다 — “관심 회사와 구분 / 일일 요약 / 승격 가능”(FR-62). 이 안내가 없으면 사용자는 왜 개별 알림이 안 오는지 의아해합니다.
규약 경고를 띄우지 않습니다 (플랫폼 2종 한정)→ 4차 갱신에서 무효 — 회색지대 4종이 P0로 편입돼 플랫폼이 6종이 됐고, 회색지대 선택 시 안내 배너를 노출합니다. 아래 “4차 갱신 반영” 참조.- 등록 성공 시 홈 발견 탭으로 이동(
/?companyOrigin=DISCOVERED)하고 “오늘 자정부터 이 조건으로 공고를 찾아요” 토스트를 표시합니다 — 첫 수집이 다음 자정이라 즉시 회사가 보이지 않음을 알립니다. - 제출 실패(5xx) 시 입력값을 보존합니다.
진입점: 홈 발견 탭 헤더의 [+ 발견 소스 추가](FE-09가 링크를 렌더). 라우트 /aggregator-sources/new는 FE-01이 스텁으로 선언하며, 이 티켓이 스텁을 대체합니다.
계약: POST /api/aggregator-sources는 존재합니다. 등록 소스 목록 조회·삭제 API는 없어(계약 요청 #10) P0는 등록 전용으로 두고 소스 확인·해제 UI를 만들지 않습니다.
범위: src/pages/aggregator/AggregatorSourcePage.tsx(스텁 대체), 전용 컴포넌트(PlatformSegment·CategorySelect), src/api/aggregator.ts, src/hooks/aggregator/useRegisterAggregatorSource.ts.
의존
- FE-02 (UI 프리미티브), FE-03 (카테고리 상수·검증)
- BE 의존: BE-17 (회사·소스 등록 — 애그리게이터 소스 등록 API 포함) 또는 신규 BE 티켓. 실 연동은 그 이후, 개발·테스트는 MSW 목으로 선행
다이어그램
처리 흐름
sequenceDiagram participant U as 사용자 participant P as AggregatorSourcePage participant C as aggregatorCategories participant M as useRegisterAggregatorSource participant S as 서버 U->>P: 플랫폼 "사람인" 선택 P->>C: 사람인 카테고리 목록 C-->>P: 한글 라벨 Select 채움 U->>P: 카테고리 선택 · 키워드 입력 후 제출 P->>M: mutate(platform, searchCategoryCode, searchKeyword?) M->>S: POST /api/aggregator-sources alt 성공 S-->>M: {jobSourceId, platform} M-->>U: 홈 발견 탭으로 이동 + 토스트 else 실패 S-->>M: 400 또는 5xx M-->>U: 인라인/토스트 + 입력값 보존 end
클래스 의존
flowchart LR subgraph Page["pages/aggregator"] Main[AggregatorSourcePage] Plat[PlatformSegment] Cat[CategorySelect] end subgraph Hooks["hooks/aggregator"] H[useRegisterAggregatorSource] end subgraph Api["api"] A[aggregator.ts] end subgraph Const["constants"] Cats[aggregatorCategories] end subgraph Ui["components/ui"] Seg[Segment] Sel[Select] TF[TextField] Btn[Button] end Main --> Plat Main --> Cat Main --> H Plat --> Seg Cat --> Sel Cat --> Cats Main --> TF Main --> Btn H --> A
테스트 케이스
- 플랫폼 Select에 6종(사람인·점핏·원티드·리멤버·잡코리아·서핏)이 렌더된다 (4차 갱신)
- 플랫폼을 점핏으로 바꾸면 카테고리 Select 선택지가 점핏 카테고리로 교체된다
- 카테고리를 선택하지 않으면 “소스 추가하기” 버튼이 비활성이다
- 키워드 없이(카테고리만) 제출하면
searchKeyword가 미전송된다 - 카테고리·키워드를 모두 입력해 제출하면 세 값이 전송된다
- 발견 회사 성격 안내(일일 요약·승격 가능)가 화면에 렌더된다
- 등록 성공 시 홈 발견 탭(
?companyOrigin=DISCOVERED)으로 이동하고 안내 토스트가 표시된다 - 등록이 5xx면 입력값이 보존된 채 에러 토스트가 표시된다
- 제출 중 CTA에 로딩이 표시되고 폼이 비활성화된다
- 청정 플랫폼(사람인·점핏) 선택 시 회색지대 안내 배너가 렌더되지 않는다 (4차 갱신)
- 다크 모드에서 세그먼트·Select·안내 박스가 오류 없이 렌더된다
3차 갱신 반영 (2026-07-22, BE 최종 확정)
이 티켓이 애그리게이터 도메인의 등록(S-12) + 관리(S-13) 두 화면을 소유합니다. 파일 소유 경계(pages/aggregator/**·api/aggregator.ts·hooks/aggregator/**)는 그대로라 wave·티켓 수 무변경.
1. 카테고리를 API로 채움 (상수화 금지)
- 직무 카테고리 Select를
GET /api/aggregator-sources/categories?platform=으로 채웁니다({categories:[{code, label}]}). FE 상수화는 어댑터 지원 코드와 드리프트가 나므로 금지. - 플랫폼 세그먼트가 바뀌면 카테고리를 재조회합니다 — 의존 쿼리(
useAggregatorCategories(platform), queryKey['aggregator-categories', platform],enabled: !!platform). 카테고리 옵션은 서버 상태(Query)라useState에 복사하지 않습니다. - 카테고리 로딩 중 Select는 “불러오는 중” placeholder + CTA 비활성. 조회 실패 시 Select 자리에 에러 +
[다시 시도]. - 등록 성공 시 소스 관리 화면(S-13)으로 이동(기존 “홈 발견 탭”에서 변경) — 방금 추가한 소스가 목록에 나타나 반영을 즉시 확인.
2. 소스 관리 화면 S-13 추가 (AggregatorSourceListPage)
GET /api/aggregator-sources(활성·비활성 함께) 목록 +[발견 소스 추가]CTA(→ S-12).- 비활성 소스는
disabled=true로 흐리게(text-tertiary + 점선 테두리 + “중지된 소스예요”). 소프트 삭제라 이력이 보존됩니다. DELETE /api/aggregator-sources/{id}(소프트 삭제)는 확인 다이얼로그 없이 낙관적 비활성 처리 → 실패 시 롤백(onMutate스냅샷 →onError복원 →onSettled['aggregator-sources']무효화).- 진입점: 홈 발견 탭 헤더
[발견 소스 관리]→ S-13. S-13이 S-12의 부모. - 4상태: loading(카드 스켈레톤) / empty(“아직 등록한 발견 소스가 없어요” + 중앙 CTA) / error(목록 에러 대체, CTA 유지) / success.
추가 컴포넌트·훅
AggregatorSourceListPage,AggregatorSourceItem(활성/비활성 위계),CategorySelect(API 소비)useAggregatorCategories(platform),useAggregatorSources(),useDeleteAggregatorSource()
추가 테스트 케이스
- 플랫폼을 사람인→점핏으로 바꾸면 카테고리를 재조회하고 선택지가 교체된다
- 카테고리 조회 중 Select가 로딩 상태이고 CTA가 비활성이다
- 카테고리 조회 실패 시 에러와
[다시 시도]가 렌더된다 - 카테고리가 0건이면 안내 문구가 렌더되고 CTA가 비활성이다
- 등록 성공 시 소스 관리 화면(S-13)으로 이동한다
- S-13 목록에 활성 소스와 비활성(disabled) 소스가 흐리게 함께 렌더된다
- 비활성 소스에 “중지된 소스예요” 표기가 렌더된다
[삭제]클릭 시 낙관적으로 비활성 처리되고 실패 시 원복된다- S-13에서
[발견 소스 추가]클릭 시 S-12(/aggregator-sources/new)로 이동한다 - S-13 소스 0건이면 empty 안내와 중앙 CTA가 렌더된다
- 다크 모드에서 활성/비활성 위계가 오류 없이 렌더된다
4차 갱신 반영 (2026-07-22, 회색지대 애그리게이터 P0 편입)
- 플랫폼 선택지 2→6종(사람인·점핏 + 원티드·리멤버·잡코리아·서핏). 6개라 세그먼트가 아니라 Select(
PlatformSelect)로 제시합니다. - 회색지대 4종 선택 시 안내 배너 — warning-subtle 배너로 “이 플랫폼은 서비스 약관상 자동 수집에 제약이 있어요. 개인 열람 용도로만 사용됩니다”. 청정 2종(사람인·점핏)은 배너 미노출로 시각 구분. 회색지대 판정은 FE-03
isGrayZonePlatform에 위임. 배너는 등록을 막지 않습니다(안내 전용). - 카테고리는 여전히
GET .../categories?platform=로 동적 구성 — 플랫폼이 늘어도 카테고리 로직은 무변경(플랫폼 변경 시 재조회 그대로).
추가 테스트 케이스
- 플랫폼 Select에 6종이 모두 렌더된다
- 회색지대 플랫폼(원티드) 선택 시 안내 배너가 노출된다
- 청정 플랫폼(사람인) 선택 시 안내 배너가 노출되지 않는다
- 회색지대 배너가 있어도 카테고리·키워드가 정상이면 등록 CTA가 활성이다(등록을 막지 않음)