말하는 대로
대본이 따라 흐른다
음성 인식으로 대본이 자동 스크롤되는 텔레프롬프터. 폰·키보드·오퍼레이터가 한 화면을 같이 몰고, 네트워크가 끊겨도 대본은 기기에서 그대로 열린다. 카메라 녹화와 맞춤법 AI 교정까지 한 앱에 있다.

소개 화면과 프롬프터 화면
// the brief
텔레프롬프터는 '읽어주는 속도'와 싸운다
대부분의 텔레프롬프터는 정해진 속도로 대본을 올린다. 그러면 사람이 기계 속도에 자기를 맞춰야 한다. 조금 빨라지거나 한 박자 쉬면 대본이 저 혼자 앞서가고, 그걸 따라잡느라 말이 흔들린다. 발표·촬영처럼 다시 못 찍는 순간일수록 이 어긋남이 치명적이다.
현장의 조건도 까다롭다. 발표자·오퍼레이터·리모컨이 한 대본을 동시에 제어해야 하고, 대본은 보통 PDF나 워드 파일로 온다. 촬영장 와이파이는 믿을 게 못 되고, 녹화물은 폰에서 바로 저장돼야 한다. '목소리로 따라 흐르는 스크롤' 하나만 되면 되는 게 아니라, 이 모든 게 안 깨지고 맞물려야 쓸 수 있는 도구가 된다.
// approach
접근
기성 프레임워크로 얹기보다, 이 문제 전용으로 엔진을 설계했다. 핵심은 스크롤 속도를 사람에게 맞추는 것. 말하는 위치를 실시간으로 문서 좌표에 붙여 그 지점이 항상 화면 시선 라인에 오도록 대본을 흘린다. React 19 + Vite로 화면을, Hono(CF Worker)·D1로 백엔드를, Dexie로 오프라인 저장을 맡겼다.
속도를 내는 데는 AI 도구를 적극적으로 엮었다. 다만 무엇을 만들고 어디서 멈출지, 현장에서 안 깨지는 경계가 어디인지에 대한 판단과 마감은 전부 사람이 쥐었다. 실사용자가 있는 프로덕션 앱이라, 지오메트리·동기화처럼 빌드 통과만으로는 안 잡히는 부분은 헤드리스 브라우저로 실측하고 순수 로직은 회귀 테스트로 못 박았다.
속도를 사람에게 맞춘다. 사람이 기계에 맞추는 게 아니라.
// key decisions
핵심 결정
멀티 센더는 토글이 아니라 멱등 명령으로
라이브 현장에선 발표자 폰·오퍼레이터 노트북·키보드가 한 대본을 같이 제어한다. PLAY_PAUSE 같은 토글 명령을 쓰면 두 사람이 거의 동시에 누를 때 서로 상쇄돼 상태가 뒤집힌다. 그래서 원격으로 보내는 재생·정지를 PLAY/PAUSE 같은 절대 액션(멱등)으로 설계했다. 몇 번을 누가 눌러도 결과가 같다. 오퍼레이터가 개입하면 4초 창 동안 음성 추적을 눌러뒀다가, 만료되면 현재 시선 위치에서 다시 이어받아 '두 운전자'가 싸우지 않게 했다.
유료 STT는 무료 티어 밖에만 둔다
서버형 STT(종량 과금)를 무료 사용자에게까지 열면 사용자가 늘수록 비용이 커져 무료 티어를 유지할 수 없다. 그래서 무료 경로는 브라우저에 내장된 음성 인식만 쓰고, 서버형 엔진은 Pro 전용 '정밀 음성 인식'으로 둔다. 음성 입력 지점을 matcher.feed() 하나의 시임으로 좁혀 둔 덕에, 새 엔진을 브라우저 인식기와 같은 모양으로 감싸 꽂자 매처와 워치독·재시작 처리를 고치지 않고 그대로 쓸 수 있었다. 자격과 월 분량은 서버가 확인해 브라우저에는 15분짜리 1회용 토큰만 주고, 거부되거나 켜 둔 채 자리를 비워 3분 넘게 인식 결과가 없으면 안내 한 줄과 함께 기본 인식으로 내려온다.
네트워크가 끊겨도 대본은 안 잃는다
촬영장 네트워크는 믿을 게 못 된다. 그래서 Dexie로 기기 안에 먼저 저장하고 연결되면 LWW로 동기화하는 오프라인 우선 구조로 잡았다. 편집 자동저장은 화면을 떠나는 순간 마지막 입력까지 흘려보내고, 동기화는 실제로 써진 문서만 '반영됨'으로 돌려보내며, 큰 문서는 청크로 나눠 보낸다.
세 결정의 공통점은 화려한 기능이 아니라 '현장에서 안 깨지는 것'이다. 텔레프롬프터는 생방송·촬영처럼 되돌릴 수 없는 순간에 쓰인다. 그 압박을 기준선으로 삼고 설계했다.
// voice
음성 추적 엔진: 목소리를 문서 좌표에 붙이는 법
음성 추적의 진짜 어려움은 '인식'이 아니라 '지금 어디를 읽고 있나'를 문서 위 좌표로 정확히 잡는 데 있다. 다음 문단에 우연히 비슷한 세 단어가 나오면 스크롤이 엉뚱한 데로 낚아채여 발표자를 놓친다. 그래서 평상시엔 전방 점프를 12단어로 캡하고, 정말로 건너뛴 경우에만 별도 recovery 경로로 넓게 재배치하도록 나눴다. 매처는 순수 함수로 떼어내 Node에서 세 시나리오(우연한 앞 일치 무시·진짜 스킵 재획득·연속 추적 무흔들림)를 회귀 테스트로 지킨다.
17개 언어를 지원하려고 두 가지를 따로 설계했다. 하나는 띄어쓰기가 없는 일본어·중국어·태국어다. 이 문자들만 글자 하나를 낱말처럼 세어 붙이고, 한 글자짜리는 정확히 같을 때만 맞게 해 엉뚱한 일치가 끼어들 틈을 없앴다. 다른 하나는 숫자다. 대본의 '두 시'와 인식기의 '2시', 'twenty-four'와 '24'가 만나도록 수를 쓴 낱말을 값으로 접는데, 언어별로 표를 고르지 않고 대본과 인식 결과 양쪽에 같은 접기를 건다. 양쪽이 같이 접히니 과하게 접혀도 한쪽만 틀어지지 않고, 언어 판정이 한 번 틀려도 그 대본의 숫자가 한꺼번에 어긋나지는 않는다.
// billing
돈을 받기 시작하면 규칙이 달라진다
무료로 쓰던 도구에 유료 플랜을 붙이는 순간, 지켜야 할 것이 코드 밖으로 늘어난다. 결제·환불·갱신 안내·회원 탈퇴는 약관에 적은 대로 실제로 동작해야 하고, 그건 기능이 아니라 약속이다.
정기결제는 토스페이먼츠로 붙였고, 웹훅은 요청 본문을 믿지 않고 토스에 되물어 검증한다. 갱신 예정 안내는 앱 안에서만이 아니라 앱을 안 여는 사람에게도 닿게 했다. 환불은 약관에 적은 대로 실제로 실행되도록 경로를 따로 만들었고, 회원 탈퇴도 약관이 말한 대로 앱 안에서 끝난다. 연 99,000원을 낸 지 한 달 된 사람이 탈퇴하면 남은 기간이 사라진다는 사실을 먼저 알리고 확인받는다.
이 영역은 실수의 대가가 다르다. 그래서 배포 게이트가 결제·인증 스위트를 통째로 돌리고, 결제 키가 잘못 박힌 빌드는 배포 자체를 거부한다. 요금·환불 규정은 계정을 만들지 않아도 볼 수 있어야 해서 로그인 밖에 뒀다.
iOS 앱이 나오면서 결제 경로가 둘이 됐다. 웹은 우리가 토스로 청구하지만, App Store 구독은 애플이 청구하고 우리는 상태를 볼 뿐이다. 그래서 두 경로를 섞지 않는다. 서버의 권한 판정은 앱이 넘긴 거래 정보도 애플의 서버 알림도 '가서 확인하라'는 신호로만 쓰고, 애플에 직접 되물은 결과로만 한다. 해지·환불·결제 재시도 안내도 구매한 곳 기준으로 나눴고, App Store 구독은 계정을 지워도 해지되지 않아 탈퇴 전에 그 사실부터 알린다.
// result
결과
frompter.com에 라이브로 운영 중이고, App Store 에도 출시했다(2026-10-03 · 17개 언어). 발표·촬영 현장에서 목소리로 대본을 몰고, 폰이나 애플워치를 리모컨 삼아 넘기고, 네트워크 없이도 쓴다. PDF·워드 대본을 그대로 가져와 바로 올리고, 녹화물은 iOS 설치형 PWA의 저장 제약까지 감안해 공유 시트로 내보낸다. 로그인은 패스키(WebAuthn)로 간편하게, 데이터는 기기와 클라우드가 조용히 맞춰진다. 핵심 기능은 계속 무료로 두고, 정밀 음성 인식처럼 쓸수록 비용이 드는 것과 고화질 녹화·2차 화면 출력 같은 제작용 기능을 Pro(연 99,000원)로 나눴다. 케이스를 처음 쓴 뒤로도 개선이 계속 쌓였고, 지금도 실사용 속에서 다듬어지고 있다.
// 이런 걸 만들어 드립니다.