와와 학원 · 설명 AI · 모바일 시스템

학생들 90%가
핸드폰으로 쓴다.
그래서 모바일이 본체.

데스크톱은 강사 전용 + 학생 가끔 쓰는 채널. 학생 일상은 모바일 — 사진 찍어 묻고, 노트에 써 묻고, 통학 길에 복습. 이 문서는 5개 모바일 화면(mobile-home / chat / photo / write / drill)을 시스템으로 묶어 PWA 구현까지 연결한다.

5모바일 화면
7모바일-only 토큰
~12모바일-only 컴포넌트
4필요 Native API
1구현 경로 (PWA)

System · 01

화면 맵 — 5개로 끝

데스크톱은 7화면(채팅·사진·강사 큐·디자인 시스템 등)이지만 모바일은 학생만 5화면. 강사 큐는 모바일에서 안 함.

묻기 홈 mobile-home 진입점 글로 묻기 mobile-write 노트 + 수식 toolbar 사진 채점 mobile-photo 빨간펜 오버레이 오늘 복습 mobile-drill SM-2 카드 대화 mobile-chat 스트림 + sheet 보내기 결과 "모름" → 다시
🏠

묻기 홈 mobile-home.html

진입점. Hero CTA 2개(사진/글) + 오늘 복습 카드 + 이어할 대화 + 강사 코멘트 알림.

글로 묻기 mobile-write.html

노트 종이톤 textarea + 수식 toolbar. 보내기 → 대화로.

📷

사진 채점 mobile-photo.html

가로 스크롤 사진 + OCR 게이트 + 풀-블리드 빨간펜 채점.

💬

대화 mobile-chat.html

스텝 collapse + 인용 sheet + sticky composer. 모든 입력의 결과 화면.

🔥

오늘 복습 mobile-drill.html

4가지 신호(AskAI fail / 시험 오답 / 숙제 감점 / 단원지 X)에서 자동 카드. SM-2 + "모름" → 대화 재진입.

Foundations · 01

모바일-only 토큰

desktop과 공유하는 토큰은 _shared.css. 모바일만의 추가는 7개.

--safe-top
--safe-bottom
env(safe-area-inset-*) wrapping. 노치/홈 인디케이터 침범 방지. viewport-fit=cover 필수.
--tabbar-h: 64px 하단 탭바 높이. main 콘텐츠 padding-bottom에 더해서 사용.
--header-h: 56px sticky 헤더 높이. 콤팩트 — 데스크톱 64px 보다 작음.
--composer-h: 64px sticky composer 높이. main 콘텐츠 하단 여유 + cite-pill 위치 계산용.
--shadow-frame 데스크톱 폰 프리뷰 frame 그림자. 0 24px 60px rgba(0,0,0,0.10)
--rule-line
--margin-line
--paper-textbook
paper textarea / 사진 SVG 도메인 톤 — desktop과 공유 (이미 _shared.css에 추출됨)

Foundations · 02

뷰포트 규약

모든 모바일 화면이 따라야 할 head 메타.

viewport width=device-width, initial-scale=1, viewport-fit=cover — cover 없으면 노치 영역 비어 보임
theme-color iOS Safari 상태바·Android Chrome 주소창 배경색. #fafbfc (canvas) 또는 #f7f5ee (paper, write 화면만)
apple-mobile-web-app-capable PWA 홈화면 추가 시 풀스크린 모드. yes
phone preview frame 데스크톱 검토용 — max-width: 430px; margin: 0 auto; @media (min-width:480px) { border-radius: 36px; box-shadow: var(--shadow-frame); }

Foundations · 03

터치타겟

모바일은 데스크톱과 다르게 모든 인터랙션 ≥ 44×44px 디폴트. _shared.css@media (pointer: coarse)가 자동 적용.

