# Phase 7: 테스트 + 빌드 + 배포 ## 목표 테스트 커버리지를 확보하고, 프로덕션 빌드/패키징 파이프라인을 구축하여 Windows 설치 파일을 생성한다. CI/CD를 설정하여 자동 빌드 및 배포를 가능하게 한다. ## 태스크 ### 7.1 테스트 전략 수립 - 3계층 테스트 피라미드: - **단위 테스트** (vitest): 서비스 로직, 유틸리티, 상태 머신 - **통합 테스트** (vitest): IPC 핸들러, DB 쿼리, 서비스 간 상호작용 - **E2E 테스트** (Playwright + electron): 전체 사용자 시나리오 - 커버리지 목표: 단위 70%+, 통합 50%+, E2E 핵심 플로우 ### 7.2 단위 테스트 — 최우선 대상 - **VoiceModeService 상태 머신**: - 모든 RecognitionState 전이 경로 테스트 - 모든 AudioState 전이 경로 테스트 - 잘못된 전이 시도 시 에러 처리 - 이중 조건 플러시 (sttReady + audioBuffer) 시나리오 - 타이밍 상수 검증 (minAudioDurationMs, doublePressMs) - 세션 취소/타임아웃 시나리오 - **LocalSTTService**: - sidecar 프로토콜 파싱 (JSON stdout) - 이중 조건 플러시 로직 - 모델 로딩 상태 전이 - 에러 전파 (sidecar crash, transcription fail) - **LocalLLMService**: - Ollama API 응답 파싱 - NDJSON 스트리밍 파싱 (불완전 라인 처리 포함) - 가용성 폴링 로직 - 에러 코드별 처리 (연결 실패, 모델 미설치) - **HotkeyService**: - 키코드 매칭 + 수정자 키 조합 - 더블프레스 감지 (300ms 윈도우) - holdMode vs toggleMode 동작 - **TextInsertService**: - 클립보드 백업/복원 흐름 - 삽입 실패 시 클립보드 복원 보장 - **HistoryService / DictionaryService**: - CRUD 메서드 (in-memory SQLite 사용) - 검색/필터 쿼리 - 보존 정책 (cleanup) - 통계 집계 정확성 - **CustomInstructionService**: - CRUD + 프리셋 보호 (삭제 불가) - 순서 변경 - 프롬프트 템플릿 변수 치환 ### 7.3 통합 테스트 - **IPC 핸들러 테스트**: - 각 IPC 채널이 올바른 서비스 메서드를 호출하는지 검증 - 인자 검증 + 에러 응답 형식 - preload bridge ↔ main handler 매핑 정합성 - **DB 쿼리 통합 테스트**: - drizzle-orm 쿼리가 실제 SQLite에서 올바르게 실행되는지 검증 - 마이그레이션 적용 후 스키마 일관성 - 동시 접근 시나리오 (WAL 모드) - **서비스 간 상호작용**: - VoiceModeService → AudioCaptureService → LocalSTTService → LocalLLMService → TextInsertService 파이프라인 - ConfigService 변경 → 서비스 반영 (설정 핫리로드) ### 7.4 E2E 테스트 (Playwright) - `@playwright/test` + `electron` fixture 사용 - 핵심 시나리오: 1. 앱 실행 → 메인 윈도우 표시 2. Dashboard 통계 로드 3. Settings 열기 → 설정 변경 → 저장 4. History 목록 표시 → 검색 → 삭제 5. Dictionary 추가 → 편집 → 삭제 6. 온보딩 플로우 완주 (첫 실행 시뮬레이션) - 외부 의존성 모킹: - Ollama API: MSW(Mock Service Worker) 또는 로컬 HTTP 서버 - faster-whisper sidecar: 모킹된 stdout 응답 - piper-tts sidecar: 모킹된 WAV 출력 ### 7.5 테스트 유틸리티 - `tests/helpers/`: - `createTestDb()` — in-memory SQLite + 스키마 적용 - `mockElectronStore()` — electron-store 모킹 - `mockSidecar()` — sidecar stdout/stdin 모킹 - `createMockAudioBuffer()` — 테스트용 PCM16 버퍼 생성 - `waitForState()` — 상태 머신 전이 대기 유틸 - vitest 설정: - `vitest.config.ts`: 경로 별칭, 환경변수, 타임아웃 - `vitest.workspace.ts`: unit / integration / e2e 워크스페이스 분리 ### 7.6 electron-vite 빌드 설정 - `electron.vite.config.ts`: - main: TypeScript → CJS, externals (better-sqlite3, uiohook-napi, @nut-tree/nut-js) - preload: TypeScript → CJS, contextIsolation 호환 - renderer: React + MUI → 번들 (코드 스플리팅, 트리 셰이킹) - 환경 분리: `MAIN_VITE_*`, `RENDERER_VITE_*` - native 모듈 빌드: electron-rebuild 또는 prebuild-install - 팝업 HTML 복사: recording-tip.html, result-popup.html → output ### 7.7 electron-builder 패키징 - `electron-builder.yml` 설정: ```yaml appId: com.d3ro.voice productName: D3RO Voice directories: output: release win: target: - target: nsis arch: [x64] icon: resources/icon.ico nsis: oneClick: false allowToChangeInstallationDirectory: true createDesktopShortcut: true createStartMenuShortcut: true extraResources: - from: sidecar/ to: sidecar/ filter: ["**/*"] ``` - sidecar 번들링: - faster-whisper Python 환경 (embedded Python 또는 PyInstaller 빌드) - piper-tts 바이너리 + 음성 모델 - Whisper 모델 파일 (선택: 초기 번들 또는 첫 실행 시 다운로드) - native 모듈 리빌드: better-sqlite3, uiohook-napi, @nut-tree/nut-js - asar 설정: native 모듈은 asar 외부 (`asarUnpack`) ### 7.8 자동 업데이트 (electron-updater, 선택) - `electron-updater` 라이브러리 통합 - GitHub Releases를 업데이트 서버로 사용 - 업데이트 체크: 앱 시작 시 + 24시간 주기 - 업데이트 플로우: 1. `autoUpdater.checkForUpdates()` 2. 업데이트 발견 시 알림 (강제 아님) 3. 사용자 승인 시 백그라운드 다운로드 4. 다운로드 완료 → 재시작 안내 - 버전 관리: semver, `package.json` version 필드 ### 7.9 CI/CD (GitHub Actions) - `.github/workflows/ci.yml`: ```yaml on: [push, pull_request] jobs: lint-and-typecheck: - npm run lint - npm run typecheck unit-test: - npm run test:unit integration-test: - npm run test:integration build: - npm run build - 빌드 산출물 아티팩트 업로드 ``` - `.github/workflows/release.yml`: ```yaml on: push: tags: ['v*'] jobs: build-and-release: - npm run build - electron-builder --win --x64 - GitHub Release 생성 + 설치파일 업로드 ``` - Windows 러너 사용 (`runs-on: windows-latest`) - 캐시: node_modules, electron 바이너리 ### 7.10 코드 서명 (선택) - Windows: Authenticode 서명 (EV 코드 서명 인증서) - electron-builder 설정: ```yaml win: sign: ./scripts/sign.js signingHashAlgorithms: [sha256] ``` - CI에서 서명: GitHub Secrets에 인증서 저장 - 서명 없이도 실행 가능 (SmartScreen 경고 표시) ## Speakly RE 참조 - Speakly 패키징: electron-builder, NSIS 인스톨러, Squirrel.Windows 기반 업데이트 - Speakly 빌드: NativeHelper.dll → extraResources, asar 외부 배치 - Speakly CI: 자동 빌드/배포 파이프라인 (D3RO는 GitHub Actions로 대체) ## 완료 조건 - [ ] `npm run test:unit` — VoiceModeService, HistoryService 등 핵심 서비스 테스트 통과 - [ ] `npm run test:integration` — IPC 핸들러, DB 쿼리 테스트 통과 - [ ] `npm run test:e2e` — 핵심 사용자 시나리오 테스트 통과 - [ ] `npm run build` — 프로덕션 빌드 성공 - [ ] electron-builder로 Windows NSIS 인스톨러 생성 - [ ] 인스톨러로 설치 → 실행 → 기본 기능 작동 확인 - [ ] GitHub Actions CI 파이프라인 작동 (lint, typecheck, test, build) - [ ] (선택) 자동 업데이트 체크 작동