# 프론트엔드 UX 개선 - 글로벌 키보드 포커스 **날짜**: 2025-08-07 **작업자**: happybell80 & Claude **관련 서비스**: frontend-customer ## 오후 7시 25분 ### 글로벌 키보드 포커스 관리 구현 **사용자 요구사항**: - 채팅 입력창 클릭 없이 타이핑 가능 - 어디서든 Ctrl+V로 붙여넣기 - Discord/Slack 같은 UX 구현 **구현 내용**: ```javascript // chat-interface.tsx:58-126 useEffect(() => { const handleGlobalKeyDown = (e: KeyboardEvent) => { // 안전 조건 체크 const target = e.target as HTMLElement; // 이미 입력 필드면 무시 if (['INPUT', 'TEXTAREA', 'SELECT'].includes(target.tagName)) return; // contentEditable, 버튼, 링크면 무시 if (target.contentEditable === 'true') return; if (['BUTTON', 'A'].includes(target.tagName)) return; // 모달/드롭다운 열려있으면 무시 if (document.querySelector('[role="dialog"], [role="menu"]')) return; // 네비게이션 키는 무시 (Tab, Arrow 등) if (['Tab', 'ArrowUp', ...].includes(e.key)) return; // Ctrl+V 붙여넣기 if (e.ctrlKey && e.key === 'v') { e.preventDefault(); textareaRef.current?.focus(); navigator.clipboard.readText().then(text => { // 클립보드 내용 붙여넣기 }); return; } // 일반 문자 입력 시 자동 포커스 if (e.key.length === 1) { textareaRef.current?.focus(); } }; document.addEventListener('keydown', handleGlobalKeyDown); return () => document.removeEventListener('keydown', handleGlobalKeyDown); }, [input, setInput]); ``` ### 안전 장치 1. **다른 입력 필드 방해 안 함** - INPUT, TEXTAREA, SELECT 태그 체크 - contentEditable 요소 체크 2. **인터랙티브 요소 보호** - 버튼, 링크 클릭 동작 보장 - 모달, 드롭다운 메뉴 정상 동작 3. **네비게이션 키 보존** - Tab, Arrow, Home, End 등 - 페이지 스크롤 키 정상 동작 4. **브라우저 단축키 보호** - Ctrl+F (검색) 등 보존 - 기능키 (F1-F12) 정상 동작 ### 브라우저 권한 이슈 **클립보드 API 권한**: - `navigator.clipboard.readText()` 사용 - Chrome이 클립보드 읽기 권한 요청 - 사용자가 허용해야 Ctrl+V 동작 **대안 비교**: | 방법 | 권한 | 작동성 | 상태 | |------|------|--------|------| | navigator.clipboard | 필요 | 100% | 권장 | | document.execCommand | 불필요 | 10% | deprecated | | 포커스만 이동 | 불필요 | 80% | 차선책 | ### 배포 **커밋 정보**: ```bash git commit -m "feat: 글로벌 키보드 포커스 관리 구현 - 페이지 어디서든 타이핑 시 자동으로 입력창 포커스 - Ctrl+V 붙여넣기 지원 (클립보드 권한 필요) - 안전 조건: 다른 입력필드, 버튼, 모달 열림 시 동작 안 함 - 네비게이션 키는 정상 동작 유지" ``` **Gitea Actions 자동 배포**: - frontend-customer → main 브랜치 - 51123 서버 자동 배포 ## 교훈 1. **글로벌 이벤트 리스너 설계** - 예외 조건을 먼저 정의 - 기존 UX 방해 최소화 - 접근성 표준 준수 2. **클립보드 보안 정책** - 브라우저별 권한 정책 차이 - 사용자 명시적 동의 필요 - deprecated API 사용 지양 3. **UX 개선 우선순위** - 편의성 < 예측 가능성 - 자동화 < 사용자 제어권 - 단순함 > 복잡한 기능 4. **크로스 브라우저 호환성** - Chrome: 클립보드 권한 팝업 - Firefox: 다른 권한 정책 - Safari: 더 엄격한 제한 ## 다음 개선 사항 - [ ] 설정에서 on/off 토글 제공 - [ ] 시각적 포커스 표시기 추가 - [ ] ~~모바일 터치 키보드 대응~~ (UX 해침) ## 오후 7시 30분 ### 모바일 지원 검토 결과 **문제 발견**: - 모바일에서 글로벌 키보드 포커스 기능 작동 안 함 - 가상 키보드로 인한 `keydown` 이벤트 미발생 **모바일 특성 분석**: 1. **이벤트 차이** - PC: `keydown`, `keyup` 이벤트 - 모바일: `touchstart`, `touchend` 이벤트 - 가상 키보드는 물리 키보드와 다른 이벤트 체계 2. **포커스 동작** - 모바일 브라우저의 자동 포커스 제한 - 가상 키보드 자동 팝업 방지 정책 3. **UX 고려사항** - 화면 터치 시 자동 포커스 → 키보드 계속 올라옴 - 스크롤, 스와이프 동작 방해 - 작은 화면에서 키보드가 차지하는 공간 문제 **구현 안 하기로 결정**: ```javascript // 가능한 구현 (하지만 UX 해침) document.addEventListener('touchstart', (e) => { if (!['INPUT', 'TEXTAREA'].includes(e.target.tagName)) { textareaRef.current?.focus(); // 키보드 계속 올라옴 } }); ``` **결론**: - PC: 글로벌 키보드 포커스 유지 ✅ - 모바일: 기존 방식 유지 (직접 터치) ✅ - 모바일은 터치 인터페이스가 더 자연스러움 ## 교훈 1. **플랫폼별 UX 차별화** - PC와 모바일은 다른 인터랙션 패턴 - 같은 기능이라도 플랫폼에 맞게 조정 - 모든 기능을 동일하게 구현할 필요 없음 2. **모바일 UX 원칙** - 가상 키보드는 필요할 때만 - 자동 포커스는 신중하게 - 터치 제스처 우선 3. **기능 구현 vs UX** - 기술적으로 가능 ≠ 구현해야 함 - 사용자 경험이 최우선 - 때로는 안 하는 것이 더 나은 선택 --- ## 2025-09-12 IME 충돌 문제 발견 및 해결 ### 문제 발생 글로벌 키보드 포커스 구현 1개월 후 한글 입력 버그 발견: - **증상**: "로빙" 입력 시 "f→ㅗ→로빙" 또는 "ㅗ빙" 출력 - **원인**: keydown 이벤트에서 즉시 focus() 호출이 IME 조합 과정 방해 ### 시도한 해결책들 **1차 시도 - composition 이벤트 처리** ```javascript if (e.isComposing || isComposing) return; // IME 입력 중 체크 ``` - 결과: 속도 개선되었으나 첫 글자 영문 출력 지속 **2차 시도 - setTimeout 지연** ```javascript setTimeout(() => textareaRef.current?.focus(), 0); ``` - 결과: 첫 자음 누락 ("ㄹ" 사라짐) 새로운 문제 발생 **3차 시도 - textarea 직접 이벤트** ```javascript onCompositionStart={() => setIsComposing(true)} onCompositionEnd={() => setIsComposing(false)} ``` - 결과: 부분 개선, 하지만 완벽하지 않음 ### 근본적 깨달음 - **핵심 문제**: 글로벌 키보드 캡처와 IME는 본질적으로 충돌 - **업계 표준**: "/" 키 같은 제한적 단축키만 사용 (Discord, Slack도) - **브라우저 한계**: IME 조합 중 DOM/포커스 변경 시 조합 깨짐 ### 최종 해결 - 완전 제거 ```javascript // 글로벌 키보드 이벤트 90줄 삭제 // 대신 간단한 autoFocus 사용