기본 버튼·링크·input 모두 min-height: 44px
작은 시각 + 큰 hit photo-chip의 X 닫기 버튼 — 시각 24px, ::after { inset: -12px }로 hit 44px
탭바 탭 탭 자체가 1/5 너비 × 64px 높이 — Fitts' Law 충족
FAB 56×56px — 표준 Material 사이즈. 터치영역 충분

Atoms

모바일-only 아톰 7종

desktop에 없는 모바일 전용 컴포넌트. desktop과 공유하는 Button·Chip·Badge 등은 design-system.html 참조.

<BottomTab/>
5칸 고정. active 시 상단 indicator. fixed 또는 sticky bottom.
components/mobile/BottomTab.tsx
<FAB icon ariaLabel/>
56×56 floating. mobile-write 음성, 향후 빠른 작성 등.
components/mobile/FAB.tsx
<PhotoChip src onRemove/>
composer dropzone. 64×64 시각, X 버튼 44 hit area.
components/mobile/PhotoChip.tsx
<MathKey symbol onTap/>
mobile-write 수식 toolbar. 44×44 italic serif. selectionStart에 삽입.
components/mobile/MathKey.tsx
🔥 4일 연속
<StreakIndicator days/>
drill 가벼운 게임화. 1자리 → 2자리 색 변화.
components/mobile/StreakIndicator.tsx
<SelfRateButton level/>
drill 4종 (easy/ok/hard/dunno). 색 + 다음 간격 라벨.
components/mobile/SelfRateButton.tsx
AskAI 막힌 단계 4일 전
<SourcePill type when/>
drill 카드 출처 표시. 4종 (askai/exam/homework/unit) 색 매핑.
components/mobile/SourcePill.tsx

Molecules

모바일-only 몰리큘 5종

인용된 자료 3개

📘 확통 III-1 / §2.1 표본과 모집단
📘 확통 II-3 / §1.4 분산의 성질
📝 이루다 약점 메모
<BottomSheet open onClose/>
drag handle + ESC/backdrop tap/swipe-down으로 닫음. body scroll lock 자동.
components/mobile/BottomSheet.tsx
더 물어볼 거 있어?
<StickyComposer onSend onPhotoPick/>
3컬럼 grid (사진버튼·textarea·send). auto-grow 60→120px. safe-area-bottom.
components/mobile/StickyComposer.tsx
여기에 자유롭게 써봐.
한 줄로도 좋고,
풀이 과정 풀어 적어도
좋아.
<PaperTextarea/>
노트 줄 (32px 간격) + 빨간 마진선. mobile-write 전용 — 쓰는 행위 자체가 만족스럽게.
components/mobile/PaperTextarea.tsx
<PhotoStrip photos/>
가로 scroll-snap. 한 사진씩 집중. 옆으로 밀어서 다음.
components/mobile/PhotoStrip.tsx
✦ AskAI 막힌 단계 · 4일 전
표본평균의 분산이 왜 σ²/n 인가요?
<DrillCard data/>
스택 메타포 (::before/::after로 뒤 카드들 비침). source pill + context note + 문제 + reveal 버튼 + answer.
components/mobile/DrillCard.tsx

Patterns

5 화면 와이어프레임

9:16 폰 비율. 클릭하면 실제 mockup으로.

Implementation · 01

PWA 전략 — 별도 앱 X, 기존 student 앱 확장

이미 apps/student가 React + Vite로 모바일 친화적. 추가만 하면 됨.

왜 PWA 추천

apps/student + manifest + service worker + capacitor 옵션

장점: 기존 코드 베이스 재사용 (학생 인증·라우팅·KaTeX·Layout 다 있음). 앱스토어 심사 없음. URL 공유 가능. 학원 도메인 한 곳에서 관리.

모바일 추가:

  • manifest.json — 이름·아이콘·테마컬러·시작URL·standalone 디스플레이
  • sw.js 또는 Vite PWA 플러그인 — 정적 자산 캐싱 + 오프라인 fallback
  • <meta> 추가 — viewport-fit=cover, theme-color, apple-mobile-web-app-capable
  • 홈화면 추가 프롬프트 (a2hs) — 학생에게 한 번만 안내

