← 프로젝트 목록

Kitsch Labs · Shipped Desktop Product

Mora

Kitsch Labs에서 출시한 데스크톱 음성 입력 제품 Mora입니다. 전역 단축키로 말하면 문장을 정리해 현재 앱의 커서 위치에 바로 입력하며, macOS와 Windows용 v0.1.0을 배포했습니다.

담당
Kitsch Labs · Desktop, realtime STT API와 cross-repository integration
기술
Tauri 2 · React · Rust · FastAPI · WebSocket · Soniox

Actual product

실제 동작

설정 화면만 보여 주는 대신, 단축키로 녹음을 시작해 현재 사용 중인 앱에 문장이 입력되는 흐름을 먼저 확인할 수 있습니다.

01

현재 앱에 바로 입력하는 받아쓰기

Codex 위에서 Fn으로 녹음을 시작하고 완료하면, 확정된 한국어 문장이 기존 커서 위치에 한 번만 삽입됩니다.

Fn push-to-talk · transcript finalization · focused-field insertion
02

Mora 데스크톱 홈과 설정

사용량과 Fn 단축키, 입력 마이크, 받아쓰기 언어, 문장 정리 방식을 한 화면에서 확인하고 개인화 기능으로 이동할 수 있습니다.

사용량, Fn 단축키, 입력 마이크와 받아쓰기 설정을 보여 주는 Mora 데스크톱 앱 홈 화면
Mora Desktop 홈 화면
Kitsch STT client에서 FastAPI STT runtime, Soniox realtime WebSocket과 Flow Supabase로 이어지는 저장소 Mermaid 아키텍처
KitschLabs/kitsch-tts-api · docs/10_architecture.md (Mermaid)저장소의 Speech to Text runtime diagram을 그대로 렌더링했습니다.
01

Context

Mora는 별도의 채팅창에 전사문을 복사하는 도구가 아니라, 어느 앱에서든 전역 단축키로 녹음을 시작하고 현재 커서 위치에 결과를 돌려주는 데스크톱 제품입니다. 따라서 음성 인식 정확도만큼 microphone permission, global shortcut, focused-field insertion과 앱 포커스를 빼앗지 않는 overlay가 중요했습니다.

Desktop, Flow Web, 공용 Kitsch Voice API가 서로 다른 저장소와 데이터 경계를 가집니다. 기존 Kitsch 사용자의 인증과 사용량 경로를 깨뜨리지 않으면서 Flow JWT, 전용 Supabase entitlement와 transcript 저장을 실시간 STT 경로에 연결해야 했습니다.

02

Engineering

Tauri 2 Desktop의 Rust session controller가 전역 단축키, microphone capture, 24 kHz mono PCM 정규화와 입력 삽입을 소유합니다. React는 포커스를 빼앗지 않는 overlay와 설정 화면을 표시하고, streaming client는 짧은 Flow JWT로 FastAPI WebSocket에 연결합니다.

FastAPI STT runtime은 검증된 product context에 따라 Flow와 기존 Kitsch 서비스 경로를 분리한 뒤 Soniox Realtime WebSocket을 호출합니다. planned transport rollover에서도 logical session ID를 유지하고, finalized segment와 terminal completion을 구분해 여러 segment를 하나의 전사 결과로 조립합니다. 원본 audio는 저장하거나 연결이 끊긴 구간을 재전송하지 않습니다.

내가 맡은 일

  • Desktop의 audio capture, authenticated WebSocket client, rollover와 terminal completion 조립을 구현했습니다.
  • FastAPI에 Flow realtime STT orchestration, 전용 repository와 session close·transcript persistence 경로를 연결했습니다.
  • JWT issuer와 product context를 검증한 뒤에만 Flow/Kitsch 데이터 경로를 선택하도록 인증 경계를 설계했습니다.
  • Desktop과 API 저장소의 lifecycle 테스트를 작성하고 Stop·rollover 경합, 연결 종료와 provider 오류를 회귀 사례로 고정했습니다.

주요 기술 판단

인증 후에만 제품과 데이터 경로 결정

client가 보낸 tenant 이름이나 검증 전 issuer로 DB를 고르지 않습니다. JWT signature, issuer, audience와 session claim을 확인한 뒤 typed product context를 만들고, 그 결과로 Flow 전용 repository와 entitlement를 선택했습니다.

segment 완료와 session 완료를 분리

planned rollover가 만든 중간 segment는 terminal=false로 누적하고, server-side session close 뒤 terminal=true가 도착해야 한 번만 입력합니다. Stop이 rollover와 겹쳐도 같은 logical session으로 terminal commit을 보내도록 했습니다.

연결이 끊긴 audio는 재생성하지 않음

disconnected gap을 임의로 replay하면 중복 전사와 사용량 왜곡이 생깁니다. bounded PCM만 전송하고 끊긴 구간은 버리며, raw audio는 기본적으로 저장하지 않는 경계를 유지했습니다.

03

Validation

Desktop의 deterministic tests는 bounded audio delivery, PCM normalization, same-session rollover, disconnected-gap drop, terminal completion과 cancellation을 다룹니다. API tests는 Flow/Kitsch 인증 격리, entitlement checkpoint, transcript persistence, provider cleanup과 session close의 idempotency를 확인합니다.

macOS 데모에서 Fn 녹음 overlay와 실제 입력창 삽입을 확인했고, v0.1.0 설치 파일을 macOS와 Windows용으로 공개했습니다. 아직 운영 성능 수치는 충분히 검증하지 않아 포트폴리오에 넣지 않았습니다.

확인한 결과

  • 전역 단축키로 녹음한 결과가 현재 앱의 기존 커서 위치에 한 번만 입력되는 전체 사용자 흐름을 연결했습니다.
  • Desktop과 API 양쪽에서 rollover, 정상 종료, 연결 중단과 취소를 서로 다른 상태로 다루는 회귀 테스트를 남겼습니다.
  • 제품 웹사이트와 macOS·Windows용 v0.1.0 설치 파일을 공개해 실제 사용 가능한 배포 흐름까지 완성했습니다.

실패와 변경

  • audio source가 먼저 닫히면 stream finalization까지 기다리지 못하고 마지막 결과를 놓치는 경우가 있었습니다. transport 종료와 transcript terminal completion을 분리하고 terminal event를 기다리도록 수정했습니다.
  • Stop과 planned rollover가 겹치면 새 transport가 중간 segment만 남기고 끝날 수 있었습니다. 같은 logical session ID로 다시 연결해 terminal commit을 보내고, combined transcript를 한 번만 확정하도록 바꿨습니다.

추후 발전 방향

  • 물리 Windows 환경에서 microphone permission, global shortcut 충돌, focused-field insertion과 secure storage를 반복 검증하고 자동 업데이트 경로를 더 단단하게 만들 수 있습니다.
  • 네트워크 손실, JWT 만료, provider timeout과 사용 가능 시간 소진을 실제 격리 환경에서 end-to-end로 검증하고, reconnect 이후 중복 전사와 누락을 관찰하는 운영 지표를 추가할 수 있습니다.