fix(desktop): 한글 IME Enter 중복 addMemo 가드 + Meeting pre-push 직접 실증 (빅뱅 Phase 5 Part 4)

Phase 5 Part 3에서 남겨둔 U2(Fix 1 직접 시연 미수행)를 해소하고,
실증 중 발견한 한글 IME Enter 중복 addMemo 버그(Bug 10)를 같이 픽스.

## Bug 10 — 한글 IME Enter 중복 addMemo
- 증상: 실증 녹음 중 한글 메모 "메모 기능이 잘 작동하는지 봅니다"가
  2ms 간격으로 2번 저장 (로그 21:47:57.410/.412, UUID c7f0db45 + a6e442cc).
  5개 메모 중 이 하나만 중복 — 조합이 아직 확정 안 된 상태에서 Enter를
  친 메모에서만 발생.
- 원인: 한글 IME 조합 중 Enter → Chromium이 keydown 2번 발화
  ((1) IME 조합 확정 isComposing=true/keyCode=229,
   (2) 실제 Enter 액션). MeetingModePage.tsx:164-174의 handleMemoKeyDown이
  e.key === 'Enter' && !e.shiftKey만 검사하고 isComposing을 무시해
  두 이벤트에서 모두 handleAddMemo() 발화 → IPC 2회 → addMemo() 2회.
- 수정: MeetingModePage.tsx:168에 이중 가드 추가.
  isComposing은 모던 표준, keyCode === 229는 조합 확정 시점에
  isComposing이 false로 내려가면서 keyCode만 229로 주는 Chromium
  엣지 케이스 호환.
- 검증: Vite HMR로 즉시 반영 후 재시연 녹음(78421e75)에서 한글 메모
  5건 연속 → 전원 1:1 매핑, 중복 0.

## Fix 1 (Phase 3.3 startRecording pre-push) 직접 실증
재시연 녹음 78421e75 (21:51:24~21:51:50, 약 26초) 로그 타임라인:

  21:51:24.937  pushOne meetings/78421e75 ok      <- Fix 1 pre-push (676ms)
  21:51:30.499  pushOne meeting_memos/4ed7e59a ok <- addMemo #1 (113ms)
  21:51:32.202  pushOne meeting_memos/de011c15 ok <- addMemo #2 (78ms)
  21:51:35.893  pushOne meeting_memos/3184fe5e ok <- addMemo #3 (66ms)
  21:51:38.263  pushOne meeting_memos/00f041ef ok <- addMemo #4 (82ms)
  21:51:40.740  pushOne meeting_memos/0808c4d2 ok <- addMemo #5 (64ms)
  21:51:50.681  pushOne meetings/78421e75 ok      <- _runPostProcessing (108ms)
  21:51:50.709  pushOne history/70388090 ok       <- CaptionService→History

meetings/78421e75가 녹음 시작 + 종료 두 번 push된 것이
Fix 1의 직접 증거 — 부모 row가 메모 push 시점에 이미 Supabase에
존재했기 때문에 메모 5건이 RLS/FK 차단 없이 통과. Bug 7 재현 방지 확인.

## 잠재 IME 버그 7곳
동일 패턴의 Enter 핸들러 7곳에 가드 없음 (KnowledgeBasePage:85,
VoiceConversationPage:114, MeetingChatPanel:113, EditableSegment:72,
MeetingDetailTabs:242, HistoryEntryCard:65, CommandsPage:337).
다음 세션 /simplify 패스에서 일괄 처리 예정.

## 검증
- desktop tsc --noEmit EXIT=0
- Meeting 녹음 2회차 실증 통과

memory/project_status.md SaaS [11] 섹션 추가.
This commit is contained in:
윤찬 2026-04-11 21:58:20 +09:00
parent 2a8efa81af
commit 96e23f57bc
2 changed files with 57 additions and 0 deletions

View file

