Critical States (빈/에러/로딩/상태 모델 분기)
handoff brief 의 각 surface §6 정합. 모든 화면이 반드시 처리해야 할 상태 카탈로그.
Overview
UI 가 “기본 동작” 외 반드시 그려야 할 상태들. 어떤 디자인이든 빠뜨리면 데이터 강제 ①을 어긴 것.
1. Empty 상태
| 화면 | Empty 케이스 | 처리 |
|---|---|---|
/ 대시보드 | 본사 0건, 장애 0건 (녹색 정상) | 권장 액션 카드 |
/hq 본사 목록 | 본사 0건, 검색 0건 | CTA (신규 본사 등록) + 검색 reset |
/stores 매장 목록 | 매장 0건 | CTA (INDEPENDENT 매장 등록) |
/music 음원 풀 | 음원 0건 (운영 초기) | 업로드 CTA |
/libraries 라이브러리 | 라이브러리 0건 | 복제 / 새로 만들기 CTA |
/tickets 티켓 | 티켓 0건 | ”처리할 티켓 없음” 휴식 메시지 |
/stats 통계 | 데이터 없음 (베타 초기) | “데이터 수집 중” 안내 |
2. Loading 상태
| 화면 | Loading | 처리 |
|---|---|---|
| 모든 목록 | 초기 fetch | Skeleton (3~5 rows) |
| 임퍼소네이션 진입 | exchange token 발급 + 새 탭 + 첫 페이지 로드 | ”진입 중…” overlay + 새 탭 |
| 폼 submit | mutation pending | 버튼 spinner + form disabled |
| refresh | access token 만료 → BFF refresh | invisible (UI 가 끊김 없음) |
3. Error 상태
3-1. 인증 실패
- 401 (
AUTH_INVALID_CREDENTIALS) — 로그인 폼: “이메일 또는 비밀번호가 일치하지 않습니다.” - 401 backend 재인증 실패 (refresh 도 만료) — BFF 가
session.destroy()+/login으로 redirect - 5xx (서버 장애) —
BACKEND_ERROR— session 유지 + Toast “잠시 후 다시 시도” - 네트워크 도달 실패 —
BACKEND_UNREACHABLE— 동일하게 session 유지 + Toast
3-2. 검증 실패
- 400 (
VALIDATION_FAILED) — backend 가 field errors 반환. 폼 inline 표시 (field 별 error 메시지). - 409 (예:
EMAIL_DUPLICATE) — Toast + field highlight.
3-3. 권한 실패
- 403 —
/audit/impersonation등 OPERATOR-only 페이지에 HQ_MANAGER 접근 — 빈 화면 + “권한 없음”.
4. 상태 모델 분기 (데이터 강제 ①)
4-1. HQ status
| status | UI 상태 |
|---|---|
| 활성 (가상) | 정상. 임퍼소네이션 가능 (FRANCHISE만) |
SUSPENDED | 본사 row 회색 표시 + “이용 정지” 배지. 임퍼소네이션 비활성 (사유 표시). |
| INDEPENDENT | 임퍼소네이션 자체 비활성 — confirm dialog 안 뜸, 버튼 disabled + 사유 |
현재 도입:
Hq.statusenum 4종 (ACTIVE/ONBOARDING/UNPAID/SUSPENDED) — BE v0.7.0 (SPEC #013). 가상 본사는ACTIVE고정 + INDEPENDENT 분기로 임퍼소네이션 차단. 정지/복구 전이 + 관리 UI 도입 완료 (SPEC #018, BE v0.11.0) — hq-list 행 [정지]/[복구] + suspend/reactivate endpoint + 세션 무효화·로그인 차단. 자동 전환·사유 저장은 후속.
4-2. Store status (StoreStatus enum)
| status | UI 상태 |
|---|---|
ACTIVE | 정상 |
SUSPENDED | ”이용 정지” 배지. 송출 차단 (후속 SPEC) |
INACTIVE | ”장기 미사용” 배지 (90일 자동 전환, 후속 SPEC) |
4-3. AccountStatus
| status | UI 상태 |
|---|---|
ACTIVE | 정상 |
SUSPENDED | 즉시 세션 무효화 + 다음 로그인 차단 (구현 후속 SPEC) |
WITHDRAWN | 탈퇴. 로그인 차단 + soft-delete |
4-4. PlanType 분기
| Plan | UI 분기 |
|---|---|
AI | AI 라이브러리만 노출 |
TRUST | AI + TRUST 라이브러리 노출 |
4-5. StoreType / HqType 분기
| 분기 | UI |
|---|---|
| FRANCHISE 본사 | 산하 매장 (DIRECT / FRANCHISE) 묶음 표시. 결제는 본사 단위. |
| INDEPENDENT 매장 | 가상 본사 산하. 결제는 매장 단위. UI 에서 본사 정보 hide. |
5. 임퍼소네이션 진입 분기 (① 가장 중요)
| 진입 시도 | 결과 |
|---|---|
| FRANCHISE + non-SUSPENDED (ACTIVE·ONBOARDING·UNPAID) | confirm dialog → 새 탭 + exchange → 본사 모드 + 빨간 배너 |
| FRANCHISE + SUSPENDED | confirm dialog 안 뜸. 버튼 disabled + “이용 정지된 본사” |
| INDEPENDENT | confirm dialog 안 뜸. 버튼 disabled + “개인 매장 가상 본사” |
| exchange token 만료 (60초 초과) | 새 탭에 “토큰 만료, 다시 시도” |
| exchange 중 운영사 세션 만료 | exchange 실패 + 로그인 redirect |
6. 로그인 후 role 라우팅 · 교차 리디렉션 (SPEC #016)
6-1. 로그인 role 분기 (AuthResponse.role)
| role | 결과 |
|---|---|
| OPERATOR | safeNext(next) (기본 /) — ops 셸 |
| HQ_MANAGER | /admin — 본사 모드 셸 (배너 없음, MeResponse.hqName 표시) |
| STORE_MANAGER | redirect 없음 — “지원되지 않는 역할” 안내 + POST /api/auth/logout(세션 destroy). 앱 접근 차단 |
passwordMustChange=true 는 (로그인이 지원되는 OPERATOR·HQ_MANAGER 레이아웃 내에서) role 분기보다 우선 → /onboarding/change-password. STORE_MANAGER 는 위 표대로 로그인 자체가 차단(점장 모드 미구축)되어 이 FE redirect 에 도달하지 않는다. 반면 BE PasswordChangeEnforcementFilter 는 role 무관하게 pmc=true 계정의 allowlist 밖 보호 endpoint 호출을 403 PASSWORD_CHANGE_REQUIRED 로 차단해 직접 API 우회까지 방어한다 (이중, SPEC #022).
6-2. 교차 리디렉션 (레이아웃 가드 — defense-in-depth)
| 진입 | 가드 | 결과 |
|---|---|---|
ops (protected)/* 에 HQ_MANAGER me | (protected)/layout.tsx | redirect("/admin") |
/admin/* 에 OPERATOR lm_session(임퍼소네이션 아님) | app/admin/layout.tsx | redirect("/") |
/admin/* 에 임퍼소네이션 + 실 HQ_MANAGER 세션 공존 | app/admin/layout.tsx | 임퍼소네이션 우선 |
/admin/* 세션 없음 | app/admin/layout.tsx | /login |
순서: 두 layout 모두 passwordMustChange → role 라우팅 순. 변경 완료 후 role home 으로 바운스(루프 없음 — onboarding layout 은 me 재호출 안 함).
References
- handoff
10-surface-ops-backoffice.md§6 Critical states - handoff
11/12-surface-*.md§6 /features/hq/impersonation— 진입 흐름 상세