d3ro-voice/docs/phases/phase-5.md
Yun Chan e24bb8378c 초기 프로젝트 설정: 하네스 시스템 + 설계서 + RE 노하우
- CLAUDE.md: 프로젝트 규칙, 기술 스택, 코딩 규칙, 페이즈 로드맵
- .claude/settings.json: 권한, 강제 훅 (매 프롬프트 설계서 규칙 주입)
- .claude/skills/: implement-phase, review-phase, scaffold, test-commit, debug
- .claude/agents/: electron-architect, voice-pipeline-expert, ui-specialist
- docs/design/00-09: 마스터 아키텍처, 서비스 명세(16개), IPC(113채널),
  DB스키마, UI컴포넌트, 검증리포트, 외부엔진연동, 갭분석,
  VoiceMode패턴, 디자인시스템, 히스토리팝업
- docs/phases/1-7+3.5: 전체 구현 페이즈 문서
- docs/re-findings/: Speakly RE 노하우 5개 문서
2026-04-05 01:03:03 +09:00

6.9 KiB

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<History> — nanoid로 ID 생성
    • getById(id: string): Promise<History | null>
    • getList(filter: HistoryFilter): Promise<{ entries: History[]; total: number }> — 페이지네이션
    • update(id: string, data: Partial<History>): Promise<void>
    • softDelete(id: string): Promise<void> — deleted 플래그 설정
    • hardDelete(id: string): Promise<void> — 물리 삭제
    • bulkDelete(ids: string[]): Promise<void>
  • 검색: originalText, polishedText에 대한 LIKE 검색, 날짜 범위, 모드, 상태 필터
  • 통계 집계:
    • getStats(): Promise<HistoryStats> — 총 항목, 총 녹음 시간, 언어별 통계 등
    • 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<Dictionary>
    • update(id: string, data: Partial<Dictionary>): Promise<void>
    • delete(id: string): Promise<void>
    • getAll(filter?: DictionaryFilter): Promise<Dictionary[]>
    • search(query: string): Promise<Dictionary[]> — word 검색
    • incrementUsage(id: string): Promise<void> — 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 네비게이션으로 각 화면 이동 가능