[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(게이트 밖) ·/watchlist2개 라우트를 lazy 스텁으로 신설하고, 기존 11개 라우트를AuthGateelement 아래로 이동하면서 회사 목록만/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-45→FE-20→FE-22(리베이스 후 중복 파일 제거) 입니다. - 일반 원칙: 공용 테스트 헬퍼·fixture·가드는 각 단계 wave 1 계약 티켓이 선점 소유하고 후행 wave는 소비만 합니다.
- 회사 목록 경로 이동의 영향 범위 3파일을 이 티켓이 소유합니다 — 경로 상수 + 그 경로를 가리키는 라벨·안내 문구까지 바꾸고, 그 밖의 본문 로직은 건드리지 않습니다.
- [정정 2026-08-09] 초안은 “상수 1줄씩만”으로 못박았으나 범위가 부족했습니다.
ManualJobPostingPage.tsx:76-78의description="홈에서 회사를 다시 선택해 주세요"·actionLabel="홈으로"처럼 경로를 말로 설명하는 문구가 함께 있어, 상수만/companies로 바꾸면 “홈으로” 버튼이 회사 목록으로 가는 거짓말이 됩니다. - 따라서 이 티켓의 범위는 경로 상수 + 그에 딸린 라벨·안내 문구입니다. 판단 기준: “이 문자열이 바뀐 목적지를 잘못 설명하는가” — 그렇다면 함께 고칩니다.
- 범위를 넘지 않는 선은 그대로입니다 — 레이아웃·상태 관리·API 호출·컴포넌트 구조는 건드리지 않습니다.
- [정정 2026-08-09] 초안은 “상수 1줄씩만”으로 못박았으나 범위가 부족했습니다.
| 파일 | 현재 | 변경 |
|---|---|---|
pages/application/ApplicationListPage.tsx:14 | COMPANY_LIST_PATH = '/' | '/companies' |
pages/posting/ManualJobPostingPage.tsx:10 | HOME_PATH = '/' | '/companies' |
pages/posting/JobPostingDetailPage.tsx:12 | HOME_PATH = '/' | '/companies' |
JobPostingDetailPage.tsx는 FE-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.ts의expectNoHardcodedColor를 import해서 사용 — 직접 만들지 않는다).