왜 React Native 아님 대안

앱스토어 심사·iOS 인증서·CodePush·React Native 학습 곡선·디자인 시스템 재구현 — 모두 비용. 학원 학생 100명 단위 사용량에선 ROI 약함. 나중에 사용자 1000+ 가 되거나 카메라 ML 같은 native 강한 기능이 필요해지면 그때 Capacitor wrapper로 점진 이전.

Capacitor 옵션 필요 시

PWA로 시작 → 나중에 카메라·푸시 등 native API가 정말 필요하면 Capacitor로 같은 React 코드를 네이티브 wrapper에 넣음. 코드 재작성 X. 현 단계에선 대부분 PWA Web API로 충분.

Implementation · 02

필요한 Native API 4개

전부 Web API로 가능. 해상도·권한 처리만 잘 하면 PWA로 충분.

getUserMedia (카메라)
mobile-photo
용도: 교과서·노트 사진 촬영. 대안: <input type="file" accept="image/*" capture="environment"> 가 더 단순 (네이티브 카메라 앱 호출).
SpeechRecognition (음성)
mobile-write FAB
용도: 핸드폰으로 말해서 질문. iOS Safari는 미지원 → fallback: 일반 키보드 받아쓰기 권장.
Push Notifications
강사 코멘트 알림
용도: 강사가 코멘트 달면 학생에게 알림. iOS 16.4+ PWA 지원 (단 홈화면 추가 후만).
Service Worker (오프라인)
drill 카드 캐싱
용도: 오늘 카드 8장 미리 캐시 → 통학길 데이터 안 터져도 복습 가능. 자가평가는 큐에 저장 → 온라인 시 동기.

Implementation · 03

오프라인 · 캐싱 전략

학생 통학길 = 오프라인 ↔ 약신호 반복. 핵심 기능 살아있어야.

화면별 오프라인 동작

  • mobile-home: 마지막 fetch한 데이터 캐시 → 오프라인서도 보임 ("연결 안 됨" 배너만)
  • mobile-drill: 오늘 카드 8장 + 정답 + AI cite 미리 prefetch. 자가평가는 IndexedDB 큐에 → 온라인 시 동기. 오프라인 핵심 기능
  • mobile-chat: 새 질문은 큐에 저장 → 온라인 시 send. 기존 대화는 캐시에서
  • mobile-photo: 사진 업로드는 reliable connection 필요 → 오프라인 시 "연결되면 보냄" 큐
  • mobile-write: textarea 내용 localStorage 자동저장. 오프라인 send 큐

Implementation · 04

열린 질문

구현 들어가기 전 답이 필요한 것.

PWA

설치 프롬프트 시점

3번 사용 후? 첫 강사 코멘트 받으면? 학생이 짜증 안 날 시점.

탭바

기존 BottomTabBar.tsx 와 어떻게 통합

이미 있는 컴포넌트에 "묻기" 탭 추가만 할지, 별도 layout으로 갈지.

알림

강사 코멘트 푸시 정책

모든 코멘트? OK 처리만? 빈도 제한? 학생이 끄는 옵션?

오프라인

drill 카드 prefetch 시점

아침 wifi 잡혔을 때 자동? 명시적 다운로드 버튼? 매일 0시 백그라운드 sync?

사진

업로드 압축 정책

학생 폰 카메라 = 4MB+. AI 입력엔 1MB도 충분. 클라이언트 압축 강제.

카메라

input file vs getUserMedia

input file = 단순 + iOS 호환. getUserMedia = UI 통제 가능. MVP는 input file 권장.

키보드

iOS 키보드 올라올 때 sticky composer 처리

visualViewport.height 감지 + composer 위치 조정. 또는 Visual Viewport API.

스크롤

iOS bounce 스크롤 처리

bottom sheet open 시 background scroll 막기. overscroll-behavior: contain + body scroll lock.