bookquote

화면 설계 — 친구 프로필 /u/:userId (⏳ PR18-C 신규)

신규 그룹 4(소셜). 입력 근거: DECISIONS 2026-05-17 “친구 서재 탐험 V1.0 합류”, db-schema.md §2.5 follows. 친구 서재 탐험의 유일한 풀스크린 — 다른 진입점(Me 친구찾기·책 상세 친구 미니리스트·카드 deep link sender 배너)은 모두 이 화면으로 모임. 본인 프로필 진입은 /me로 redirect — 이 화면은 남의 서재 read-only 만.

1. 목적 / 진입·이탈 / 라우트

2. 와이어프레임

공개 프로필 (is_library_public=true)

┌─────────────────────────────────────────┐
│ ←  지윤                              ⋮   │  AppBar — ⋮ V1.5(신고/차단). V1엔 ⋮ 자체 숨김
├─────────────────────────────────────────┤
│   ┌──┐                                   │  헤더 — 64×64 아바타(없으면 이니셜)
│   │지 │  지윤                            │  display_name headlineSmall primary900
│   └──┘  팔로워 12 · 팔로잉 5             │  카운트 bodySmall primary500 (탭=시트로 리스트)
│         ┌─────────────────┐              │
│         │   + 팔로우      │              │  accent500 FilledButton 36dp. 팔로잉 중이면
│         └─────────────────┘              │  "✓ 팔로잉" OutlinedButton(탭=언팔로우 확인)
├─────────────────────────────────────────┤
│ 지윤님의 서재   [ 책 23 ] [ 인용구 47 ] │  세그먼트(library.md와 같은 톤). 카운트는
├─────────────────────────────────────────┤  잠금 제외(RLS가 거른 N).
│  (책 탭) library.md의 _BookList 그대로  │
│  미드나잇 라이브러리       [3구절]       │  "N구절" 배지는 친구의 공개 인용구 카운트 only
│  ...                                     │
│                                          │
│  (인용구 탭) quote-list.md의 무드 칩    │
│  + 카드 목록. 카드 액션은:               │
│  ┌─────────────────────────────────────┐│  접힘 / 펼침 모두 카드 우상단 액션 X
│  │ "가장 깊은 밤에 가장 빛나는 별이…"   ││  ([공유]·[카드 만들기]·[삭제] 모두 숨김)
│  │  📕 미드나잇 라이브러리 p.132 · 위로 ││  대신 펼침 시 [📕 책 보기 ▸]만(/book/:id로)
│  └─────────────────────────────────────┘│
└─────────────────────────────────────────┘

비공개 프로필 (is_library_public=false) — “잠긴 서재”

┌─────────────────────────────────────────┐
│ ←  지윤                                   │
├─────────────────────────────────────────┤
│   ┌──┐  지윤                              │  헤더 + 팔로우 버튼은 그대로 표시 가능
│   │지 │  팔로워 12 · 팔로잉 5             │  (팔로우는 공개 여부와 무관 — 트위터식)
│   └──┘  ┌─────────────────┐               │
│         │   + 팔로우      │               │
│         └─────────────────┘               │
├─────────────────────────────────────────┤
│                                          │
│             🔒                            │  primary400 큰 아이콘
│                                          │
│        이 서재는 비공개예요               │  headlineSmall primary600
│   지윤님이 공개 설정을 켜면 보여요        │  bodyMedium primary500
│                                          │
└─────────────────────────────────────────┘

본인 진입 시 — 화면 빌드 직전 context.go('/me') redirect. 깜박임 회피 위해 build 시점이 아니라 initState/didChangeDependencies에서 검사.

3. 상태 (PR18-C 신규)

| 상태 | 처리 | 심각도 | |—|—|—| | 로딩: 프로필 | friendProfileProvider(userId) (FutureProvider.autoDispose<Profile>) | 낮음 | | 로딩: 책·인용구 | 각각 friendBooksProvider(userId)·friendQuoteFeedProvider(userId) (notifier · cursor-after). 세그먼트 미선택 탭은 lazy | 낮음 | | 미로그인 | 라우터 가드가 /auth/login?from=/u/:userId로 — 도달 가능성 0 | — | | 비공개 프로필 | profile.is_library_public=false → 헤더 그대로, body는 “잠긴 서재” 빈상태. FollowState에 따라 카피 분기(팔로우 전: “공개 설정을 켜면 보여요” / 팔로잉 중: “팔로우 요청을 보냈어요. 서재가 공개되면 여기서 볼 수 있어요”). | — | | 본인 진입 | 라우터 _redirect에서 auth.uid() == userId 검사 → /me로 redirect (1프레임 흰 화면 회피, 2026-05-18 결정) | — | | 닉네임 미설정/의심 | display_name이 email local-part 패턴(./_ 포함)이거나 비어있으면 본 화면 진입 봉쇄 → _NicknameGateView 풀스크린 노출 (PR18-B/C 게이트) | — | | 공개인데 빈 서재 | books·quotes 둘 다 0 → “아직 공개한 책이 없어요” 빈상태 | 낮음 | | 팔로우 토글 중 | 낙관적 업데이트 — 버튼 즉시 토글, 실패 시 rollback + SnackBar “팔로우에 실패했어요” | 낮음 | | 에러 (네트워크) | _ErrorView userMessage + 다시 시도. raw $error 노출 X (library.md 기준) | 중간 | | 존재하지 않는 userId | profile fetch가 PGRST116(0 row) → “사용자를 찾을 수 없어요” 빈상태 + [홈으로] | 낮음 |

4. 인터랙션

5. 토큰 매핑

6. 재사용 / 신규

7. 엣지 / 접근성 + 보안 점검 (PR18-E 침투 테스트로 회귀 가드)

보안 핵심 (잠금 인용구 노출 사고 = 신뢰 파괴 1순위):

UX 엣지:

접근성:

변경 이력