콘텐츠로 이동

macOS 보이스 오버레이 라이프사이클 관리하기

음성 명령 기능을 개발하다 보면 여러 입력 방식이 충돌해서 사용자 경험이 꼬이는 경우가 자주 발생해요. 예를 들어 wake-word로 음성 인식을 시작했는데 중간에 사용자가 단축키(push-to-talk)를 누르면, 기존에 인식되던 텍스트가 사라지거나 오버레이가 제멋대로 닫히는 식이죠. 이런 상태 관리의 복잡함은 개발자를 참 힘들게 만듭니다.

macOS 앱에서 wake-word와 push-to-talk이 겹칠 때 보이스 오버레이를 안정적으로 유지하는 최적의 구현 방법을 정리해 드릴게요.

  • macOS 앱 컨트리뷰터 권한

오버레이의 예측 가능성을 높이기 위해 5분 안에 적용할 수 있는 핵심 단계입니다.

  1. VoiceSessionCoordinator (actor) 도입 한 번에 딱 하나의 VoiceSession만 관리하도록 설계하세요. 토큰 기반 API(beginWakeCapture, beginPushToTalk, endCapture 등)를 사용해 오래된 콜백이 오버레이를 다시 여는 현상을 방지합니다.

  2. 세션 채택(Adoption) 로직 적용 wake-word로 오버레이가 이미 떠 있는 상태에서 사용자가 단축키를 누르면, 세션을 초기화하는 대신 기존 텍스트를 그대로 가져옵니다. 단축키를 떼기 전까지 오버레이를 유지하고, 텍스트가 있으면 전송하고 없으면 닫습니다.

  3. VoiceSessionPublisher로 SwiftUI 연결 ObservableObject인 VoiceSessionPublisher가 활성 세션의 상태를 SwiftUI 뷰(VoiceWakeOverlayView)에 전달하게 하세요. 뷰가 직접 싱글톤을 수정하지 않고 퍼블리셔를 통해서만 렌더링되도록 구성합니다.

  4. 통합된 전송 경로(Unified send path) 사용 endCapture 시점에 텍스트가 비어 있으면 즉시 dismiss하고, 텍스트가 있다면 performSend를 호출합니다. push-to-talk 종료 후에는 짧은 cooldown을 적용해 wake-word가 즉시 재트리거되지 않도록 관리하세요.

오버레이가 화면에서 사라지지 않거나 동작이 이상할 때는 다음 명령어로 로그를 확인해 보세요.

Terminal window
sudo log stream --predicate 'subsystem == "bot.molt" AND category CONTAINS "voicewake"' --level info --style compact

체크리스트:

  • 활성 세션 토큰이 하나만 유지되는지 확인하세요. 오래된 콜백은 coordinator에서 드랍되어야 합니다.
  • push-to-talk을 뗐을 때 항상 활성 토큰과 함께 endCapture가 호출되는지 확인하세요. 텍스트가 없다면 전송음 없이 dismiss되어야 정상입니다.

도움이 더 필요하신가요? AI Setup Assistant에서 바로 물어보세요.

  • VoiceSession 모델 상세 구조 보기
  • VoiceWakeRuntime 리팩토링 가이드
  • VoicePushToTalk 세션 채택 로직 구현
  • 오버레이 바인딩 및 테스트 케이스 추가
OpenClaw

OpenClaw Expert

아직 막혀 있나요?

이 문서에서 답을 못 찾았다면 OpenClaw Expert에게 바로 물어보세요.