PLAUD 수준의 기능 확장: - 전사 편집: 인라인 편집 + 원본 보존 + 수정됨 표시(점선 밑줄) - 다중 문서 생성: 회의록/보고서/아이디어노트/커스텀 템플릿 - MD 에디터: 렌더링/편집 토글 (react-markdown + remark-gfm) - 상세 페이지 리디자인: 풀스크린 탭 전환 구조 - 내보내기: MD, PDF, TXT, DOCX 4종 (docx 패키지) - 커스텀 프롬프트 템플릿 저장/재사용 (electron-store) - SSOT: 마크다운 파싱 유틸 추출, 후처리에서 자동 문서 생성 제거 DB: meeting_documents 테이블 + edited_transcript 컬럼 IPC: 8+4 채널, 서비스 2개(MeetingModeService 확장 + MeetingDocTemplateService 신규) UI: 8개 신규 컴포넌트 (meeting/ 디렉토리), 12개 locale i18n
17 KiB
17 KiB
Phase 14.5: 회의 모드 고도화 — 문서 생성/편집/내보내기
PLAUD/Otter.ai 수준의 다중 문서 생성 + 전사 편집 + MD 에디터 + 다운로드 확장 기존 Phase 14 인프라(녹음+전사+메모+LLM) 위에 확장
1. 개요
1.1 사용자 플로우
[회의 종료 후 상세 페이지]
│
├── [Transcript 탭] ← 전사 원문 편집 + 원본/수정본 토글
│ ├── 세그먼트 클릭 → 인라인 편집 (점선 밑줄 + hover "수정됨")
│ ├── 원본 보기 토글 (rawTranscript ↔ editedTranscript)
│ └── [TXT 다운로드] 버튼
│
├── [+ 버튼] → 문서 생성 다이얼로그
│ ├── 템플릿 선택 (회의록 / 보고서 / 아이디어 노트 / 커스텀)
│ ├── 커스텀: 이름 + 프롬프트 입력 (저장/재사용 가능)
│ └── [생성] → LLM 호출 → 새 탭 추가
│
└── [문서 탭 (동적)] ← 회의록, 보고서 등
├── 미리보기 모드: react-markdown 렌더링
├── 편집 모드: 마크다운 원본 textarea
├── 토글 버튼 (미리보기 ↔ 편집)
├── debounce 자동 저장 (500ms)
└── [내보내기 ▼] 드롭다운 (MD / PDF / TXT / DOCX)
1.2 핵심 차별점
- 다중 문서: 하나의 전사에서 여러 양식의 문서 생성 (PLAUD Multidimensional Summary)
- 전사 편집: 녹음 중 + 종료 후 인라인 편집, 원본 보존, 수정 표시
- 커스텀 프롬프트: 사용자 정의 AI 프롬프트 저장/재사용
- 4종 내보내기: MD, PDF, TXT, DOCX
- MD 에디터: 렌더링/편집 토글
2. DB 스키마
2.1 meeting_sessions 테이블 확장
-- 마이그레이션: 전사 편집 원본 보존용 컬럼 추가
ALTER TABLE meeting_sessions ADD COLUMN edited_transcript TEXT;
2.2 meeting_documents 테이블 (신규)
CREATE TABLE IF NOT EXISTS meeting_documents (
id TEXT PRIMARY KEY,
session_id TEXT NOT NULL,
template_type TEXT NOT NULL, -- 'minutes' | 'report' | 'idea-note' | 'custom'
title TEXT NOT NULL,
content TEXT NOT NULL DEFAULT '',
prompt_used TEXT, -- 커스텀 프롬프트 기록용
llm_model TEXT,
llm_latency_ms INTEGER,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_meeting_documents_session_id ON meeting_documents(session_id);
2.3 drizzle-orm 스키마 (src/main/db/schema.ts)
// meetingSessions에 추가
editedTranscript: text('edited_transcript'),
// 신규 테이블
export const meetingDocuments = sqliteTable(
'meeting_documents',
{
id: text('id').primaryKey(),
sessionId: text('session_id').notNull(),
templateType: text('template_type', {
enum: ['minutes', 'report', 'idea-note', 'custom'],
}).notNull(),
title: text('title').notNull(),
content: text('content').notNull().default(''),
promptUsed: text('prompt_used'),
llmModel: text('llm_model'),
llmLatencyMs: integer('llm_latency_ms'),
createdAt: integer('created_at').notNull(),
updatedAt: integer('updated_at').notNull(),
},
(table) => [
index('idx_meeting_documents_session_id').on(table.sessionId),
]
)
3. 공유 타입 (src/shared/types.ts)
// ============================================================
// Phase 14.5: Meeting Document Types
// ============================================================
export type MeetingDocTemplateType = 'minutes' | 'report' | 'idea-note' | 'custom'
export interface MeetingDocTemplate {
id: string
name: string
description: string
templateType: MeetingDocTemplateType
systemPrompt: string
isBuiltin: boolean
createdAt: number
updatedAt: number
}
export interface MeetingDocument {
id: string
sessionId: string
templateType: MeetingDocTemplateType
title: string
content: string
promptUsed: string | null
llmModel: string | null
llmLatencyMs: number | null
createdAt: number
updatedAt: number
}
export interface MeetingGenerateDocParams {
sessionId: string
templateId: string
customPrompt?: string
customTitle?: string
}
export interface MeetingGenerateDocResult {
document: MeetingDocument
}
export interface MeetingUpdateDocParams {
documentId: string
content: string
}
export interface MeetingDeleteDocParams {
documentId: string
}
export interface MeetingGetDocsParams {
sessionId: string
}
export interface MeetingUpdateTranscriptParams {
sessionId: string
editedTranscript: string
}
export type MeetingExportFormat = 'md' | 'pdf' | 'txt' | 'docx'
export interface MeetingExportDocParams {
documentId: string
format: MeetingExportFormat
}
export interface MeetingExportTranscriptParams {
sessionId: string
}
export interface MeetingDocGeneratingProgress {
sessionId: string
templateId: string
percent: number
}
export interface CreateMeetingDocTemplateParams {
name: string
description: string
systemPrompt: string
}
export interface UpdateMeetingDocTemplateParams {
id: string
name?: string
description?: string
systemPrompt?: string
}
export interface DeleteMeetingDocTemplateParams {
id: string
}
MeetingSessionDetail 확장:
export interface MeetingSessionDetail extends MeetingSessionSummary {
rawTranscript: string | null
editedTranscript: string | null // 추가
minutesMarkdown: string | null
minutes: MeetingMinutes | null
memos: MeetingMemo[]
documents: MeetingDocument[] // 추가
sttModel: string | null
llmModel: string | null
errorMessage: string | null
}
4. 에러 코드 (src/shared/errors.ts)
// === Phase 14.5: Meeting Enhancement (886-892) ===
MeetingDocumentNotFound = 886,
MeetingDocGenerationFailed = 887,
MeetingDocExportFailed = 888,
MeetingTranscriptUpdateFailed = 889,
MeetingDocTemplateNotFound = 890,
MeetingDocTemplateBuiltinDelete = 891,
MeetingDocxExportFailed = 892,
5. IPC 채널 (src/shared/ipc-channels.ts)
// MEETING_MODE 블록에 추가
UPDATE_TRANSCRIPT: 'meetingMode:updateTranscript',
GENERATE_DOCUMENT: 'meetingMode:generateDocument',
GET_DOCUMENTS: 'meetingMode:getDocuments',
UPDATE_DOCUMENT: 'meetingMode:updateDocument',
DELETE_DOCUMENT: 'meetingMode:deleteDocument',
EXPORT_DOCUMENT: 'meetingMode:exportDocument',
EXPORT_TRANSCRIPT: 'meetingMode:exportTranscript',
DOC_GENERATING_PROGRESS: 'meetingMode:docGeneratingProgress',
// 신규 그룹
MEETING_DOC_TEMPLATE: {
GET_ALL: 'meetingDocTemplate:getAll',
CREATE: 'meetingDocTemplate:create',
UPDATE: 'meetingDocTemplate:update',
DELETE: 'meetingDocTemplate:delete',
},
6. 서비스 명세
6.1 MeetingDocTemplateService (신규)
파일: src/main/services/MeetingDocTemplateService.ts
패턴: electron-store 전용 인스턴스 (DictationTemplateService 패턴)
빌트인 3종:
const BUILTIN_TEMPLATES: MeetingDocTemplate[] = [
{
id: 'builtin-minutes',
name: '회의록',
templateType: 'minutes',
systemPrompt: `전사록을 분석하여 구조화된 회의록을 작성하세요.
## 요약 (3-5줄)
## 핵심 결정사항 (- 항목)
## 할 일 목록 (- [ ] 담당자: 할일)
## 타임라인 (| 시간 | 내용 |)`,
isBuiltin: true,
},
{
id: 'builtin-report',
name: '보고서',
templateType: 'report',
systemPrompt: `전사록을 분석하여 제출용 보고서를 작성하세요.
## 개요 (배경, 목적)
## 핵심 내용 (주요 논의 사항)
## 결론 및 제안
## 첨부 참고사항`,
isBuiltin: true,
},
{
id: 'builtin-idea-note',
name: '아이디어 노트',
templateType: 'idea-note',
systemPrompt: `전사록에서 아이디어와 인사이트를 추출하세요.
## 핵심 아이디어 (번호 매기기)
## 각 아이디어의 장단점
## 우선순위 제안
## 다음 단계`,
isBuiltin: true,
},
]
메서드: getAll(), getById(), create(), update(), delete()
6.2 MeetingModeService 확장
새 메서드:
| 메서드 | 설명 |
|---|---|
updateTranscript(sessionId, editedTranscript) |
수정 전사 저장 |
generateDocument(params) |
전사+템플릿→LLM→meeting_documents 저장 |
getDocuments(sessionId) |
세션의 문서 목록 |
updateDocument(documentId, content) |
MD 편집 저장 |
deleteDocument(documentId) |
문서 삭제 |
exportDocument(documentId, format) |
MD/PDF/TXT/DOCX 내보내기 |
exportTranscript(sessionId) |
전사 TXT 다운로드 |
후처리 변경: _runPostProcessing()에서 자동 회의록 생성 제거. 전사 합산 + DB 저장(rawTranscript, status=completed)만 수행. 사용자가 [+]로 직접 문서 생성.
_loadSessionDetail 확장: editedTranscript + documents[] 포함
6.3 유틸 추출 (SSOT)
src/main/utils/meeting-markdown.ts:
parseMinutes(md)— MeetingModeService._parseMinutes() 추출buildExportMarkdown(session)— _buildExportMarkdown() 추출markdownToSimpleHtml(md)— _markdownToSimpleHtml() 추출
src/main/utils/markdown-to-docx.ts:
markdownToDocx(md, title): Promise<Buffer>— docx 패키지
7. Preload API (src/preload/index.ts)
// meetingMode 확장
updateTranscript: (params) => invoke(ch.UPDATE_TRANSCRIPT, params),
generateDocument: (params) => invoke(ch.GENERATE_DOCUMENT, params),
getDocuments: (params) => invoke(ch.GET_DOCUMENTS, params),
updateDocument: (params) => invoke(ch.UPDATE_DOCUMENT, params),
deleteDocument: (params) => invoke(ch.DELETE_DOCUMENT, params),
exportDocument: (params) => invoke(ch.EXPORT_DOCUMENT, params),
exportTranscript: (params) => invoke(ch.EXPORT_TRANSCRIPT, params),
onDocGeneratingProgress: (cb) => on(ch.DOC_GENERATING_PROGRESS, cb),
// 신규 그룹
meetingDocTemplate: {
getAll: () => invoke(ch.GET_ALL),
create: (params) => invoke(ch.CREATE, params),
update: (params) => invoke(ch.UPDATE, params),
delete: (params) => invoke(ch.DELETE, params),
},
8. UI 컴포넌트
8.1 컴포넌트 디렉토리: src/renderer/components/meeting/
| 컴포넌트 | 설명 |
|---|---|
EditableSegment.tsx |
전사 세그먼트 인라인 편집. 클릭→TextField, 점선 밑줄, hover "수정됨" 툴팁 |
MarkdownRenderer.tsx |
react-markdown + remark-gfm 래퍼. d3roPalette 스타일 |
MarkdownEditor.tsx |
미리보기/편집 토글. 편집 모드=textarea, debounce 자동저장(500ms) |
ExportMenu.tsx |
MD/PDF/TXT/DOCX 드롭다운 |
TranscriptTab.tsx |
전사 편집 탭. 원본/수정본 토글, EditableSegment 목록, TXT 다운로드 |
DocumentTab.tsx |
문서 탭. MarkdownEditor + ExportMenu + 삭제 |
AddDocumentDialog.tsx |
[+] 버튼 다이얼로그. 템플릿 선택(빌트인3+커스텀), 프롬프트 입력, 생성 |
MeetingDetailTabs.tsx |
탭 바 + 콘텐츠 라우팅 컨테이너 |
8.2 상세 뷰 레이아웃
┌──────────────────────────────────────────────────┐
│ [←] 프로젝트 킥오프 ✏️ [Export ▼] [🗑] │
│ 2026-04-06 14:30 ~ 15:12 · 42분 │
├──────────────────────────────────────────────────┤
│ [📝 Transcript] [회의록] [보고서] [+ 추가] │
├──────────────────────────────────────────────────┤
│ │
│ (선택된 탭의 풀스크린 콘텐츠) │
│ │
│ Transcript탭: EditableSegment 목록 │
│ 문서탭: MarkdownEditor (렌더링↔편집 토글) │
│ │
└──────────────────────────────────────────────────┘
8.3 녹음 뷰 세그먼트 편집
┌─ 실시간 전사 ScreenPanel ────────────────┐
│ [00:15] 안녕하세요 오늘 회의를... │ ← 클릭 시 편집 가능
│ ✏̣[̣0̣1̣:̣3̣0̣]̣ ̣예̣산̣ ̣관̣련̣인̣데̣요̣.̣.̣.̣ │ ← 수정됨: 점선 밑줄
│ [02:00] 다음 안건은... │
└──────────────────────────────────────────┘
9. i18n 키
ko.json 추가
"meeting.transcriptTab": "전사",
"meeting.addDocument": "문서 추가",
"meeting.selectTemplate": "템플릿 선택",
"meeting.generate": "생성",
"meeting.generating": "문서 생성 중...",
"meeting.editMode": "편집",
"meeting.previewMode": "미리보기",
"meeting.modified": "수정됨",
"meeting.originalText": "원본 보기",
"meeting.editedText": "수정본 보기",
"meeting.exportDocx": "DOCX 내보내기",
"meeting.exportTxt": "TXT 내보내기",
"meeting.exportFormat": "내보내기 형식",
"meeting.customPrompt": "커스텀 프롬프트",
"meeting.templateName": "템플릿 이름",
"meeting.templatePrompt": "AI 프롬프트",
"meeting.templateSave": "템플릿 저장",
"meeting.documentDeleted": "문서가 삭제되었습니다",
"meeting.autoSaved": "자동 저장됨",
"meeting.minutes": "회의록",
"meeting.report": "보고서",
"meeting.ideaNote": "아이디어 노트",
"meeting.custom": "커스텀"
10. 구현 순서
Step A: 기반 인프라 (타입 + DB + IPC 채널 + 에러코드)
src/shared/types.ts— 새 타입 추가 + MeetingSessionDetail 확장src/shared/ipc-channels.ts— MEETING_MODE 확장 + MEETING_DOC_TEMPLATE 추가src/shared/errors.ts— 886-892 추가src/main/db/schema.ts— meetingDocuments + editedTranscriptsrc/main/db/index.ts— 마이그레이션 SQL
Step B: npm 패키지 설치
npm install react-markdown remark-gfm docx
Step C: 유틸 추출 (SSOT)
src/main/utils/meeting-markdown.ts— 파싱/내보내기 로직 추출src/main/utils/markdown-to-docx.ts— DOCX 변환
Step D: 서비스 계층
src/main/services/MeetingDocTemplateService.ts— 신규src/main/services/MeetingModeService.ts— 확장 (7 메서드 + 후처리 변경 + _loadSessionDetail)
Step E: IPC + Preload
src/main/ipc/meeting-doc-template-handlers.ts— 신규src/main/ipc/meeting-mode-handlers.ts— 확장src/main/ipc/index.ts— import 추가src/preload/index.ts— API 확장
Step F: UI 컴포넌트
15-22. src/renderer/components/meeting/ — 8개 컴포넌트
Step G: 페이지 통합
src/renderer/pages/MeetingModePage.tsx— 상세 뷰 리디자인 + 녹음 뷰 편집
Step H: i18n
- 12개 locale 키 추가
Step I: 마무리
- typecheck + build + test
- bootstrap 초기화 단계 추가 (MeetingDocTemplateService)
11. TODO 항목 (향후 구현)
- AI 채팅: 회의록 레퍼런스 기반 Q&A (여러 레퍼런스 선택 + 대화)
- 웹 공유: 만료 링크 공유 (웹 서비스 연동 대비 설계)
- 마인드맵: AI 시각적 마인드맵 생성 (JPEG/SVG 내보내기)
- Auto Polish: AI 전사 다듬기 (필러 제거, 가독성 향상)
- 화자 구분: 스피커 라벨링 + 이름 지정
파일 목록
신규 (12개)
| 파일 | 설명 |
|---|---|
src/main/services/MeetingDocTemplateService.ts |
문서 템플릿 CRUD |
src/main/ipc/meeting-doc-template-handlers.ts |
템플릿 IPC |
src/main/utils/meeting-markdown.ts |
MD 파싱 유틸 |
src/main/utils/markdown-to-docx.ts |
DOCX 변환 |
src/renderer/components/meeting/MeetingDetailTabs.tsx |
탭 컨테이너 |
src/renderer/components/meeting/TranscriptTab.tsx |
전사 편집 탭 |
src/renderer/components/meeting/DocumentTab.tsx |
문서 뷰/편집 탭 |
src/renderer/components/meeting/AddDocumentDialog.tsx |
문서 생성 다이얼로그 |
src/renderer/components/meeting/MarkdownRenderer.tsx |
MD 렌더러 래퍼 |
src/renderer/components/meeting/MarkdownEditor.tsx |
렌더링/편집 토글 |
src/renderer/components/meeting/EditableSegment.tsx |
인라인 편집 세그먼트 |
src/renderer/components/meeting/ExportMenu.tsx |
내보내기 드롭다운 |
수정 (10+12 locale)
| 파일 | 변경 |
|---|---|
src/shared/types.ts |
새 타입 + MeetingSessionDetail 확장 |
src/shared/ipc-channels.ts |
8+4 채널 |
src/shared/errors.ts |
7개 에러 코드 |
src/main/db/schema.ts |
meetingDocuments + editedTranscript |
src/main/db/index.ts |
마이그레이션 |
src/main/services/MeetingModeService.ts |
7 메서드 + 후처리 변경 |
src/main/ipc/meeting-mode-handlers.ts |
7 핸들러 |
src/main/ipc/index.ts |
import 추가 |
src/preload/index.ts |
API 확장 |
src/renderer/pages/MeetingModePage.tsx |
상세 뷰 리디자인 |
src/renderer/i18n/*.json |
12개 locale |