bookquote

화면 설계 — 인용구 입력 /quote/new

그룹 1 · Stage 2 최우선. 입력 근거: competitor-screen-analysis-2026-05-11.md §5.1, QA-1 / Dart-1 가상 팀 산출. 관련 결정: DECISIONS 2026-05-11(내장 OCR 안 함 / 경량 아웃박스).


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


2. 레이아웃 와이어프레임

┌─────────────────────────────────────────┐
│ ✕  인용구 추가                912 / 2000 │  AppBar — 닫기 / 글자수 카운터(임계 근처만 색 변화)
├─────────────────────────────────────────┤
│ ┌─────────────────────────────────────┐ │
│ │ 가장 깊은 밤에 가장 빛나는 별이      │ │  인용구 본문 — 멀티라인 TextField
│ │ 보인다.                             │ │  AppFonts.quote(NotoSerifKR), 자동 포커스+키보드
│ │ |                                   │ │  placeholder: "좋아하는 한 줄을 입력하거나,
│ │                                     │ │              아래 '붙여넣기'를 눌러보세요"
│ └─────────────────────────────────────┘ │
│ ┌── 📋 클립보드에 새 텍스트가 있어요 ──┐ │  붙여넣기 감지 배너 — 클립보드에 텍스트 있고
│ │   "그래도 별은 떠 있었다…"  [붙여넣기]│ │  본문이 비었거나 사용자가 안 만진 경우만 노출.
│ └─────────────────────────────────────┘ │  탭 → 본문에 채움 + 배너 사라짐
│                                         │
│ ┌── 📕 미드나잇 라이브러리 ─── 변경 ▸ ─┐ │  책 영역 — book prefill 시 카드, 아니면
│ │  매트 헤이그 · 인플루엔셜             │ │  "+ 책 연결" 버튼. 탭 → showBookSearchSheet
│ └─────────────────────────────────────┘ │
│  페이지 [ 132 ]              무드 ▾      │  페이지 = 숫자 키패드, 선택. 무드 = 칩 펼침
│  〔위로〕〔먹먹〕〔새벽3시〕〔통찰〕〔설렘〕 │  멀티 선택(최대 3), 토글, 색 코딩+텍스트
│                                         │
│ ┌─────────────────────────────────────┐ │
│ │           카드 만들기 →             │ │  Primary CTA — accent500. 본문 비면 비활성
│ └─────────────────────────────────────┘ │
│             저장만 하기                  │  Tertiary — 텍스트 버튼, ink-400
└─────────────────────────────────────────┘

OCR “모드”는 없음 — 별도 카메라 화면 안 만든다. 사진→텍스트는 사용자가 OS에서 처리하고 붙여넣기 배너로 들어온다.


3. 상태

상태 트리거 처리 표시 심각도
로딩: 진입 화면 push 즉시 (<200ms). 스켈레톤 불필요. TextField 자동 포커스 → 키보드 (실패 시 사용자 탭으로 복구) 낮음
로딩: book prefill ?bookId= 로 진입 bookByIdProvider(id) — 책 카드 영역만 미니 스피너, 나머지 입력은 즉시 가능 Inline (영역 한정) 낮음
로딩: 저장 “카드 만들기” / “저장만” 탭 <300ms 목표. 버튼 inline 스피너 + 입력 잠금(화면 비차단). 1s 초과 시 전체 dim 오버레이 Inline → (1s+) Modal-lite 중간
첫 진입 정상 출발점 — empty-state 페이지 아님. placeholder + “+ 책 연결” + 무드 미선택
에러: 네트워크 끊김 (저장) 저장 중 오프라인 실패 아님 → 아웃박스(shared_preferences JSON 리스트)에 영속화 → “오프라인이에요. 연결되면 자동 저장돼요” + 화면 닫힘(또는 “동기화 대기” 상태). 책: 골랐으면 book_id, 아니면 manual_book_text Toast 높음 (데이터 보존 필수)
에러: Supabase 5xx / 알 수 없음 retryable Toast “잠시 후 다시 시도해주세요” + 폼 100% 유지 + [다시 시도] Toast + 폼 유지 높음
에러: 세션 만료 (PGRST301 / JWT) AuthError 입력을 아웃박스에 임시 저장 → Modal “다시 로그인이 필요해요” → 로그인 후 복귀 시 복원 Modal 높음
에러: 본문 빈 채로 저장 ValidationError VAL_REQUIRED “카드 만들기” 버튼 자체를 비활성 (선제 방지) 비활성 버튼 + (강제 시) Inline 중간
에러: 본문 너무 김 (>2000자) ValidationError VAL_TOO_LONG 카운터를 임계 근처(예: 1800+)에서 copper→error 색으로. 2000 초과 시 Inline “인용구는 한 구절만 — 너무 길어요”. 하드 자르기 금지 — 사용자가 다듬게 Inline + 카운터 중간
에러: 책 검색 실패 / 0건 / rate limit / 오프라인 (시트 내부, book-search-sheet.md 참조) 시트에서 처리 + [ISBN 직접 등록]/[직접 등록] 출구. 책 못 골라도 인용구는 저장 가능(BOOK_UNRESOLVED) 시트 내 Inline/Empty 중간
에러: 디스크 가득 (아웃박스 영속화 실패) StorageError STORAGE_DISK_FULL Modal “저장 공간이 부족해요” + “인용구를 클립보드로 복사할까요?”(최후 보존 수단) Modal 높음
오프라인 (진입 시) connectivity_plus 감지 상단 배너 “오프라인 — 저장하면 연결될 때 동기화돼요”. 책 영역의 “+ 책 연결”은 시트가 캐시(findCachedByQuery)만 보여주고 알라딘 검색 비활성 + “책 이름 직접 입력” 옵션 노출 배너 중간
권한 거부 해당 없음 이 화면은 카메라·사진 권한을 요청하지 않는다 (내장 OCR 없음 → 권한 흐름 0). 붙여넣기는 클립보드 — iOS는 붙여넣기 시 OS가 1회 알림 띄울 수 있으나 우리 권한 흐름 아님

