# 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` 테이블 확장 ```sql -- 마이그레이션: 전사 편집 원본 보존용 컬럼 추가 ALTER TABLE meeting_sessions ADD COLUMN edited_transcript TEXT; ``` ### 2.2 `meeting_documents` 테이블 (신규) ```sql 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`) ```typescript // 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`) ```typescript // ============================================================ // 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 확장:** ```typescript 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`) ```typescript // === 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`) ```typescript // 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종: ```typescript 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` — docx 패키지 --- ## 7. Preload API (`src/preload/index.ts`) ```typescript // 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 추가 ```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 채널 + 에러코드) 1. `src/shared/types.ts` — 새 타입 추가 + MeetingSessionDetail 확장 2. `src/shared/ipc-channels.ts` — MEETING_MODE 확장 + MEETING_DOC_TEMPLATE 추가 3. `src/shared/errors.ts` — 886-892 추가 4. `src/main/db/schema.ts` — meetingDocuments + editedTranscript 5. `src/main/db/index.ts` — 마이그레이션 SQL ### Step B: npm 패키지 설치 6. `npm install react-markdown remark-gfm docx` ### Step C: 유틸 추출 (SSOT) 7. `src/main/utils/meeting-markdown.ts` — 파싱/내보내기 로직 추출 8. `src/main/utils/markdown-to-docx.ts` — DOCX 변환 ### Step D: 서비스 계층 9. `src/main/services/MeetingDocTemplateService.ts` — 신규 10. `src/main/services/MeetingModeService.ts` — 확장 (7 메서드 + 후처리 변경 + _loadSessionDetail) ### Step E: IPC + Preload 11. `src/main/ipc/meeting-doc-template-handlers.ts` — 신규 12. `src/main/ipc/meeting-mode-handlers.ts` — 확장 13. `src/main/ipc/index.ts` — import 추가 14. `src/preload/index.ts` — API 확장 ### Step F: UI 컴포넌트 15-22. `src/renderer/components/meeting/` — 8개 컴포넌트 ### Step G: 페이지 통합 23. `src/renderer/pages/MeetingModePage.tsx` — 상세 뷰 리디자인 + 녹음 뷰 편집 ### Step H: i18n 24. 12개 locale 키 추가 ### Step I: 마무리 25. typecheck + build + test 26. 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 |