Phase 10~11 전체 구현: 킬러 피처 5종 + 수익화 시스템
Phase 10 킬러 피처: - MemoService: 태그 CRUD + 마크다운 내보내기 (memo_tags DB) - VoiceCommandService: 키워드→명령어 매칭, 프리셋 4종 - ScreenContextService: PowerShell 활성 윈도우 + Ctrl+C 선택 텍스트 - ChainService: LLM 명령어 순차 실행 파이프라인 - CaptionService: 6초 청크 연속 전사 + 시스템 오디오 루프백 VoiceModeService 파이프라인 통합: - 녹음 시작 → 컨텍스트 캡처 → STT → 키워드 매칭 → LLM(체인/컨텍스트 주입) → 삽입 시스템 오디오 캡처: - setDisplayMediaRequestHandler + audio: 'loopback' (IPC 브릿지) - electron-audio-loopback 패키지 contextIsolation 호환 불가 → 직접 구현 Phase 11 수익화: - LicenseService: Free/Pro/Pro+ 3티어, LemonSqueezy API - Feature Gate: requireFeature/checkFeature/consumeFeature - 일일 쿼터: Free dictation 20/일, LLM 10/일 (SQLite daily_usage) - LicenseModal, ProBadge, UpgradePromptModal UI 디자인 보강: - d3roTypo(13종), d3roShadow(10종), d3roRadius(7종) 토큰 시스템 - ScreenPanel, ButtonGroup DS 컴포넌트 신규 - PhosphorText 4→13종 변형, MetalDial conic-gradient 광택 - 공유 컴포넌트: EmptyStateCard, SearchInput, PageHeader, HistoryEntryCard 기타: - 자막 핫키 SSOT 전체 연동 (Config→Hotkey→VoiceMode→Caption→Settings) - StatusBar 자막 LED + 효과음, 자막 로딩 UI - LLM 상태 이벤트 전파 수정 (폴링 제거 → onStatusChanged) - 커맨드 팝업 "선택 해제" 항목 추가
This commit is contained in:
parent
36d77ca224
commit
a31f96bbb8
97 changed files with 11853 additions and 1143 deletions
447
docs/phases/phase-11-monetization.md
Normal file
447
docs/phases/phase-11-monetization.md
Normal file
|
|
@ -0,0 +1,447 @@
|
|||
# Phase 11: 수익화 기반 — LicenseService + Feature Gating
|
||||
|
||||
## 목표
|
||||
Freemium 모델 구현. Free/Pro/Pro+ 3단계 티어, 일일 사용량 제한, 라이센스 키 활성화, 업그레이드 유도 UI.
|
||||
로컬 앱이므로 오프라인 우선 설계 — 키 검증은 최초 1회 온라인, 이후 로컬 검증.
|
||||
|
||||
---
|
||||
|
||||
## 1. 티어 정의
|
||||
|
||||
| 기능 | Free | Pro (₩39,000) | Pro+ (₩69,000) |
|
||||
|------|------|---------------|-----------------|
|
||||
| 받아쓰기 | 15회/일 | 무제한 | 무제한 |
|
||||
| LLM 다듬기 | 3회/일 | 무제한 | 무제한 |
|
||||
| 히스토리 보존 | 3일 | 무제한 | 무제한 |
|
||||
| 커스텀 명령어 | 프리셋만 | 무제한 생성 | 무제한 생성 |
|
||||
| 히스토리 내보내기 | X | O | O |
|
||||
| 실시간 자막 | X | O | O |
|
||||
| 스크린 컨텍스트 | X | O | O |
|
||||
| 음성 메모/태그 | X | O | O |
|
||||
| 음성 단축키 | X | O | O |
|
||||
| LLM 체인 | X | O | O |
|
||||
| 파일 전사 | X | X | O |
|
||||
| 음성 대화 모드 | X | X | O |
|
||||
| 딕테이션 템플릿 | X | X | O |
|
||||
| 회의록 자동 요약 | X | X | O |
|
||||
| 로컬 RAG | X | X | O |
|
||||
| OS 자동화 | X | X | O |
|
||||
|
||||
---
|
||||
|
||||
## 2. Feature 열거형
|
||||
|
||||
```typescript
|
||||
/** 기능 게이팅 대상 */
|
||||
export enum Feature {
|
||||
// 쿼터 제한 기능 (Free에서 횟수 제한)
|
||||
DICTATION = 'dictation',
|
||||
LLM_PROCESS = 'llm_process',
|
||||
|
||||
// Pro 기능 (Free에서 잠금)
|
||||
HISTORY_UNLIMITED = 'history_unlimited',
|
||||
HISTORY_EXPORT = 'history_export',
|
||||
CUSTOM_INSTRUCTION_CREATE = 'custom_instruction_create',
|
||||
LIVE_CAPTION = 'live_caption',
|
||||
SCREEN_CONTEXT = 'screen_context',
|
||||
VOICE_MEMO = 'voice_memo',
|
||||
VOICE_COMMAND = 'voice_command',
|
||||
LLM_CHAIN = 'llm_chain',
|
||||
|
||||
// Pro+ 기능 (Pro에서도 잠금)
|
||||
FILE_TRANSCRIPTION = 'file_transcription',
|
||||
VOICE_CONVERSATION = 'voice_conversation',
|
||||
DICTATION_TEMPLATE = 'dictation_template',
|
||||
MEETING_SUMMARY = 'meeting_summary',
|
||||
LOCAL_RAG = 'local_rag',
|
||||
OS_AUTOMATION = 'os_automation',
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 타입 정의
|
||||
|
||||
```typescript
|
||||
/** 라이센스 티어 */
|
||||
export type LicenseTier = 'free' | 'pro' | 'pro_plus'
|
||||
|
||||
/** 라이센스 정보 (electron-store에 저장) */
|
||||
export interface LicenseInfo {
|
||||
tier: LicenseTier
|
||||
licenseKey: string | null
|
||||
activatedAt: number | null
|
||||
machineId: string
|
||||
/** 마지막 온라인 검증 시각 */
|
||||
lastVerifiedAt: number | null
|
||||
/** 오프라인 유예 만료 (lastVerifiedAt + 30일) */
|
||||
offlineGraceUntil: number | null
|
||||
}
|
||||
|
||||
/** 일일 사용량 */
|
||||
export interface DailyUsage {
|
||||
date: string // 'YYYY-MM-DD'
|
||||
dictationCount: number
|
||||
llmProcessCount: number
|
||||
}
|
||||
|
||||
/** 쿼터 정보 */
|
||||
export interface UsageQuota {
|
||||
feature: Feature
|
||||
used: number
|
||||
limit: number // -1 = 무제한
|
||||
remaining: number // -1 = 무제한
|
||||
resetAt: string // 다음 리셋 시각 (내일 00:00) ISO 8601
|
||||
}
|
||||
|
||||
/** 기능 접근 결과 */
|
||||
export interface FeatureAccess {
|
||||
allowed: boolean
|
||||
reason?: 'ok' | 'quota_exceeded' | 'tier_required' | 'license_expired'
|
||||
requiredTier?: LicenseTier
|
||||
quota?: UsageQuota
|
||||
}
|
||||
|
||||
/** 업그레이드 유도 이벤트 */
|
||||
export interface UpgradePromptEvent {
|
||||
feature: Feature
|
||||
reason: 'quota_exceeded' | 'tier_required'
|
||||
currentTier: LicenseTier
|
||||
requiredTier: LicenseTier
|
||||
quota?: UsageQuota
|
||||
}
|
||||
|
||||
/** 라이센스 활성화 파라미터 */
|
||||
export interface ActivateLicenseParams {
|
||||
licenseKey: string
|
||||
}
|
||||
|
||||
/** 라이센스 활성화 결과 */
|
||||
export interface ActivateLicenseResult {
|
||||
success: boolean
|
||||
tier: LicenseTier
|
||||
message: string
|
||||
}
|
||||
|
||||
/** 티어별 기능 비교 */
|
||||
export interface TierComparison {
|
||||
feature: string
|
||||
featureLabel: string
|
||||
free: boolean | string
|
||||
pro: boolean | string
|
||||
proPlus: boolean | string
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. DB 스키마
|
||||
|
||||
```sql
|
||||
CREATE TABLE daily_usage (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
date TEXT NOT NULL, -- 'YYYY-MM-DD'
|
||||
feature TEXT NOT NULL, -- Feature enum value
|
||||
count INTEGER NOT NULL DEFAULT 0,
|
||||
UNIQUE(date, feature)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_daily_usage_date ON daily_usage(date);
|
||||
```
|
||||
|
||||
```typescript
|
||||
// drizzle-orm 스키마
|
||||
export const dailyUsage = sqliteTable('daily_usage', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
date: text('date').notNull(),
|
||||
feature: text('feature').notNull(),
|
||||
count: integer('count').notNull().default(0),
|
||||
}, (table) => [
|
||||
uniqueIndex('idx_daily_usage_date_feature').on(table.date, table.feature),
|
||||
index('idx_daily_usage_date').on(table.date),
|
||||
]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. IPC 채널
|
||||
|
||||
```typescript
|
||||
LICENSE: {
|
||||
GET_INFO: 'license:getInfo',
|
||||
ACTIVATE: 'license:activate',
|
||||
DEACTIVATE: 'license:deactivate',
|
||||
CHECK_FEATURE: 'license:checkFeature',
|
||||
GET_USAGE: 'license:getUsage',
|
||||
GET_ALL_USAGE: 'license:getAllUsage',
|
||||
GET_TIER_COMPARISON: 'license:getTierComparison',
|
||||
// Main → Renderer events
|
||||
UPGRADE_PROMPT: 'license:upgradePrompt',
|
||||
TIER_CHANGED: 'license:tierChanged',
|
||||
}
|
||||
```
|
||||
|
||||
| 채널 | 방향 | 파라미터 | 반환 | 설명 |
|
||||
|------|------|---------|------|------|
|
||||
| `license:getInfo` | handle | void | `LicenseInfo` | 현재 라이센스 정보 |
|
||||
| `license:activate` | handle | `ActivateLicenseParams` | `ActivateLicenseResult` | 키 활성화 |
|
||||
| `license:deactivate` | handle | void | void | 키 비활성화 (Free로 복귀) |
|
||||
| `license:checkFeature` | handle | `{ feature: Feature }` | `FeatureAccess` | 기능 접근 가능 여부 |
|
||||
| `license:getUsage` | handle | `{ feature: Feature }` | `UsageQuota` | 특정 기능 사용량 |
|
||||
| `license:getAllUsage` | handle | void | `UsageQuota[]` | 전체 기능 사용량 |
|
||||
| `license:getTierComparison` | handle | void | `TierComparison[]` | 티어 비교표 |
|
||||
| `license:upgradePrompt` | send | — | `UpgradePromptEvent` | 업그레이드 유도 이벤트 |
|
||||
| `license:tierChanged` | send | — | `LicenseInfo` | 티어 변경 알림 |
|
||||
|
||||
---
|
||||
|
||||
## 6. LicenseService 인터페이스
|
||||
|
||||
```typescript
|
||||
interface ILicenseService {
|
||||
/** 현재 티어 */
|
||||
readonly tier: LicenseTier
|
||||
|
||||
/** 라이센스 정보 */
|
||||
getInfo(): LicenseInfo
|
||||
|
||||
/**
|
||||
* 기능 사용 가능 여부 확인.
|
||||
* 쿼터 기능: 남은 횟수 체크.
|
||||
* 티어 잠금 기능: 현재 티어로 접근 가능한지.
|
||||
*/
|
||||
canUse(feature: Feature): FeatureAccess
|
||||
|
||||
/**
|
||||
* 기능 사용 소비 (쿼터 차감).
|
||||
* canUse 통과 후 실제 사용 시 호출.
|
||||
* 쿼터 초과 시 D3ROError(QuotaExceeded) throw.
|
||||
*/
|
||||
consumeQuota(feature: Feature): void
|
||||
|
||||
/** 일일 사용량 조회 */
|
||||
getUsage(feature: Feature): UsageQuota
|
||||
|
||||
/** 전체 사용량 조회 */
|
||||
getAllUsage(): UsageQuota[]
|
||||
|
||||
/** 라이센스 키 활성화 */
|
||||
activate(key: string): Promise<ActivateLicenseResult>
|
||||
|
||||
/** 라이센스 비활성화 */
|
||||
deactivate(): void
|
||||
|
||||
/** 티어 비교표 */
|
||||
getTierComparison(): TierComparison[]
|
||||
|
||||
/** 업그레이드 유도 이벤트 발생 (내부 + IPC 전파) */
|
||||
promptUpgrade(feature: Feature, reason: UpgradePromptEvent['reason']): void
|
||||
}
|
||||
```
|
||||
|
||||
### 쿼터 한도
|
||||
|
||||
```typescript
|
||||
const QUOTA_LIMITS: Record<LicenseTier, Partial<Record<Feature, number>>> = {
|
||||
free: {
|
||||
[Feature.DICTATION]: 15,
|
||||
[Feature.LLM_PROCESS]: 3,
|
||||
},
|
||||
pro: {}, // 무제한
|
||||
pro_plus: {}, // 무제한
|
||||
}
|
||||
```
|
||||
|
||||
### 티어별 기능 접근
|
||||
|
||||
```typescript
|
||||
const FEATURE_TIERS: Record<Feature, LicenseTier> = {
|
||||
// 모든 티어 (쿼터만 다름)
|
||||
[Feature.DICTATION]: 'free',
|
||||
[Feature.LLM_PROCESS]: 'free',
|
||||
|
||||
// Pro 이상
|
||||
[Feature.HISTORY_UNLIMITED]: 'pro',
|
||||
[Feature.HISTORY_EXPORT]: 'pro',
|
||||
[Feature.CUSTOM_INSTRUCTION_CREATE]: 'pro',
|
||||
[Feature.LIVE_CAPTION]: 'pro',
|
||||
[Feature.SCREEN_CONTEXT]: 'pro',
|
||||
[Feature.VOICE_MEMO]: 'pro',
|
||||
[Feature.VOICE_COMMAND]: 'pro',
|
||||
[Feature.LLM_CHAIN]: 'pro',
|
||||
|
||||
// Pro+ 이상
|
||||
[Feature.FILE_TRANSCRIPTION]: 'pro_plus',
|
||||
[Feature.VOICE_CONVERSATION]: 'pro_plus',
|
||||
[Feature.DICTATION_TEMPLATE]: 'pro_plus',
|
||||
[Feature.MEETING_SUMMARY]: 'pro_plus',
|
||||
[Feature.LOCAL_RAG]: 'pro_plus',
|
||||
[Feature.OS_AUTOMATION]: 'pro_plus',
|
||||
}
|
||||
```
|
||||
|
||||
### 히스토리 보존 정책
|
||||
|
||||
```typescript
|
||||
const HISTORY_RETENTION_DAYS: Record<LicenseTier, number> = {
|
||||
free: 3,
|
||||
pro: -1, // 무제한
|
||||
pro_plus: -1, // 무제한
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 라이센스 키 검증
|
||||
|
||||
### 오프라인 우선 설계
|
||||
|
||||
```
|
||||
최초 활성화 (온라인 필수):
|
||||
1. 사용자가 키 입력
|
||||
2. LemonSqueezy API 검증: POST /v1/licenses/activate
|
||||
3. 응답에서 tier 추출 (meta.tier 또는 variant_id 매핑)
|
||||
4. electron-store에 저장: { tier, key, activatedAt, machineId, lastVerifiedAt }
|
||||
5. 오프라인 유예: lastVerifiedAt + 30일
|
||||
|
||||
이후 앱 실행 시:
|
||||
1. electron-store에서 라이센스 정보 로드
|
||||
2. tier !== 'free' → 로컬 검증 (machineId 일치, 유예 기간 내)
|
||||
3. 30일 경과 → 백그라운드 재검증 시도
|
||||
4. 재검증 실패 → 7일 추가 유예 후 Free로 다운그레이드
|
||||
5. 재검증 성공 → lastVerifiedAt 갱신
|
||||
```
|
||||
|
||||
### machineId 생성
|
||||
|
||||
```typescript
|
||||
import { machineIdSync } from 'node-machine-id'
|
||||
const machineId = machineIdSync(true) // 해시된 하드웨어 ID
|
||||
```
|
||||
|
||||
또는 electron-store에 저장된 UUID (하드웨어 변경에 더 관대):
|
||||
|
||||
```typescript
|
||||
import { randomUUID } from 'crypto'
|
||||
// 최초 실행 시 1회 생성 후 저장
|
||||
const machineId = store.get('machineId') ?? randomUUID()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. UI 명세
|
||||
|
||||
### 8.1 UpgradePromptModal
|
||||
|
||||
쿼터 소진 또는 잠긴 기능 접근 시 표시.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ 🔒 오늘의 받아쓰기를 모두 사용했습니다 │
|
||||
│ │
|
||||
│ Free: 15회/일 → Pro: 무제한 │
|
||||
│ │
|
||||
│ Pro로 업그레이드하면: │
|
||||
│ ✓ 무제한 받아쓰기 │
|
||||
│ ✓ AI 텍스트 다듬기 무제한 │
|
||||
│ ✓ 실시간 자막 │
|
||||
│ ✓ 히스토리 무제한 보존 │
|
||||
│ │
|
||||
│ [내일 다시 사용하기] [Pro 알아보기 →] │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 8.2 UsageIndicator (StatusBar 또는 Dashboard)
|
||||
|
||||
```
|
||||
[🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪ 12/15 받아쓰기]
|
||||
```
|
||||
|
||||
### 8.3 FeatureGate 래퍼 컴포넌트
|
||||
|
||||
```tsx
|
||||
<FeatureGate feature={Feature.LIVE_CAPTION} fallback={<LockedFeatureCard />}>
|
||||
<CaptionControls />
|
||||
</FeatureGate>
|
||||
```
|
||||
|
||||
### 8.4 Settings License 탭
|
||||
|
||||
```
|
||||
┌─ License ──────────────────────────────────────┐
|
||||
│ │
|
||||
│ 현재 플랜: FREE │
|
||||
│ │
|
||||
│ ┌─ 라이센스 키 ──────────────────────────────┐ │
|
||||
│ │ [____________________________] [활성화] │ │
|
||||
│ └────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─ 플랜 비교 ────────────────────────────────┐ │
|
||||
│ │ Free Pro Pro+ │ │
|
||||
│ │ 받아쓰기 15회/일 무제한 무제한 │ │
|
||||
│ │ AI다듬기 3회/일 무제한 무제한 │ │
|
||||
│ │ 자막 ✗ ✓ ✓ │ │
|
||||
│ │ 파일전사 ✗ ✗ ✓ │ │
|
||||
│ │ 대화모드 ✗ ✗ ✓ │ │
|
||||
│ │ ... │ │
|
||||
│ └────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ 오늘 사용량: │
|
||||
│ 받아쓰기: ████████░░░░░░░ 8/15 │
|
||||
│ AI 다듬기: ██░░░░░░░░░░░░ 1/3 │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Feature Gating 통합 포인트
|
||||
|
||||
| 서비스 | 체크 위치 | Feature |
|
||||
|--------|----------|---------|
|
||||
| VoiceModeService.startSession() | 세션 시작 전 | DICTATION |
|
||||
| LocalLLMService.process() | 처리 시작 전 | LLM_PROCESS |
|
||||
| CaptionService.start() | 자막 시작 전 | LIVE_CAPTION |
|
||||
| ScreenContextService.capture() | 캡처 전 | SCREEN_CONTEXT |
|
||||
| MemoService.addTag() | 태그 추가 전 | VOICE_MEMO |
|
||||
| VoiceCommandService.match() | 매칭 전 | VOICE_COMMAND |
|
||||
| ChainService.execute() | 체인 실행 전 | LLM_CHAIN |
|
||||
| HistoryService.export() | 내보내기 전 | HISTORY_EXPORT |
|
||||
| CustomInstructionService.create() | 생성 전 | CUSTOM_INSTRUCTION_CREATE |
|
||||
|
||||
---
|
||||
|
||||
## 10. 에러 코드
|
||||
|
||||
```typescript
|
||||
// 850-869: License
|
||||
LicenseKeyInvalid = 850,
|
||||
LicenseKeyExpired = 851,
|
||||
LicenseActivationFailed = 852,
|
||||
LicenseDeactivationFailed = 853,
|
||||
LicenseMachineIdMismatch = 854,
|
||||
LicenseOfflineGraceExpired = 855,
|
||||
LicenseVerificationFailed = 856,
|
||||
FeatureNotAvailable = 860,
|
||||
QuotaExceeded = 861,
|
||||
TierRequired = 862,
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Speakly RE 참조
|
||||
- Speakly는 클라우드 인증(AuthService) + 서버 라이센스 모델 사용
|
||||
- D3RO는 오프라인 우선 → LemonSqueezy 라이센스 키 + 로컬 검증
|
||||
- 쿼터 추적은 Speakly의 RecordStatsService 패턴 차용
|
||||
|
||||
## 완료 조건
|
||||
- [ ] LicenseService 싱글톤 구현 + 초기화
|
||||
- [ ] Feature enum + 티어별 접근 매핑
|
||||
- [ ] daily_usage 테이블 + 쿼터 추적
|
||||
- [ ] 라이센스 키 활성화/비활성화
|
||||
- [ ] IPC 핸들러 + preload API
|
||||
- [ ] UpgradePromptModal UI
|
||||
- [ ] Settings License 탭
|
||||
- [ ] Dashboard 사용량 표시
|
||||
- [ ] 기존 서비스에 canUse() 체크 통합
|
||||
- [ ] i18n 키 추가 (ko/en)
|
||||
- [ ] typecheck 통과
|
||||
38
docs/phases/phase-11.md
Normal file
38
docs/phases/phase-11.md
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Phase 11: 빌드 & 배포 + 라이선스 시스템
|
||||
|
||||
## 목표
|
||||
프로덕션 빌드 완성 + 라이선스 키 기반 Free/Pro/Pro+ 티어 시스템 구현
|
||||
|
||||
## 11.1 LicenseService 구현
|
||||
- `src/main/services/LicenseService.ts` — 싱글톤
|
||||
- electron-store에 LicenseInfo 저장 (machineId 바인딩)
|
||||
- 기능 게이팅: Feature enum 기반 접근 제어
|
||||
- 일일 쿼터: Free 티어 dictation 20회/일, LLM 10회/일
|
||||
- LemonSqueezy API 연동: activate/validate/deactivate
|
||||
- 오프라인 유예: 마지막 검증 후 30일
|
||||
|
||||
## 11.2 Feature Gate 미들웨어
|
||||
- `src/main/utils/feature-gate.ts` — requirePro(), checkAccess()
|
||||
- 각 서비스 start()/execute()에 게이트 체크 삽입
|
||||
- 렌더러: useProFeature() 훅
|
||||
|
||||
## 11.3 라이선스 UI
|
||||
- LicenseModal.tsx — 키 입력 + 활성화 + 상태 표시
|
||||
- ProUpgradeBanner.tsx — Free 사용자 업그레이드 유도
|
||||
- 각 페이지 Pro 기능에 잠금 아이콘 + 업그레이드 넛지
|
||||
|
||||
## 11.4 AutoUpdaterService
|
||||
- electron-updater 연동, GitHub Releases 기반
|
||||
- 앱 시작 30초 후 체크, 4시간 주기
|
||||
- Settings About 탭에 업데이트 버튼
|
||||
|
||||
## 11.5 빌드 최종화
|
||||
- electron-builder.yml 완성 (publish, asarUnpack, NSIS 커스텀)
|
||||
- GitHub Actions release workflow
|
||||
- prebuild 스크립트 (SoX + sidecar)
|
||||
|
||||
## 완료 조건
|
||||
- npm run typecheck 통과
|
||||
- npm run dist로 NSIS 인스톨러 생성
|
||||
- Free/Pro 기능 게이팅 동작
|
||||
- 라이선스 키 활성화/비활성화 UI 동작
|
||||
56
docs/phases/phase-12-pro-features.md
Normal file
56
docs/phases/phase-12-pro-features.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# Phase 12-13: Pro/Pro+ 킬러 피처
|
||||
|
||||
## Phase 12: Pro 피처 강화 (즉시 매출)
|
||||
|
||||
### 12.1 파일 전사 (File Transcription) — Pro+
|
||||
- 오디오/비디오 파일 드래그앤드롭 → Whisper 전사
|
||||
- ffmpeg(fluent-ffmpeg)로 미디어 → PCM 16kHz 변환
|
||||
- 배치 처리: 큰 파일을 30초 청크로 분할 → 순차 전사
|
||||
- 진행률 표시 (현재 청크/전체)
|
||||
- 결과: 전체 텍스트 + 타임스탬프 세그먼트
|
||||
- 히스토리에 자동 저장 (mode: 'file-transcription')
|
||||
- UI: DashboardPage에 드래그 존 또는 별도 페이지
|
||||
|
||||
### 12.2 회의록 자동 요약 (Meeting Summary) — Pro+
|
||||
- CaptionService 세션 종료 시 자동 트리거
|
||||
- 전체 전사 텍스트 → Ollama 요약 프롬프트
|
||||
- 출력: 3줄 요약 + 핵심 결정사항 + 할 일 목록
|
||||
- 마크다운 내보내기
|
||||
- 히스토리에 요약 첨부
|
||||
|
||||
### 12.3 딕테이션 템플릿 (Dictation Templates) — Pro+
|
||||
- 템플릿 정의: 이름 + 필드 목록 + 출력 포맷
|
||||
- 예: "이메일 템플릿" → 받는 사람, 제목, 본문 순서로 음성 입력
|
||||
- 상태 머신: 필드 안내 TTS → 음성 입력 → 다음 필드 → 완료
|
||||
- CustomInstruction과 별도 관리 (TemplateService)
|
||||
|
||||
## Phase 13: Pro+ 프리미엄 피처
|
||||
|
||||
### 13.1 음성 대화 모드 (Voice Conversation) — Pro+
|
||||
- THE 킬러 피처: 로컬 ChatGPT Voice Mode
|
||||
- Whisper STT → Ollama 스트리밍 → TTS 재생 → 다시 듣기
|
||||
- 대화 히스토리 컨텍스트 유지 (최근 10턴)
|
||||
- 문장 단위 TTS 스트리밍 (레이턴시 최적화)
|
||||
- UI: 전용 대화 윈도우 또는 DashboardPage 내 패널
|
||||
|
||||
### 13.2 로컬 RAG (Retrieval Augmented Generation) — Pro+
|
||||
- Ollama nomic-embed-text 모델로 문서 임베딩
|
||||
- better-sqlite3에 임베딩 벡터 저장 (float array → blob)
|
||||
- 코사인 유사도 검색 (SQLite UDF 또는 JS 계산)
|
||||
- 지원 포맷: .txt, .md, .pdf (pdf-parse), .docx
|
||||
- 음성 질문 → 관련 문서 검색 → LLM에 컨텍스트 주입 → 답변
|
||||
- UI: Knowledge Base 페이지 (문서 목록 + 인덱싱 상태)
|
||||
|
||||
### 13.3 OS 자동화 (Voice Action) — Pro+
|
||||
- 음성 → Ollama가 JSON 액션 플랜 생성 → 실행
|
||||
- 액션 타입: 앱 열기, URL 열기, 파일 열기, 키보드 단축키, 텍스트 입력
|
||||
- 안전장치: 실행 전 확인 팝업 (위험 액션)
|
||||
- 사전 정의 명령: "크롬 열어", "메모장 열어", "볼륨 올려"
|
||||
|
||||
---
|
||||
|
||||
## 구현 우선순위
|
||||
1. 파일 전사 (12.1) — 가장 쉬운 Pro+ 피처, ffmpeg만 추가
|
||||
2. 회의록 요약 (12.2) — 자막 종료 후 LLM 호출 추가만
|
||||
3. 음성 대화 모드 (13.1) — 가장 임팩트 큰 피처
|
||||
4. 나머지는 Phase 11 수익 검증 후 결정
|
||||
30
docs/phases/phase-12.md
Normal file
30
docs/phases/phase-12.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Phase 12: 랜딩 페이지 & 마케팅
|
||||
|
||||
## 목표
|
||||
Astro 기반 제품 랜딩 페이지 + SEO 블로그 + 출시 마케팅 계획
|
||||
|
||||
## 12.1 랜딩 페이지 (Astro + Tailwind)
|
||||
- `landing/` 디렉토리에 별도 프로젝트
|
||||
- 섹션: Hero, 데모 비디오, 기능 6개, 경쟁사 비교표, 가격표, 다운로드, FAQ
|
||||
- LemonSqueezy 결제 위젯 임베드
|
||||
- Vercel/Cloudflare Pages 배포
|
||||
|
||||
## 12.2 프라이버시 정책
|
||||
- 로컬 전용 → "We collect nothing"
|
||||
- 라이선스 키 검증 시 네트워크 통신 명시
|
||||
- GDPR/CCPA 대응
|
||||
|
||||
## 12.3 SEO 블로그
|
||||
- "Why local AI voice assistant"
|
||||
- "D3RO-VOICE vs Krisp vs Otter.ai"
|
||||
- "How to use Whisper + Ollama for dictation"
|
||||
|
||||
## 12.4 Product Hunt 준비
|
||||
- 태그라인, 설명, 갤러리 이미지, 데모 GIF
|
||||
- 메이커 코멘트 준비
|
||||
- PH 유저 50% 할인 프로모 코드
|
||||
|
||||
## 12.5 소셜 미디어 런칭
|
||||
- r/LocalLLaMA, r/selfhosted, HN "Show HN"
|
||||
- Twitter/X 데모 비디오 스레드
|
||||
- dev.to 기술 아티클
|
||||
Loading…
Add table
Add a link
Reference in a new issue