[FE-22] 라우트 골격 · 5탭 내비게이션 · AuthGate 스텁

작업 내용 (설계 의도)

근거: 지원 관리 확장 FE 웹 설계 라우팅 · 내비게이션 (탭 구조 5탭 + 랜딩 전환 · 회사 목록 경로 이동의 영향 범위 · 1단계 라우팅 흐름 · 라우트 등록 Single Writer 장치) · 화면 목록방안 2(랜딩 = 지원 대시보드, 사용자 확정) · 컴포넌트 트리 · S-14 로그인의 게이트 배치 규칙 · Single Writer per File 검증 > 단계 1 wave 1.

변경 사항

  • routes.tsx각 단계 wave 1의 골격 티켓만 수정하는 공통 파일입니다. 화면 티켓(FE-23·24·26)이 각자 라우트를 등록하면 같은 wave에서 같은 파일을 세 번 고쳐 머지 충돌이 납니다. 이 티켓이 라우트 자리를 미리 뚫고, 화면 티켓은 자기 페이지 파일만 대체합니다.
  • 랜딩 화면을 전환합니다 (사용자 확정, 2026-08-08). routes.tsx의 index(/)를 지원 대시보드로 바꾸고, 지금 index를 차지하던 회사 목록(CompanyListPage)을 /companies 로 옮깁니다. 앱을 여는 가장 잦은 동기가 “내 지원이 지금 어디까지 왔나”이고, 회사 목록은 등록·소스 관리용이라 매일 열 화면이 아니기 때문입니다.
  • /dashboard 별칭은 두지 않습니다. 한 화면에 URL이 2개면 활성 탭 판정과 뒤로가기가 모호해집니다 — 대시보드의 정규 URL은 / 하나뿐입니다.
  • /login(게이트 ) · /watchlist 2개 라우트를 lazy 스텁으로 신설하고, 기존 11개 라우트를 AuthGate element 아래로 이동하면서 회사 목록만 /companies로 경로를 바꿉니다. AuthGate는 앱 셸 바깥이 아니라 routes.tsx의 최상위 element로 배치합니다 — 셸 안에 두면 미인증 상태에서 탭바·헤더가 먼저 그려집니다.
  • components/auth/AuthGate.tsx통과만 시키는 스텁으로 생성합니다. 세션 조회·리다이렉트 로직은 FE-23이 같은 파일을 구현으로 대체합니다(다른 wave이므로 충돌 없음). 스텁 단계에서 기존 11개 화면이 그대로 동작해야 하므로, 이 티켓의 게이트는 children을 그대로 렌더합니다.
  • pages/auth/LoginPage.tsx·pages/watchlist/WatchlistPage.tsx·pages/dashboard/DashboardPage.tsx를 “준비 중” 문구만 가진 스텁으로 생성합니다(DashboardPage는 라우트가 /입니다). 스텁은 어떤 훅·API·타입도 import하지 않습니다 — FE-20의 산출물을 참조하면 wave 1 내부에 없어야 할 의존이 생깁니다.
  • NavTabs.tsx를 4탭에서 5탭으로 확장하면서 순서도 재정의합니다 — 랜딩인 지원이 1번, 일일 판단 화면인 보관함이 2번, 등록·관리 성격인 회사가 3번입니다(토스는 홈을 첫 자리에 둡니다). 기존 FE 설계의 “5탭 금지” 규칙은 이번 범위에서 개정합니다 — 보관함(교차 회사 공고)은 회사 탭의 하위가 아니라 회사 경계를 가로지르는 독립 진입점이라 기존 탭에 접을 자리가 없기 때문입니다.
#랜딩활성 판정
1지원 (랜딩)/pathname === '/' 정확 일치 · /applications* · /documents* · /resume-profile* · /contact-events*
2보관함 (신규)/watchlist/watchlist*
3회사/companies/companies* · /job-postings* · /aggregator-sources*
4매칭/settings/matching/settings*
5운영/operations/operations*
  • 지원 탭 활성 판정만 startsWith가 아니라 정확 일치입니다. 랜딩이 /라 접두사 판정을 쓰면 모든 경로에서 지원 탭이 켜집니다. 나머지 4탭은 기존처럼 접두사 판정이고, 2·3단계 화면이 늘어도 탭 하이라이트가 꺼지지 않도록 하위 경로를 미리 포함합니다.
  • [소유권 확정 2026-08-09] web/src/test/hardcodedColor.ts는 FE-20 소유입니다 — 이 티켓은 소비만 하고 만들지 않습니다.
    • 리뷰 지적이 FE-20(칩 테스트의 팔레트 이름 오탐)과 이 티켓(다크 모드 토큰 회귀 가드)에 독립적으로 가면서 양쪽이 같은 경로에 주석 한 줄만 다른 동일 파일을 각자 만들어 머지 충돌이 났습니다. 소유자는 FE-20으로 확정됐습니다.
    • 이 티켓은 FE-20 머지 후 main에서 가져다 import합니다. 머지 순서는 BE-45FE-20FE-22(리베이스 후 중복 파일 제거) 입니다.
    • 일반 원칙: 공용 테스트 헬퍼·fixture·가드는 각 단계 wave 1 계약 티켓이 선점 소유하고 후행 wave는 소비만 합니다.
  • 회사 목록 경로 이동의 영향 범위 3파일을 이 티켓이 소유합니다경로 상수 + 그 경로를 가리키는 라벨·안내 문구까지 바꾸고, 그 밖의 본문 로직은 건드리지 않습니다.
    • [정정 2026-08-09] 초안은 “상수 1줄씩만”으로 못박았으나 범위가 부족했습니다. ManualJobPostingPage.tsx:76-78description="홈에서 회사를 다시 선택해 주세요"·actionLabel="홈으로"처럼 경로를 말로 설명하는 문구가 함께 있어, 상수만 /companies로 바꾸면 “홈으로” 버튼이 회사 목록으로 가는 거짓말이 됩니다.
    • 따라서 이 티켓의 범위는 경로 상수 + 그에 딸린 라벨·안내 문구입니다. 판단 기준: “이 문자열이 바뀐 목적지를 잘못 설명하는가” — 그렇다면 함께 고칩니다.
    • 범위를 넘지 않는 선은 그대로입니다 — 레이아웃·상태 관리·API 호출·컴포넌트 구조는 건드리지 않습니다.
