모바일 사용성 갈아엎기 — 영상을 버리지 않고 '내 영상'으로 쌓는 TubeShad식 플로우
import하면 클립 하나 만들고 영상을 버리던 모바일 플로우를, 영상을 저장해 언제든 다시 열어 전체 자막을 보고 아무 줄이나 쉐도잉하는 구조로 바꿨다. 근본 원인은 '내가 import한 영상' 개념의 부재였다.
불편의 정체
사용자 한마디로 시작됐다: "import하고나서 tubeshed처럼 전체 저장 및 언제든 자막표시가 안되냐."
당시 모바일 플로우는 이랬다: URL import → 문장 목록이 잠깐 뜸 → 문장 하나를 탭하면 클립 1개를 만들고 플레이어로 이동 → 그걸로 끝. 그 영상은 어디에도 안 남았다. "어제 import한 그 TED talk를 다시 열어서 다른 문장을 쉐도잉"이 불가능했다.
근본 원인 — 잘못된 게 화면이 아니라 데이터 모델
다중 에이전트로 모바일+백엔드를 훑어 확인한 진짜 원인: videos 테이블은 전역 캐시(youtube_id UNIQUE, user_id 없음) 라 — "이 유저가 import한 영상"이라는 개념 자체가 존재하지 않았다. 유저별 산출물은 클립뿐이었고, 라이브러리는 영상이 아니라 클립을 나열했다. import → 클립은 일회성 깔때기였고(import.tsx의 setVideo는 휘발성 컴포넌트 상태, 첫 탭이 makeClip → router.replace('/player/...')), 영상은 그 순간 버려졌다.
→ 즉 "영상을 못 본다"는 표면 증상이고, 진짜는 전역 공유 캐시(스케일용)와 per-user 라이브러리(내 것)를 한 테이블이 겸하려다 후자가 통째로 빠진 것.
고친 구조
백엔드 — library 도메인 신설
V20 library_videos조인 테이블 (user_id+video_id, UNIQUE): 전역 영상 캐시 위에 얹는 per-user "저장한 영상" 레이어. 저장은 멱등 + 경합 안전.LibraryVideoController: POST 저장 / GET 내 영상(최신순,clipCount배지) / DELETE 제거.VideoController가 모든 import를 자동으로 라이브러리에 저장 (저장 버튼 누를 필요 없음 — 마찰 0).
모바일
videos.tsx— "내 영상" 목록(썸네일·제목·채널·클립 수), 당겨서 새로고침, 길게 눌러 제거.video/[id].tsx— 키스톤: 플레이어 + 전체 자막 스크롤. 줄을 탭하면 그 지점부터 재생, 재생 중인 줄이 하이라이트(getCurrentTime폴링), 문장/전체 토글, "이 줄 클립" → 클립 만들고 클립 플레이어(루프·녹음·AI)로.import.tsx— onSuccess가 클립 강제 대신/video/[id]로. import-내-문장-선택 제거(그 동작은 영상 상세로 이동, 이제 반복 가능).- 홈에 "내 영상" 카드(메인), 기존 클립 라이브러리는 "클립"으로.
패턴 (재사용 교훈)
전역 자원 캐시와 per-user 라이브러리는 다른 관심사다 — 한 테이블에 겸하지 마라. 공유 캐시는 콘텐츠(youtube_id)로 키를 잡아 스케일을 받치고, "내 것/최근/소유"는 얇은 per-user 조인 테이블에 둔다. 파생 산출물(클립)로 라이브러리를 흉내 내려던 게 UX를 막다른 길로 만든 원인이었다.
상태
Phase 1 코드 완료, 컴파일/타입체크 통과(backend javac, mobile+core tsc 0). 커밋 ed58caf. Phase 2(범위 선택 클립), Phase 3(공유 플레이어 컴포넌트 추출)는 후속.