- docs/REFACTOR_WAVE3_REPORT.md and the Wave 3 policy: canonical map, production changes, verification and remaining external steps. - Gap backlog: GAP-BILL-01 resolved; new GAP-BILL-02 (Payple renewal never ran, Payple client key never set), GAP-WEB-01 (tunnel host for /app), GAP-OPS-01 (NAS compose/.env drift), GAP-CI-01, GAP-I18N-02, GAP-TEAM-02. - design.md: hero loop decision (numbers taken from the app capsule), pricing mismatch closed; feature catalog SHELL-11 updated. - docs/map, release guide and mobile release docs no longer describe the deleted wwwroot, binaries, Dockerfile.admin, NAS site copy or .github CI. - docker-compose.nas.yml gains the d3ro-whisper service that only existed in the NAS copy, so the repository file is the complete definition. - refactor-wave skill: Wave 3 index and lessons P10-P12.
193 lines
7.4 KiB
Markdown
193 lines
7.4 KiB
Markdown
# Phase 7: 테스트 + 빌드 + 배포
|
|
|
|
> 2026-09-26: `.github/workflows`는 삭제되었고 CI는 `.forgejo/workflows`가 정본이다 — 아래 `7.9 CI/CD (GitHub Actions)` 절은 최초 계획 시점의 스냅샷이다.
|
|
|
|
## 목표
|
|
테스트 커버리지를 확보하고, 프로덕션 빌드/패키징 파이프라인을 구축하여 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)
|
|
- [ ] (선택) 자동 업데이트 체크 작동
|