@ -163,6 +163,9 @@ export function MeetingModePage(): React.ReactElement {
const handleMemoKeyDown = useCallback( const handleMemoKeyDown = useCallback(
(e: React.KeyboardEvent) => { (e: React.KeyboardEvent) => {
// 한글 IME 조합 중 Enter는 keydown을 2번 발화(조합 확정 + 실제 Enter)하므로
// isComposing=true인 이벤트는 무시해 addMemo 중복 호출 방지
if (e.nativeEvent.isComposing || e.keyCode === 229) return
if (e.key === 'Enter' && !e.shiftKey) { if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault() e.preventDefault()
handleAddMemo() handleAddMemo()

View file

@ -208,6 +208,60 @@ OAuth provider도 아직 Supabase에 설정 안 된 상태. 강제 게이트는
- `~/Library/Application Support/d3ro-voice/users/${uuid}/d3ro.db` 파일 생성 확인 - `~/Library/Application Support/d3ro-voice/users/${uuid}/d3ro.db` 파일 생성 확인
- 기존 `d3ro-voice.db`가 있는 환경에서 archive rename 동작 확인 - 기존 `d3ro-voice.db`가 있는 환경에서 archive rename 동작 확인
### SaaS [11] Meeting pre-push 직접 실증 + IME Enter 중복 addMemo 픽스 (Phase 5 Part 4, 2026-04-11)
> **U2 해소.** Meeting 모드 녹음을 직접 돌려 Fix 1(startRecording pre-push) + addMemo hook을 실시간 로그로 증명. 부수적으로 발견된 한글 IME Enter 중복 addMemo 버그(Bug 10)를 `isComposing` 가드로 픽스.
**직접 실증 타임라인 (session 78421e75, 21:51:24~21:51:50)**
```
21:51:24.937 pushOne meetings/78421e75 ok ← Fix 1 pre-push (813ms after startRecording)
21:51:30.499 pushOne meeting_memos/4ed7e59a ok ← addMemo #1 "메모가"
21:51:32.202 pushOne meeting_memos/de011c15 ok ← addMemo #2 "두번씩"
21:51:35.893 pushOne meeting_memos/3184fe5e ok ← addMemo #3 "입력되는걸 막아야합니다."
21:51:38.263 pushOne meeting_memos/00f041ef ok ← addMemo #4 "잘되는지 봅시다."
21:51:40.740 pushOne meeting_memos/0808c4d2 ok ← addMemo #5 "잘되는 것 같네요."
21:51:50.681 pushOne meetings/78421e75 ok ← _runPostProcessing hook
21:51:50.709 pushOne history/70388090 ok ← CaptionService → HistoryService.create
```
- **meetings/78421e75가 녹음 시작 + 종료 두 번 push** → Fix 1 완전 증명 (부모 row가 메모 push 시점에 이미 Supabase에 존재해서 자식 RLS/FK 통과)
- **메모 5건 전부 100ms 내 1:1 push** → addMemo hook 정상, RLS 에러 0
- **history push 출처 확인**: `CaptionService.ts:459`에서 회의 전사가 완료되면 전체 텍스트를 HistoryService에도 저장하는 기존 경로. Phase 3.3 훅이 `HistoryService.create()` 끝에 걸려 있어 자동으로 따라옴. 의도대로 동작.
**발견+픽스한 버그 — Bug 10: 한글 IME Enter 중복 addMemo**
- 증상: 첫 번째 시연 녹음(bd7f2367)에서 "메모 기능이 잘 작동하는지 봅니다"가 **2ms 간격으로 2번** 저장됨 (UUID c7f0db45 + a6e442cc, 로그 21:47:57.410/.412). 다른 메모들은 1회.
- 원인: 한글 IME 조합 중 Enter 키는 Chromium이 `keydown`을 두 번 발화 — (1) IME 조합 확정용(`isComposing=true`, keyCode=229) + (2) 실제 Enter 액션. 기존 `handleMemoKeyDown`(MeetingModePage.tsx:164-174)은 `e.key === 'Enter' && !e.shiftKey`만 검사하고 `isComposing`을 무시해 두 이벤트 모두에서 `handleAddMemo()` 발화 → IPC 2회 → `MeetingModeService.addMemo()` 2회 → DB insert 2건 → `pushOne meeting_memos` 2회. 중복이 다른 메모에서 안 난 이유는 해당 메모들은 IME 조합이 이미 확정된 상태(공백/한자 변환 등)에서 Enter를 쳤기 때문.
- Fix: `MeetingModePage.tsx:168`에 이중 가드 추가. `if (e.nativeEvent.isComposing || e.keyCode === 229) return``isComposing`은 모던 표준, `keyCode === 229`는 Chromium 엣지 케이스(조합 확정 시점에 isComposing이 false로 내려가면서 keyCode만 229로 주는 경우) 호환.
- 검증: 두 번째 녹음(78421e75)에서 한글 메모 5건 연속 입력 → **전원 1:1 매핑**, 중복 0. Vite HMR로 즉시 반영, 별도 재기동 없이 fix 유효.
**잠재 IME 버그 — 다른 Enter 핸들러 7곳 (다음 `/simplify` 때 일괄 처리 예정)**
| 파일 | 라인 | 용도 | 한글 위험도 |
|---|---|---|---|
| `KnowledgeBasePage.tsx` | 85 | RAG 쿼리 | 높음 |
| `VoiceConversationPage.tsx` | 114 | 텍스트 채팅 | 높음 |
| `components/meeting/MeetingChatPanel.tsx` | 113 | 미팅 챗 | 높음 |
| `components/meeting/EditableSegment.tsx` | 72 | 전사 세그먼트 편집 | 높음 |
| `components/meeting/MeetingDetailTabs.tsx` | 242 | 미팅 타이틀 | 중간 |
| `components/shared/HistoryEntryCard.tsx` | 65 | 태그 추가 | 중간 |
| `CommandsPage.tsx` | 337 | 키워드 추가 | 중간 |
(LicenseModal.tsx:196은 ASCII 전용이라 제외)
**검증**
- desktop `tsc --noEmit` ✅ (EXIT=0)
- Meeting 모드 녹음 1회차(bd7f2367): Fix 1 + addMemo hook 증명, IME 중복 1건 발견
- Meeting 모드 녹음 2회차(78421e75): IME fix 적용 후 메모 5건 전부 1:1 ✅
- Realtime 채널 상태: 여전히 U1 TIMED_OUT (블로커 아님, Phase 3.3 auto push가 대체 경로)
**빅뱅 성공 기준 상태 — 이번 세션 갱신 없음** (8개 중 7개 완료, Phase 3.2 PREMIUM_LLM만 잔존)
**다음 세션 권장 액션**
- `/simplify` 실행 — Phase 3.3 + Bug 10 fix 코드 품질 리뷰 + 위 7개 IME Enter 핸들러 일괄 가드 추가
- Phase 3.2 PREMIUM_LLM Edge Function 경로 (Anthropic/OpenAI key 주입 필요, 사용자 의사결정 대기)
- U1 Realtime TIMED_OUT 원인 조사 (선택)
- `package-lock.json` optional dep 정리 (Phase 5 Part 2부터 이월)
---
### SaaS [10] Phase 3.3 Auto push on write + 웹 transcript fallback + Dashboard Link 픽스 (Phase 5 Part 3, 2026-04-11) ### SaaS [10] Phase 3.3 Auto push on write + 웹 transcript fallback + Dashboard Link 픽스 (Phase 5 Part 3, 2026-04-11)
> **수동 Push 버튼이 사라졌다.** 로컬 SQLite write가 일어나는 모든 경로(8곳)에서 `CloudSyncService.pushOne`이 fire-and-forget으로 즉시 실행되어 Supabase에 반영. 웹 Dashboard 새로고침 한 번이면 방금 녹음한 미팅/메모/전사까지 전부 보인다. 이번 세션 한 번에 end-to-end 실증까지 통과. > **수동 Push 버튼이 사라졌다.** 로컬 SQLite write가 일어나는 모든 경로(8곳)에서 `CloudSyncService.pushOne`이 fire-and-forget으로 즉시 실행되어 Supabase에 반영. 웹 Dashboard 새로고침 한 번이면 방금 녹음한 미팅/메모/전사까지 전부 보인다. 이번 세션 한 번에 end-to-end 실증까지 통과.