파일현재변경
pages/application/ApplicationListPage.tsx:14COMPANY_LIST_PATH = '/''/companies'
pages/posting/ManualJobPostingPage.tsx:10HOME_PATH = '/''/companies'
pages/posting/JobPostingDetailPage.tsx:12HOME_PATH = '/''/companies'
  • JobPostingDetailPage.tsxFE-29(wave 3)도 수정하지만 다른 wave이므로 Single Writer 규칙에 위배되지 않습니다. 같은 wave의 화면 티켓(FE-24·26)은 이 3파일을 건드리지 않습니다.
  • 랜딩 전환·경로 상수 갱신·탭 순서 재정의가 더해져 티켓 사이즈는 S에서 M(~400줄)으로 조정합니다.
  • AppShell.tsx 헤더의 ThemeToggle 옆에 로그아웃 버튼 슬롯을 만듭니다. 버튼 구현체(LogoutButton)는 FE-23이 채웁니다 — 이 티켓은 자리와 레이아웃만 확보해 FE-23이 AppShell.tsx를 다시 열지 않게 합니다.
  • NavTabs.tsx·AppShell.tsx는 2·3단계에서 탭이 늘지 않으므로 이 티켓 이후 수정 0건입니다.

롤백: index를 회사 목록으로 되돌리고(경로 상수 3파일도 '/'로 원복), NavTabs 탭 정의를 4탭으로, AuthGate element를 걷어내면 기존 라우팅으로 즉시 복귀합니다(스텁 페이지는 참조가 사라져 무해).

의존

  • 없음 (단계 1 wave 1, BE 의존 없음). FE-20·FE-21과 수정 파일 교집합이 0건이라 동시 진행합니다.

다이어그램

처리 흐름

sequenceDiagram
    participant User as 사용자
    participant Router as routes.tsx
    participant Gate as AuthGate 스텁
    participant Shell as AppShell + NavTabs
    User->>Router: /watchlist 진입
    Router->>Gate: 게이트 통과 위임
    Gate-->>Router: children 그대로 렌더
    Router->>Shell: 5탭 셸 렌더
    Shell-->>User: 보관함 탭 활성 + 준비 중 스텁

컴포넌트 의존

flowchart LR
    Routes[routes.tsx] --> Login["LoginPage 스텁 (게이트 밖)"]
    Routes --> Gate[AuthGate 스텁]
    Gate --> Shell[AppShell]
    Shell --> Tabs[NavTabs 5탭]
    Shell --> Slot[로그아웃 슬롯]
    Gate --> Dashboard["DashboardPage 스텁 (/ 랜딩)"]
    Gate --> Watchlist[WatchlistPage 스텁]
    Gate --> Companies["CompanyListPage (/companies 이동)"]
    Gate --> Existing[나머지 기존 라우트]
    Companies --> Consts["경로 상수 3파일 갱신"]
    Slot -.->|FE-23이 채움| Logout[LogoutButton]
    Gate -.->|FE-23이 대체| Impl[AuthGate 구현]

테스트 케이스

  • /watchlist로 진입하면 보관함 스텁 화면이 렌더되고 탭바의 보관함 탭이 활성으로 표시된다.
  • /로 진입하면 지원 대시보드 스텁이 렌더되고 회사 목록은 렌더되지 않는다.
  • /companies로 진입하면 기존 회사 목록 화면이 그대로 렌더된다.
  • /에서 지원 탭이 활성으로 표시되고, /applications로 진입해도 같은 지원 탭이 활성으로 유지된다.
  • /companies·/watchlist로 진입하면 지원 탭이 활성이 아니다(정확 일치 판정이라 접두사로 켜지지 않는다).
  • /dashboard는 라우트로 선언되지 않는다(별칭 없음).
  • 지원 목록·수동 공고 등록·공고 상세의 “회사 목록” 이동이 /가 아니라 /companies로 간다.
  • 경로 상수만 바뀐 3개 화면의 기존 렌더·동작이 그대로 유지된다.
  • /login으로 진입하면 앱 셸(탭바·헤더)이 렌더되지 않는다.
  • 기존 11개 라우트가 AuthGate 아래로 이동한 뒤에도 전부 기존과 동일하게 렌더된다.
  • AuthGate 스텁은 세션 조회 요청을 발생시키지 않고 children을 그대로 렌더한다.
  • 스텁 3개 페이지가 어떤 API 요청도 발생시키지 않는다.
  • 탭바에 지원·보관함·회사·매칭·운영 5개 항목이 이 순서대로 렌더되고 각각의 랜딩 경로(/·/watchlist·/companies·/settings/matching·/operations)로 이동한다.
  • AppShell 헤더에 테마 토글과 로그아웃 슬롯이 함께 렌더되고, 슬롯이 비어 있어도 헤더 레이아웃이 깨지지 않는다.
  • 다크 모드로 탭바·헤더를 렌더하면 시맨틱 토큰 class만 사용하고 하드코딩 색이 0건이다 (web/src/test/hardcodedColor.tsexpectNoHardcodedColorimport해서 사용 — 직접 만들지 않는다).