# 06. 갭 분석 리포트 > 작성일: 2026-04-04 > 분석 대상: 00-master-architecture.md, 01-service-specifications.md, 02-ipc-and-types.md, 03-db-and-ui.md, CLAUDE.md --- ## 1. 설계서 간 불일치/충돌 ### 1.1 서비스 이름 불일치 | 항목 | 00 (마스터 아키텍처) | 01 (서비스 명세) | 02 (IPC 채널) | 수정 제안 | |------|---------------------|-----------------|--------------|----------| | TTS 서비스 | `TTSService` | `LocalTTSService` (섹션 3) | `LocalTTSService` (tts:* 담당) | **`LocalTTSService`로 통일** — 00에서 수정 | | LLM 서비스 | `LLMService` | `LocalLLMService` (섹션 4) | `OllamaService` (llm:* 담당) | **`LocalLLMService`로 통일** — 00, 02에서 수정 | | STT 서비스 | `LocalSTTService` | `LocalSTTService` | `LocalSTTService` | 일치 (OK) | ### 1.2 서비스 개수 불일치 - **00**: 15개 서비스 (명시적 목록: 표 2.1) - **01**: 10개 서비스만 상세 명세 (AudioCapture, LocalSTT, LocalTTS, LocalLLM, VoiceMode, TextInsert, Hotkey, Config, History, WindowManager) - **빠진 서비스 명세**: I18nService, LoggerService, DictionaryService, SoundEffectService, CustomInstructionService, AutoLaunchService (6개) - **01에만 있는 서비스**: WindowManagerService (00의 WindowManager를 서비스로 격상) **수정 제안**: 01에 빠진 6개 서비스의 상세 명세를 추가하거나, 별도 문서로 분리 ### 1.3 02의 IPC 채널에서만 등장하는 서비스/네임스페이스 | 02 IPC 채널 | 담당 서비스 명 | 00/01 존재 여부 | 비고 | |-------------|--------------|----------------|------| | `system:*` | SystemService, PermissionService | 00/01에 없음 | 00에서 제거된 PermissionService가 02에 부활 | | `stats:*` | StatsService | 00/01에 없음 | 03의 stats 테이블과 관련되지만 서비스 명세 없음 | | `dictionary:*` | DictionaryService | 00에 있지만 01에 명세 없음 | | **수정 제안**: - `system:*` 채널의 담당 서비스를 명확히 정의 (신규 SystemService? 또는 기존 서비스에 분배?) - StatsService를 00의 서비스 목록에 추가하거나 HistoryService에 통합 명시 - 01에 DictionaryService 명세 추가 ### 1.4 IPC 네임스페이스 규칙 불일치 **00의 규칙**: `${feature}:${action}` (예: `hotkey:setDictation`, `history:getRecent`, `app:getVersion`) **02의 실제 채널**: `hotkey:setDictationShortcut`, `history:getAll`, `system:getVersion` | 00 예시 | 02 실제 | 차이 | |---------|---------|------| | `hotkey:setDictation` | `hotkey:setDictationShortcut` | action 이름 불일치 | | `history:getRecent` | `history:getAll` | 채널 자체가 다름 (getRecent 없음) | | `app:getVersion` | `system:getVersion` | 네임스페이스 다름 (`app:` vs `system:`) | | `dictionary:addWord` | `dictionary:add` | action 이름 불일치 | | `instruction:*` (00에 명시) | 02에 없음 | CustomInstruction 채널 전체 누락 | **수정 제안**: 00의 5.1절 예시를 02의 실제 채널명과 일치하도록 갱신 ### 1.5 이벤트 타입 이름/구조 불일치 (00/01 vs 02) | 항목 | 00/01 (서비스 이벤트) | 02 (IPC 타입) | 불일치 | |------|----------------------|---------------|--------| | 에러 타입 | `ServiceError` (01 공통타입) | `ErrorCode` enum (00 §6.2) | 01의 ServiceError는 문자열 코드, 00의 ErrorCode는 숫자 enum | | AudioDevice | `{ id, name, isDefault }` (01) | `{ deviceId, label, isDefault }` (02) | 필드명 불일치: `id`/`deviceId`, `name`/`label` | | STTModel | `{ id, name, size, language, downloaded }` (01) | `{ id, name, sizeBytes, downloaded, languages, accuracy, speed }` (02) | 02가 더 풍부. `language` (단일) vs `languages` (배열) 차이 | | RecognitionState | `Idle/Preparing/Ready/Recognizing/Processing/Completing/Completed/Cancelled/Error` (01) | `IDLE/PREPARING/READY/RECOGNIZING/COMPLETING/COMPLETED/CANCELLED/ERROR` (02) | 01에 `Processing` 있음, 02에 없음. 케이싱도 다름 (PascalCase vs UPPER_CASE) | | VoiceMode | `Dictation/HandsFree` (01, const enum) | `'dictation' / 'hands-free'` (02, string literal) | 타입 표현 방식 차이 | **수정 제안**: 02의 `shared/types.ts`를 정본(source of truth)으로 하고, 01의 타입을 02에 맞춰 갱신 ### 1.6 DB 스키마 vs HistoryService/IPC 필드 불일치 | 03 DB history 컬럼 | 02 HistoryEntry 타입 | 불일치 | |-------------------|---------------------|--------| | `original_text` | `originalText` | OK (camelCase 변환) | | `polished_text` | `processedText` | **이름 불일치**: `polished` vs `processed` | | `mode` (dictation/translate/command) | `llmAction` (refine/translate/...) | **의미 불일치**: DB의 mode는 녹음 모드, IPC의 llmAction은 LLM 처리 유형 | | `focused_app`, `focused_app_name`, `focused_app_window_title` | `targetApp` (단일 필드) | **세분화 불일치**: DB는 3개 필드, IPC는 1개 | | `duration` (초, REAL) | `durationMs` (ms) | **단위 불일치**: 초 vs 밀리초 | | `stt_model`, `llm_model`, `stt_latency_ms`, `llm_latency_ms` | 해당 필드 없음 | **02에 누락** | | `error_code`, `status`, `audio_local_path`, `detected_language`, `mic_device`, `app_version` | 해당 필드 없음 | **02에 누락** | | 해당 컬럼 없음 | `sessionId` | **03에 누락** (id가 sessionId와 동일하다는 주석이 있으나 별도 필드는 없음) | **수정 제안**: 02의 HistoryEntry 타입을 03의 DB 스키마와 1:1 매핑되도록 확장 ### 1.7 Dictionary 스키마 vs IPC 타입 불일치 | 03 DB dictionary 컬럼 | 02 DictionaryEntry 타입 | 불일치 | |----------------------|------------------------|--------| | `word`, `pronunciation`, `category`, `usage_count` | `from`, `to`, `caseSensitive`, `enabled`, `useCount` | **완전히 다른 구조**: DB는 "단어+발음" 패턴, IPC는 "교정(from→to)" 패턴 | **수정 제안**: 사전의 목적을 명확히 한 후 어느 한쪽으로 통일. "커스텀 단어 사전"(03)과 "자동 교정 사전"(02)은 다른 기능이므로, 두 테이블이 필요할 수 있음 ### 1.8 IPC 방향 불일치 | 채널 | 00 §5.2 예시 | 02 채널 테이블 | 불일치 | |------|-------------|---------------|--------| | `voice:startRecording` | `on` (fire-and-forget) | `handle` (요청→응답) | 00은 fire-and-forget, 02는 양방향 | | `voice:stopRecording` | `on` (fire-and-forget) | `handle` (요청→응답) | 동일 불일치 | **수정 제안**: 02의 handle 방식이 더 적절 (sessionId 반환 필요). 00 §5.2를 갱신 --- ## 2. 빠진 설계 영역 ### 우선순위 High | # | 영역 | 현황 | 필요한 내용 | |---|------|------|------------| | 1 | **빌드 설정** | CLAUDE.md에 `electron-vite` 언급만 있고, `electron.vite.config.ts`의 구체적 설정 없음 | electron-vite 설정 (main/preload/renderer 엔트리, external 모듈, 네이티브 모듈 처리), tsconfig.json (paths, target, module), vite.config.ts (proxy, define, alias) | | 2 | **패키징 설정** | 00에 `electron-builder.yml` 파일 트리에 없음 | electron-builder.yml (app ID, productName, files, nsis/msi 설정, extraResources: Whisper/Piper 바이너리 번들링, afterSign hook) | | 3 | **프로젝트 초기화** | CLAUDE.md에 `npm run dev` 등 명령어만 있음 | package.json 의존성 전체 목록, postinstall (네이티브 모듈 리빌드), scripts 정의, electron-rebuild 설정 | | 4 | **보안 (preload 안전성, IPC 검증)** | 00에 contextIsolation:true 언급만 있음 | preload에서 expose할 채널 화이트리스트, IPC 입력값 검증 (zod schema), webPreferences 전체 설정 (sandbox, webSecurity), CSP 헤더 | ### 우선순위 Medium | # | 영역 | 현황 | 필요한 내용 | |---|------|------|------------| | 5 | **테스트 전략** | CLAUDE.md에 `vitest` 언급만 있음 | 테스트 피라미드 (단위/통합/E2E 비율), 서비스별 테스트 파일 매핑, mock 전략 (Electron IPC, better-sqlite3, child_process), E2E 프레임워크 선택 (Playwright? Spectron?), CI 파이프라인 | | 6 | **로깅 전략** | 00에 `LoggerService = electron-log 래퍼` 정도만 있음 | 로그 레벨 정책 (info/warn/error 기준), 로그 파일 위치/로테이션/최대 크기, 카테고리별 로거 (audio, stt, llm, ipc), 민감 정보 마스킹, 디버그 모드 활성화 방법 | | 7 | **성능 기준** | 설계서에 타이밍 상수는 있으나 성능 목표 없음 | STT 지연 목표 (base 모델 기준 < Xms), LLM 응답 시간 목표, UI 응답 시간 (FID < 100ms), 메모리 사용량 상한, CPU 사용량 기준 | | 8 | **Sidecar 바이너리 관리** | STT/TTS sidecar 언급은 있으나 바이너리 배포 방식 미정의 | faster-whisper Python 환경 번들링 (PyInstaller? embedded Python?), Piper 바이너리 배포 방식, 모델 파일 저장 경로 (userData), 버전 관리, 자동 업데이트 | ### 우선순위 Low | # | 영역 | 현황 | 필요한 내용 | |---|------|------|------------| | 9 | **접근성** | 설계서에 언급 없음 | 키보드 네비게이션 (Tab order, focus trap), aria-label/role, 스크린 리더 호환성, 고대비 모드, 폰트 크기 조정 | | 10 | **에러 복구 전략** | 에러 코드 정의는 있으나 복구 흐름 미정의 | 사이드카 크래시 시 자동 재시작 정책, 네트워크(Ollama) 끊김 시 재연결 로직, DB 손상 시 복구 절차 | | 11 | **마이그레이션 전략** | 03에 drizzle-orm migrate 호출만 있음 | 마이그레이션 파일 생성/관리 방법, 스키마 버전 관리, 하위 호환성 정책 | | 12 | **국제화(i18n) 상세** | 00에 I18nService 서비스 목록만 있음 | 번역 키 관리 방식 (JSON? ts?), 번역 파일 구조, fallback 언어, 날짜/숫자 포맷 | --- ## 3. 실현 가능성 리스크 ### 3.1 faster-whisper sidecar의 실시간성 | 항목 | 내용 | |------|------| | **리스크** | 01의 설계는 "오디오 버퍼 전체를 한번에 전사"하는 배치 방식. 실시간 스트리밍 전사 불가. | | **영향도** | **High** — 사용자가 긴 문장을 말할 때 녹음 종료 후 전사 지연이 체감됨 | | **현재 설계** | stdin으로 base64 인코딩된 오디오를 보내고 stdout으로 결과 수신 (01 §2 Sidecar 통신 프로토콜) | | **문제점** | (1) base64 인코딩 오버헤드 (~33% 크기 증가), (2) 전체 오디오를 버퍼링 후 전송하므로 first-token latency가 높음, (3) faster-whisper 자체가 파일/버퍼 단위 처리 (진정한 스트리밍 미지원) | | **대안** | (A) WAV 파일 임시 저장 후 파일 경로 전달 (base64 오버헤드 제거), (B) whisper.cpp의 stream 모드 사용 (진정한 실시간), (C) 청크 분할 전사 + 결합 (VAD 기반 세그먼트 단위), (D) faster-whisper의 `--live` 모드 활용 (커뮤니티 fork) | | **권장** | 초기에는 WAV 파일 전달 방식(A)으로 구현. 지연이 문제 시 whisper.cpp stream(B)으로 전환 | ### 3.2 uiohook-napi Windows 호환성 | 항목 | 내용 | |------|------| | **리스크** | uiohook-napi v1.x는 Electron 33+에서 context-aware 네이티브 모듈로 빌드 필요 | | **영향도** | **Medium** — 빌드 실패 시 핫키 기능 전체 불가 | | **문제점** | (1) N-API 버전 호환성 확인 필요, (2) electron-rebuild로 리빌드 시 빌드 도구(MSVC, Python 3) 필요, (3) Windows Defender가 키보드 후킹을 위협으로 감지할 수 있음 | | **대안** | (A) Electron의 globalShortcut API (제한적이지만 네이티브 모듈 불필요), (B) iohook (더 오래된 포크, 유지보수 우려), (C) PowerShell 스크립트로 키 후킹 (복잡도 높음) | | **권장** | uiohook-napi를 우선 시도하되, `electron-rebuild` 설정을 빌드 문서에 명시. globalShortcut은 hold-to-talk 미지원이므로 fallback으로 부적합 | ### 3.3 @nut-tree/nut-js 관리자 권한 이슈 | 항목 | 내용 | |------|------| | **리스크** | Windows에서 키보드 시뮬레이션(Ctrl+V)에 관리자 권한이 필요할 수 있음 | | **영향도** | **Medium** — UAC 프롬프트 없이 텍스트 삽입 불가 시 핵심 기능 차질 | | **문제점** | (1) 일부 앱(관리자 권한으로 실행된 앱)에 키 입력 불가, (2) nut-js v3은 prebuild 바이너리 제공하지만 Electron과의 호환성 미확인, (3) Windows UAC 설정에 따라 동작 불일치 | | **대안** | (A) Electron의 `clipboard.writeText()` + `robot.js`로 Ctrl+V 시뮬레이션, (B) PowerShell `SendKeys`, (C) Windows Input Simulator (C++ addon), (D) `node-key-sender` | | **권장** | nut-js로 우선 구현. 관리자 권한 앱 대상 실패 시 `app.setAsDefaultProtocolClient`나 매니페스트에 `uiAccess: true` 설정 검토 | ### 3.4 better-sqlite3 + Electron 네이티브 모듈 빌드 | 항목 | 내용 | |------|------| | **리스크** | better-sqlite3는 C++ 네이티브 모듈로 Electron 버전과 Node.js ABI 불일치 시 빌드 실패 | | **영향도** | **High** — DB 초기화 실패 시 앱 전체 불가 (critical step) | | **문제점** | (1) electron-rebuild가 필요하지만 MSVC 빌드 도구 필수, (2) Electron 33의 Node.js 버전과 better-sqlite3 prebuild 버전 일치 여부 불확실, (3) asar 패키징 시 .node 파일 제외 필요 | | **대안** | (A) `@neondatabase/sql.js` (WASM 기반, 빌드 불필요하지만 성능 저하), (B) `sql.js` (WASM), (C) `drizzle-orm/libsql` (libsql WASM 바인딩) | | **권장** | better-sqlite3 유지. `electron-builder` extraFiles에 .node 파일 포함, `postinstall`에 `electron-rebuild` 스크립트 추가. package.json에 `"build": { "asarUnpack": ["**/better-sqlite3/**"] }` 설정 | ### 3.5 electron-vite vs vite-plugin-electron | 항목 | 내용 | |------|------| | **리스크** | 선택 근거가 문서화되지 않음 | | **영향도** | **Low** — 둘 다 성숙한 도구이며 전환 비용은 초기에 낮음 | | **비교** | electron-vite: 공식 Electron 지원 느낌, main/preload/renderer 분리 빌드 기본 지원, 네이티브 모듈 external 자동 처리. vite-plugin-electron: 더 가벼움, Vite 생태계 플러그인, 커스터마이징 유연. | | **권장** | CLAUDE.md에 `electron-vite` 명시됨. 선택 근거를 설계 문서에 한 줄 추가: "electron-vite 채택 — main/preload/renderer 3-entry 빌드 기본 지원, 네이티브 모듈 external 자동 처리" | --- ## 4. 보강 권장사항 (액션 아이템) ### 4.1 설계서 수정 (기존 문서) | # | 대상 문서 | 액션 | 우선순위 | |---|----------|------|---------| | A1 | 00-master-architecture.md | 서비스명 통일: `TTSService` → `LocalTTSService`, `LLMService` → `LocalLLMService` | High | | A2 | 00-master-architecture.md | §5.1 IPC 예시를 02의 실제 채널명과 동기화 | Medium | | A3 | 00-master-architecture.md | §5.2 `voice:startRecording`/`stopRecording`의 방향을 `handle`로 수정 | Medium | | A4 | 01-service-specifications.md | 누락된 6개 서비스 명세 추가 (I18n, Logger, Dictionary, SoundEffect, CustomInstruction, AutoLaunch) | High | | A5 | 01-service-specifications.md | 01의 타입 정의를 02와 통일 (AudioDevice, STTModel, RecognitionState 등) | High | | A6 | 02-ipc-and-types.md | `instruction:*` (CustomInstruction) IPC 채널 추가 | Medium | | A7 | 02-ipc-and-types.md | HistoryEntry 타입을 03 DB 스키마와 매핑되도록 확장 | High | | A8 | 02-ipc-and-types.md | DictionaryEntry 타입과 03 DB dictionary 스키마 불일치 해결 | High | | A9 | 02-ipc-and-types.md | SystemService, StatsService를 00 서비스 목록에 반영하거나, 기존 서비스에 역할 배분 | Medium | | A10 | 03-db-and-ui.md | `polished_text` → `processed_text`로 변경 (또는 02 타입을 `polishedText`로 변경) | Medium | | A11 | 03-db-and-ui.md | `duration` 단위를 ms (INTEGER)로 통일 (02의 durationMs와 일치) | Medium | ### 4.2 신규 설계 문서 작성 | # | 문서명 | 내용 | 우선순위 | |---|--------|------|---------| | B1 | `04-build-and-packaging.md` | electron-vite 설정, tsconfig, electron-builder.yml, 네이티브 모듈 빌드 설정, extraResources (sidecar 바이너리), scripts 정의, CI/CD 파이프라인 | High | | B2 | `05-testing-strategy.md` | 테스트 프레임워크 (vitest), 서비스별 테스트 파일, mock 전략, E2E 전략, 커버리지 목표 | Medium | | B3 | `07-sidecar-management.md` | faster-whisper/Piper 바이너리 배포, Python 환경 관리, 모델 저장 경로, 버전 관리, 헬스체크 상세, 크래시 복구 | High | | B4 | `08-security-checklist.md` | preload 화이트리스트, IPC 입력 검증 (zod), webPreferences, CSP, sandbox 설정, 외부 URL 열기 제한 | Medium | ### 4.3 CLAUDE.md 보강 | # | 액션 | 우선순위 | |---|------|---------| | C1 | electron-vite 선택 근거 한 줄 추가 | Low | | C2 | 네이티브 모듈 리빌드 명령어 (`npx electron-rebuild`) 추가 | Medium | | C3 | 환경 요구사항 추가: Node.js 버전, Python 버전 (faster-whisper용), MSVC Build Tools | Medium | --- ## 5. 요약 매트릭스 | 카테고리 | High | Medium | Low | 합계 | |---------|------|--------|-----|------| | 설계서 간 불일치 | 4 | 5 | 0 | 9 | | 빠진 설계 영역 | 4 | 4 | 4 | 12 | | 실현 가능성 리스크 | 2 | 2 | 1 | 5 | | 보강 액션 아이템 | 7 | 9 | 2 | 18 |