# Phase 5: TTS + 히스토리/사전 DB ## 목표 TTS 엔진을 연동하여 텍스트를 음성으로 재생하고, 히스토리와 사전 DB를 완전 구현하여 사용자 데이터를 체계적으로 관리한다. Dashboard 통계 UI를 완성한다. ## 태스크 ### 5.1 LocalTTSService 구현 - `src/main/services/LocalTTSService.ts` - Piper-TTS 또는 Kokoro sidecar 프로세스 관리 (spawn/kill) - 상태 머신: `Idle → Loading → Speaking → Idle` (서비스 명세 TTSState 참조) - 싱글톤 + EventEmitter 패턴 - Sidecar 통신 프로토콜: ``` Main Process piper-tts sidecar │── stdin: text ────────►│ │◄── stdout: WAV data ───│ (또는 PCM 스트리밍) ``` - 음성 모델 디렉토리 관리 (`userData/tts-models/`) - TTSVoice 목록 조회: id, name, language, gender, sampleRate, downloaded ### 5.2 TTS 오디오 재생 - Node.js 측에서 PCM/WAV 데이터를 renderer로 전달 - renderer에서 Web Audio API로 재생 - IPC 채널: - `tts:speak` — 텍스트 합성 및 재생 시작 - `tts:stop` — 재생 중지 - `tts:getVoices` — 사용 가능한 음성 목록 - `tts:audioChunk` (main→renderer) — 오디오 청크 스트리밍 - `tts:finished` (main→renderer) — 재생 완료 - TTSOptions: speed (0.5~2.0), format (pcm/wav) ### 5.3 TTS 재생 UI - ResultPopupWindow에 재생/중지 버튼 추가 - 재생 아이콘 (▶) → 클릭 시 TTS 재생, 아이콘 중지(■)로 변경 - 재생 중 상태 표시 (파형 또는 진행 바) - History UI 각 항목에 재생 버튼 추가 - ConfigService의 `tts.enabled` 설정에 따라 버튼 표시/숨김 ### 5.4 HistoryService 완전 구현 - `src/main/services/HistoryService.ts` - better-sqlite3 + drizzle-orm (DB 스키마: `03-db-and-ui.md` 참조) - CRUD 메서드: - `create(input: CreateHistoryInput): Promise` — nanoid로 ID 생성 - `getById(id: string): Promise` - `getList(filter: HistoryFilter): Promise<{ entries: History[]; total: number }>` — 페이지네이션 - `update(id: string, data: Partial): Promise` - `softDelete(id: string): Promise` — deleted 플래그 설정 - `hardDelete(id: string): Promise` — 물리 삭제 - `bulkDelete(ids: string[]): Promise` - 검색: originalText, polishedText에 대한 LIKE 검색, 날짜 범위, 모드, 상태 필터 - 통계 집계: - `getStats(): Promise` — 총 항목, 총 녹음 시간, 언어별 통계 등 - stats 테이블 싱글턴 업데이트 (세션 완료 시 자동 갱신) - 연속 사용 일수(streakDays) 계산 - 보존 정책: - `cleanupOldEntries()`: retentionDays(30일) 초과 + softDelete된 항목 물리 삭제 - 앱 시작 시 + 24시간 주기로 실행 - maxEntries 초과 시 오래된 항목부터 softDelete ### 5.5 DictionaryService 완전 구현 - `src/main/services/DictionaryService.ts` - better-sqlite3 + drizzle-orm (DB 스키마: `03-db-and-ui.md` 참조) - CRUD 메서드: - `add(word: string, pronunciation?: string, category?: string): Promise` - `update(id: string, data: Partial): Promise` - `delete(id: string): Promise` - `getAll(filter?: DictionaryFilter): Promise` - `search(query: string): Promise` — word 검색 - `incrementUsage(id: string): Promise` — usageCount 증가 + lastUsedAt 갱신 - 카테고리 관리: `user`, `auto`, `technical` - STT 연동: Whisper initialPrompt에 사전 단어 목록 주입 - 전사 시작 전 DictionaryService에서 상위 N개 단어 조회 - initialPrompt 형태: `"단어1, 단어2, 단어3"` (Whisper 컨텍스트 힌트) - 사용 횟수 자동 갱신: 전사 결과에 사전 단어가 포함되면 incrementUsage 호출 ### 5.6 History UI (React) - `src/renderer/components/History.tsx` - 목록 표시: - MUI DataGrid 또는 커스텀 리스트 - 원본 텍스트, 다듬어진 텍스트, 모드, 상태, 날짜, 녹음 시간 표시 - 무한 스크롤 또는 페이지네이션 (기본 50개씩) - 검색: 텍스트 검색 입력 + 필터 (날짜 범위, 모드, 언어) - 항목별 액션: - 복사 (원본/다듬은 텍스트) - 재시도 (같은 텍스트를 다시 LLM 처리) - TTS 재생 (Phase 5.3 연동) - 삭제 (softDelete + 확인 다이얼로그) - IPC 채널: - `history:getList` — 목록 조회 - `history:getById` — 상세 조회 - `history:delete` — 삭제 - `history:bulkDelete` — 일괄 삭제 - `history:search` — 검색 - `history:getStats` — 통계 ### 5.7 Dictionary UI (React) - `src/renderer/components/Dictionary.tsx` - 단어 목록: word, pronunciation, category, usageCount 표시 - 추가: 단어 + 발음 힌트 + 카테고리 입력 다이얼로그 - 편집: 인라인 편집 또는 모달 - 삭제: 확인 후 삭제 - 가져오기/내보내기: - JSON 파일로 내보내기 (`[{ word, pronunciation, category }]`) - JSON 파일에서 가져오기 (중복 word+category 시 스킵 또는 덮어쓰기 옵션) - Electron dialog.showOpenDialog / dialog.showSaveDialog 사용 - IPC 채널: - `dictionary:getAll` — 전체 목록 - `dictionary:add` — 추가 - `dictionary:update` — 수정 - `dictionary:delete` — 삭제 - `dictionary:import` — 파일에서 가져오기 - `dictionary:export` — 파일로 내보내기 ### 5.8 Dashboard 통계 UI 완성 - `src/renderer/components/Dashboard.tsx` - 통계 카드: - 총 녹음 시간 (시:분:초 형식) - 총 단어 수 - 총 세션 수 - 연속 사용 일수 (streakDays) - 최근 7일 / 30일 활동 그래프 (간단한 바 차트, MUI 또는 커스텀 SVG) - 최근 세션 목록 (5~10개, History UI로 이동 링크) - 언어별/모드별 사용 비율 (파이 차트 또는 비율 바) - stats 테이블에서 데이터 조회 + history 테이블에서 최근 데이터 집계 ### 5.9 Drawer 네비게이션 업데이트 - Speakly 패턴: MUI Drawer (240px, permanent) - 네비게이션 항목 추가: Dashboard, History, Dictionary - 아이콘 + 텍스트 라벨 - 현재 라우트 하이라이트 ## Speakly RE 참조 - HistoryService: SQLite 스키마 (history 테이블), CRUD 패턴, 검색 쿼리 - DictionaryService: 단어 사전 관리, 카테고리 분류 - RecordStatsService: 싱글턴 통계 테이블, 누적 집계 패턴 - App.js: MUI Drawer 네비게이션, 라우팅 패턴 (useState 기반) - ResultPopup: 결과 표시 + 액션 버튼 패턴 ## 완료 조건 - [ ] TTS로 텍스트 음성 재생 가능 - [ ] 재생/중지 버튼 UI 작동 - [ ] 히스토리 CRUD + 검색 + 페이지네이션 작동 - [ ] 히스토리 보존 정책 (30일 초과 자동 정리) 작동 - [ ] 사전 CRUD + 가져오기/내보내기 작동 - [ ] 사전 단어가 STT initialPrompt에 주입됨 - [ ] Dashboard 통계 카드 및 최근 세션 표시 - [ ] Drawer 네비게이션으로 각 화면 이동 가능