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 |