4. 인터랙션 명세


5. 디자인 토큰 매핑 (lib/core/theme/tokens.dart)

영역 토큰
화면 배경 AppColors.secondary200 (#FAFAF8 paper base)
AppBar 투명, elevation 0 (AppTheme.appBarTheme) · 타이틀 AppTextStyles ui w600 16 / AppColors.primary900 · ✕ 아이콘 primary500
글자수 카운터 ui xxs(9) primary400 → 임계(1800+) accent500 → 초과 semanticError
인용구 TextField 텍스트 AppFonts.quote (NotoSerifKR w400) 16~17 / primary800 · placeholder primary400 · 컨테이너 secondary100 배경 + primary200 border 1.5 + AppRadius.sm · 포커스 시 border accent500 (AppTheme.inputDecorationTheme) · padding AppSpacing.s4(16)
붙여넣기 배너 배경 secondary300 / 아이콘·텍스트 primary500 / [붙여넣기] = accent500 텍스트 버튼 / AppRadius.sm
책 카드 secondary100 배경 + primary100 border + AppRadius.md · BookCover 위젯(34×50) · 제목 ui w600 12 primary800 / 메타 ui xxs primary400 · “변경 ▸” accent600
페이지 입력 작은 secondary100 박스 + primary200 border, ui 12 primary700, width ~60
무드 칩 (미선택) 배경 secondary300(또는 무드별 연한 톤) / 텍스트 무드별 어두운 톤 / border 1 secondary500 / radius pill / ui xxs(9.5)
무드 칩 (선택) 배경 primary900 / 텍스트 secondary50 / border primary900
Primary CTA accent500 배경 / secondary50 텍스트 ui w600 14 / AppRadius.md / AppShadow.floating / 비활성 시 secondary600 배경·primary400 텍스트
저장만 하기 텍스트 버튼, ui 13 primary500
오프라인 배너 semanticWarningLight 배경 / semanticWarning 텍스트 / 화면 상단 full-width
Toast (SnackBar) AppTheme.snackBarThemeprimary900 배경, action accent400
에러 Inline semanticError 텍스트 ui xs

새 토큰 필요: 무드별 연한 배경/어두운 텍스트 쌍(예: 위로=success 계열, 먹먹=neutral 계열, 새벽3시=info 계열…). tokens.dartmoodColors 맵 추가 — card-editor.md와 공유(카드에도 무드 칩 표시).


6. 재사용 컴포넌트 / 신규

재사용 (코드에 있음)

신규


7. 엣지 케이스 / 접근성

교차 관심사 (공통 8원칙 적용): ① 오프라인=1급(아웃박스) ② 데이터 유실 금지(draft+아웃박스) ③ PII 로그 금지(본문·붙여넣기 내용 미전송) ④ 막다른 골목 금지(책 못 골라도 저장, 시트에 ISBN 직접 등록) ⑤ 시트 왕복 시 입력 보존 ⑥ 에러 표시 일관성 ⑦ 인증 가드 ⑧ 해당 없음(이 화면은 카드 미리보기 없음).

화면 고유 엣지

엣지 심각도 처리
본문에 이모지·전각 따옴표·특수문자 낮음 그대로 저장(UTF-8). 카드 렌더 시 이모지 컬러 글리프 fallback 체인
본문에 줄바꿈 다수 (시 구절) 낮음 보존, 멀티라인. 카드에서도 줄바꿈 유지(card-editor.md 협의)
1단어 인용구 (“사랑”) 낮음 허용 (VAL_TOO_SHORT 안 씀 — 1글자도 valid). 카드는 폰트 자동 확대로 균형
같은 인용구 중복 저장 중간 차단 안 함(같은 문장 두 번 모으기는 정상). 단 직전 저장과 (book_id, text) 동일하면 “방금 같은 인용구를 저장했어요. 또 저장할까요?” 확인 1회
책 없이 저장 중간 허용 (BOOK_UNRESOLVED) — book_id/manual_book_text 둘 다 null도 OK. “책은 나중에 연결하세요” 안내. 책 상세/서재에서 사후 매핑 경로
거대한 텍스트 붙여넣기 (웹 기사 통째) 중간 VAL_TOO_LONG + 카운터. “인용구는 한 구절만 — 너무 길어요”
클립보드에 책귀가 방금 만든 카드 텍스트가 또 있을 때 낮음 배너 정상 노출 — 무해
입력 중 시트 다녀온 뒤 본문 유실? 높음 시트는 모달 → TextEditingController state 유지. 회귀 테스트 대상으로 명문화
페이지에 음수/0/문자/매우 큰 수 낮음 숫자 키패드 + 양의 정수만; 잘못되면 page=null. 막지 않음
무드 4개째 선택 시도 낮음 “최대 3개까지” Toast, 4번째 무시
draft 복원 후 사용자가 본문 다 지움 낮음 빈 본문 = “카드 만들기” 비활성. 폐기 다이얼로그는 그대로 동작

접근성


변경 이력