Screen: Settings (더보기)
정보 링크 + 계정 관리 화면. App 전용 (Web 없음 — 웹은 비로그인 피드라 계정/탈퇴 개념 없음). bottom nav "더보기" 탭 진입. AppBar title="더보기".
- Flutter (App):
mobile/lib/features/settings/presentation/pages/settings_page.dart - 상태:
auth_controller.dart(로그인 여부 → 계정 섹션 분기)
💡 시각 미리보기: design site / settings-app
플랫폼별 차이
| 항목 | App (Flutter) | Web (Next.js) |
|---|---|---|
| 화면 존재 | ✅ | ❌ (없음 — 비로그인 피드) |
| 인증 | 비로그인 진입 가능 (lazy auth — GoRouter.redirect 는 인증 기준 분기 없음, 첫 실행 온보딩 게이트 redirect 만 존재). 계정 섹션이 로그인 여부로 분기 (비로그인=로그인 메뉴, 로그인=로그아웃+회원탈퇴) | — |
App — 화면 구성
ListView 한 줄로, 메뉴를 카드 묶음(_MenuGroup — surface 배경 + border + radius.lg, 행 사이 _Divider)으로 그룹화. 각 행은 _MenuItem (선두 아이콘 + 라벨 + 우측 caret).
| 영역 | 컴포넌트 | 조건 |
|---|---|---|
| AppBar | (시스템, title="더보기") | 항상 |
| "구독" 그룹 | _MenuGroup 2행 — ① "구독"(우측 상태) → 페이월 하단 시트(source: settings) ② "구매 복원"(액션, caret 없음) | 항상 (비로그인도 노출 — 탭 시 auth gate) |
| "정보" 섹션 | _SectionLabel + _MenuGroup (고객지원 / 이용약관 / 개인정보처리방침) | 항상 |
| "계정" 섹션 | _SectionLabel + _MenuGroup | 항상 (내용은 로그인 여부 분기) |
| 위험 그룹 | _MenuGroup (회원 탈퇴, danger) | 로그인 상태에서만 |
| 버전 푸터 | "공시한입 v{version}" (textSecondary, 중앙) | 항상 (하단) |
App — "구독" 그룹 (항상)
최상단 _MenuGroup 2행, 아이콘 없음:
1행 "구독" — 우측 상태 텍스트(caret):
- 비구독: "PRO · MAX" (
textSecondary— 티어 존재 힌트) - 구독 중: "PRO 이용 중" / "MAX 이용 중" (
brandPrimary)
탭 → 페이월 하단 시트 (source: settings). 비로그인이어도 노출, 페이월 CTA 탭 시점에 requireAuth(AuthGateAction.subscribe). 구독 상태는 activeTier(서버 — domain-subscription).
2행 "구매 복원" — 액션(caret 없음). 탭 → 스토어 복원 → POST /api/v1/subscription/verify 재호출로 엔타이틀먼트 재동기화. Apple 심사 필수 메커니즘을 여기 배치(페이월 아님 — 사용 빈도 낮고, verify-on-launch 가 평소 자동 동기화). 비로그인 시 auth gate 먼저. 결과 토스트: 활성 복원 시 "구독이 복원되었어요", 없으면 "복원할 구독이 없어요".
App — "정보" 섹션 (항상)
OS in-app browser 시트(showInAppBrowserSheet)로 외부 URL 을 연다. 로그인 여부 무관.
| 라벨 | 아이콘 (phosphor) | URL |
|---|---|---|
| 고객지원 | envelope | https://dartbrief.com/support |
| 이용약관 | fileText | https://dartbrief.com/terms |
| 개인정보처리방침 | shieldCheck | https://dartbrief.com/privacy |
App — "계정" 섹션 (로그인 여부 분기)
authControllerProvider.isLoggedIn 으로 분기.
- 로그인 상태 — 메뉴 그룹 2개:
_MenuGroup1: 로그아웃 (signOut아이콘) → 확인 dialog 후authController.logout()_MenuGroup2 (별도 그룹, 시각적 분리): 회원 탈퇴 (userMinus, danger =error색 + caret 없음) → 확인 dialog 후 탈퇴 API
- 비로그인 상태 —
_MenuGroup1개:- 로그인 (
signIn아이콘) →requireAuth(..., AuthGateAction.signIn, () async {})로 인증 게이트만 띄움(후속 액션 없음). 로그인 성공 시AuthController의 invalidate-on-login 이 컨트롤러 재fetch → 계정 섹션이 로그아웃/탈퇴 메뉴로 자동 전환
- 로그인 (
App — 로그아웃
확인 dialog(showAppModal, 취소=secondary / 로그아웃=danger) → authController.logout().
App — 회원 탈퇴 (danger)
확인 dialog(showAppModal, 경고 카피: "탈퇴하면 관심 종목, 알림 설정 등 모든 데이터가 삭제되며 복구할 수 없습니다." / 취소=secondary / 탈퇴=danger) → DELETE /api/v1/auth/withdraw (헤더 X-Confirm-Withdraw: yes) → 성공 시 logout().
- 진행 중 라벨이 "회원 탈퇴" → "탈퇴 중..." 으로 바뀌고 탭 disabled (
_deleting) - 실패 시 토스트 "회원 탈퇴에 실패했습니다. 잠시 후 다시 시도해주세요." (
ToastType.error)
App — 상태 분기
- S1. 로그인됨 — 구독 그룹[구독·구매 복원] + 정보 그룹 + 계정[로그아웃] 그룹 + 위험[회원 탈퇴] 그룹 + 버전
- S2. 비로그인 — 구독 그룹[구독·구매 복원] + 정보 그룹 + 계정[로그인] 그룹 + 버전 (위험 그룹 없음)
App — 액션 (API)
| 액션 | endpoint | method | request | response |
|---|---|---|---|---|
| 회원 탈퇴 | /api/v1/auth/withdraw | DELETE | 헤더 X-Confirm-Withdraw: yes | 200 / 204 |
로그아웃 / 로그인은 API 직접 호출이 아니라
AuthController경유 (로그인은requireAuth게이트 → 소셜 로그인 플로우). 외부 링크는 API 가 아니라 in-app browser.
Edge cases
- 탈퇴 API 실패: 토스트 +
_deleting해제 (화면 유지, 로그인 상태 그대로) - 탈퇴/로그아웃 dialog 취소: 아무 동작 없음
- 로그인 도중 화면 이탈:
requireAuth게이트는 모달이라 dismiss 시 비로그인 유지
관련 docs
- flow-subscription.md — 구매 복원/구매 시퀀스 (이 화면의 "구매 복원" 동작)
- screen-paywall.md — "구독" 진입 → 구독 결제 시트 (구매 복원은 여기 설정에 위치)
- screen-notifications.md — 동일 bottom nav 형제 화면
- social-login — 로그인 게이트의 소셜 버튼
- modal — 로그아웃/회원탈퇴 확인 dialog (
showAppModal)