글

6월, 2026의 게시물 표시

음성봇을 스텁부터 만든 이유 — 테스트로 설계를 먼저 굳히다

시리즈 · 음성봇 직접 만들기 — 실시간 음성봇의 뼈대를 바닥부터 만든 8편 기록입니다. 1. 끼어들기(barge-in) — 재생 중인 TTS를 실시간으로 끊는 법 2. "말이 끝났다"는 판단 — VAD 엔드포인터 3. 엔진을 코드 수정 없이 바꾸는 인터페이스 설계 4. 로컬 LLM 대화 두뇌와 도구 호출 루프 5. 매 턴 지연시간을 재는 이유 6. 브라우저 마이크에서 서버까지 — 오디오 프로토콜 7. 로컬 Whisper STT 연결하기 8. 스텁부터 만든 이유 — 테스트 우선 설계 (완결) — 지금 보는 글 돌아보며 시작하는 마지막 편 이 시리즈를 시작할 때부터 정해 둔 규칙이 하나 있었습니다. 진짜 음성 인식 모델과 진짜 합성 모델이 준비되기 전까지는, 그 둘을 흉내만 내는 가짜 엔진으로 전체 구조를 먼저 돌려본다는 규칙입니다. 여덟 편을 거치며 끼어들기 처리, 상태 흐름, 프로토콜, 엔진 인터페이스를 하나씩 다뤘는데, 그 모든 것이 사실 이 가짜 엔진 위에서 검증됐습니다. 지금 코드를 다시 열어 보면 그때는 별생각 없이 세운 규칙이 결과적으로 시리즈 전체의 진행 속도를 결정했다는 게 눈에 들어옵니다. 이번 편에서는 왜 굳이 가짜부터 만들었는지, 그리고 그 선택이 시리즈 전체에 어떤 영향을 남겼는지 정리하면서 이 시리즈를 마무리하려 합니다. 실제 모델을 기다리지 않기로 한 이유 음성봇의 핵심 로직은 사실 음성 인식이나 음성 합성 자체가 아닙니다. 사용자가 말을 시작했는지 끝냈는지 판단하는 상태머신, 합성 중에 끼어들기가 들어왔을 때 스트림을 멈추는 동시성 처리, 이벤트가 순서대로 오가는 흐름 제어 같은 것들이 훨씬 까다롭습니다. 반면 실제 인식·합성 모델은 무겁고 설치가 까다롭고 실행 속도도 느립니다. 이 두 가지를 한꺼번에 붙잡고 있으면 어려운 동시성 버그를 실제 모델의 로딩 시간과 뒤섞어 디버깅하게 됩니다. 모델이 느려서 반응이 늦은 건지, 상태머신이 잘못 짜여서 늦은 건지 구분이 안 되는 상황을 미...

너무 느려서 내가 안 썼다 — 음성 앱 레이턴시를 온디바이스 STT로 잡은 방법

[시리즈] Voice To Notion 개발기 · 전체 8편 중 7편 1편 떠오른 생각을 Notion에 박제하고 싶었다 발행 2편 다 만들고도 제출조차 못 한 첫 버전 발행 3편 준비 중독, 그리고 마케팅 공포 발행 4편 그래서 나는 또 도망쳤다 — v2 전면 재작성 발행 5편 Claude Code로 앱을 다시 짓다 발행 6편 앱스토어 2회 반려가 진짜로 말한 것 발행 7편 너무 느려서 내가 안 썼다 — 온디바이스 STT 피벗 현재 편 8편 매출 $0에서 다시 정한 우선순위 발행 온디바이스 STT는 음성 앱의 속도 문제와 프라이버시 문제를 동시에 해결할 수 있는 카드입니다. 그런데 이 글에서 하려는 이야기는 온디바이스 STT가 얼마나 뛰어난 기술인가가 아닙니다. 이 시리즈에서 가장 부끄러운 발견, 바로 내가 만든 앱을 내가 쓰지 않고 있었다는 사실을 먼저 꺼내야 합니다. 6편을 올리고 나서 일주일쯤 지났을 때였습니다. 앱스토어 심사를 기다리는 동안 실제로 어떻게 메모를 남기고 있는지 무심코 들여다봤습니다. 사파리에 홈 화면 단축키로 추가해둔 간단한 받아쓰기 페이지가 화면에 떠 있었습니다. JustBlurt가 아니었습니다. 거기서 말하고, 텍스트를 복사해서, Notion에 붙여넣고 있었습니다. 3단계짜리 수작업으로요. 그게 훨씬 빠르다고 몸이 판단하고 있었던 것입니다. 이유는 단순했습니다. 제 앱이 너무 느렸습니다. 만든 사람조차 못 참을 정도로요. 이 글은 그 레이턴시를 어떻게 진단하고, 온디바이스 STT로 어떻게 피벗하기로 했는지의 기술 회고입니다(이 시리즈의 다른 글처럼, 실제 기록이되 서비스명과 세부 정보는 바꿔 씁니다). 느렸던 구조 당시 파이프라인의 전체 흐름을 먼저 짚겠습니다. 사용자가 녹음 버튼을 눌렀다 뗍니다. 그 순간부터 Notion에 내용이 저장되기까지의 경로가 중요합니다. 사용자가 마이크 버튼을 누르고 말을 합니다. 녹음이 끝난 음성 파일을 통째로 서버에 업로드 합니...

앱스토어 2회 반려가 진짜로 말한 것 — 심사가 코드 대신 사용자를 묻는 이유

[시리즈] Voice To Notion 개발기 · 전체 8편 중 6편 1편 떠오른 생각을 Notion에 박제하고 싶었다 발행 2편 다 만들고도 제출조차 못 한 첫 버전 발행 3편 준비 중독, 그리고 마케팅 공포 발행 4편 그래서 나는 또 도망쳤다 — v2 전면 재작성 발행 5편 Claude Code로 앱을 다시 짓다 발행 6편 앱스토어 2회 반려가 진짜로 말한 것 현재 편 7편 너무 느려서 내가 안 썼다 — 온디바이스 STT 피벗 발행 8편 매출 $0에서 다시 정한 우선순위 발행 앱스토어 심사 반려가 늘 제품 결함 때문은 아닙니다. 5편에서 적었듯이 v2는 v1보다 분명히 더 좋은 앱이 됐습니다. 위젯도 있고, 딥링크도 되고, 테스트도 단단했습니다. 그래서 저는 이번엔 다를 거라고 믿었습니다. 제출 버튼을 누른 날 밤, 저는 처음으로 심사 대기열을 새로고침하지 않았습니다. 예전 v1 때는 제출도 못 해봤고, 이번 v2는 기술적으로 흠잡을 데가 없었으니까요. Xcode 경고 0개, TestFlight 베타 사용자 0명이지만 단위 테스트 87개 통과, 메모리 프로파일링도 깨끗했습니다. 그래서 저는 심사 통보를 기다리는 대신 잠자리에 들었습니다. 앱스토어는 v2를 두 번 반려했습니다. 지금도 그 반려를 보완해 재제출을 준비하는 중입니다. 처음엔 억울했습니다. "이만큼 잘 만들었는데 뭐가 문제냐." 그런데 반려 사유를 차분히 곱씹어보면, 애플이 사실 제 앱이 아니라 제 전략 을 반려하고 있었습니다. 이 편에서는 두 번의 반려가 어떻게 왔는지, 각각 무엇을 물었는지, 그리고 제가 그것을 어떻게 잘못 읽었다가 어떻게 제대로 읽게 됐는지를 씁니다. 반려는 기술 결함이 아니었다 1차 반려 통보는 제출 후 사흘 만에 왔습니다. 화면에 "Your submission has been rejected"가 뜨는 순간, 저는 즉각 Xcode 콘솔을 켰습니다. 크래시 로그부터 찾으려 했...

Claude Code로 앱을 다시 짓다 — 1인개발자 에이전트 주도 개발 3원칙

[시리즈] Voice To Notion 개발기 · 전체 8편 중 5편 1편 떠오른 생각을 Notion에 박제하고 싶었다 발행 2편 다 만들고도 제출조차 못 한 첫 버전 발행 3편 준비 중독, 그리고 마케팅 공포 발행 4편 그래서 나는 또 도망쳤다 — v2 전면 재작성 발행 5편 Claude Code로 앱을 다시 짓다 현재 편 6편 앱스토어 2회 반려가 진짜로 말한 것 발행 7편 너무 느려서 내가 안 썼다 — 온디바이스 STT 피벗 발행 8편 매출 $0에서 다시 정한 우선순위 발행 4편을 쓰면서 "왜 다시 짰나"를 정리했습니다. 그 이유는 단순했습니다. v1이 제출도 못 한 채 잠든 사이, 저는 같은 손으로 v2를 다시 쌓기 시작했고, 이번에는 혼자가 아니었습니다. Claude Code 에이전트를 처음으로 진짜 동료처럼 쓴 것이 v2 재작성이었습니다. 이 글은 "어떻게 재작성했나"입니다. 구체적으로 무엇을 에이전트에게 맡겼고, 어떤 방식으로 지시했고, 혼자 할 때와 무엇이 달랐는지를 사건 순서대로 적겠습니다. 당시 저는 Claude Code를 터미널에서 직접 구동하는 방식으로 작업했습니다. 에디터 플러그인이 아니라 CLI입니다. 프로젝트 루트에서 claude 를 실행하면 에이전트가 코드베이스 전체를 컨텍스트로 가져갑니다. 저는 거기에 작업 지시를 텍스트로 넣었습니다. 지시가 명확할수록 결과가 빨랐고, 지시가 뭉뚱그려질수록 에이전트는 예상치 못한 방향으로 달려나갔습니다. 그 차이를 인식하는 데 초반 며칠이 걸렸습니다. 이 글은 자책이 아니라 도구 이야기입니다. 에이전트 주도 개발이 1인개발자에게 어떤 조건에서 진짜 곱셈기가 되는지, 그리고 어떤 조건에서 빠른 속도로 틀린 방향을 향하는지를 있는 그대로 적겠습니다. 무엇을 에이전트와 함께 짰나 말로만 "AI로 개발했다"는 흔합니다. 실제로 어떤 기능을 에이전트와 함께 만들었는지가 중요합니다. v2 ...

사용자도 없는데 앱을 다시 짰다 — SwiftUI에서 React Native 전면 재작성

[시리즈] Voice To Notion 개발기 · 전체 8편 중 4편 1편 떠오른 생각을 Notion에 박제하고 싶었다 발행 2편 다 만들고도 제출조차 못 한 첫 버전 발행 3편 준비 중독, 그리고 마케팅 공포 발행 4편 그래서 나는 또 도망쳤다 — v2 전면 재작성 현재 편 5편 Claude Code로 앱을 다시 짓다 발행 6편 앱스토어 2회 반려가 진짜로 말한 것 발행 7편 너무 느려서 내가 안 썼다 — 온디바이스 STT 피벗 발행 8편 매출 $0에서 다시 정한 우선순위 발행 앱 전면 재작성은 발전일 때도 있고 도망일 때도 있습니다. 3편에서 저는 코딩 뒤에 숨는 제 패턴을 자세히 진단했습니다. 노트 앱에 써둔 자가진단의 결론도 한 줄로 요약되어 있었습니다. "제품은 끝났다. 사람을 데려와라." 몇 주에 걸친 자기관찰 끝에 내린 결론이었고, 스스로도 옳다고 생각했습니다. 그리고 저는 정확히 그 반대로 했습니다. 사람을 데려오는 대신, 앱을 처음부터 다시 짜기로 결정했습니다. 그것도 언어와 플랫폼까지 갈아엎는 전면 재작성이었습니다. SwiftUI로 만든 iOS 네이티브 앱 JustBlurt v1을 버리고, Expo와 React Native 기반의 v2를 새로 시작했습니다. 이 글은 그 결정을 변명하지 않고 기록합니다. 이 시리즈의 다른 편들처럼 실제 겪은 일을 그대로 옮기되, 서비스명과 세부 식별 정보는 바꿔서 씁니다. 도망이라는 사실을 부정하지 않겠습니다. 하지만 도망 안에도 합리적 판단이 섞여 있었고, 그 둘이 어떻게 한 결정 안에서 뒤엉켜 있었는지를 가능한 한 정확하게 적으려 합니다. 재작성이 발전인가 도망인가를 구분하는 기준에 대해 생각해온 것도 함께 정리합니다. 진단과 정반대로 간 결정 기록을 다시 읽으면 부끄러울 정도로 명백합니다. 한쪽에는 "사용자 0, 매출 $0, 이제 사람을 모아라"라는 제 손글씨가 있고, 바로 그다음 행보는 전면 재작성 이었...

코드로 인스타그램 운영 자동화하기 — 캐러셀·릴스·발행 전 과정

인스타그램 운영에서 자동화할 수 있는 것 (전체 그림) 인스타그램 운영 자동화 를 처음 고민하면, 보통 "발행만 자동으로 되면 된다"고 생각합니다. 그런데 막상 운영해 보면 정작 손이 많이 가는 쪽은 발행보다 그 앞단의 콘텐츠 제작과 인증이었습니다. 이 글은 제가 직접 운영하는 파이프라인을 기준으로, 어디까지를 코드로 묶을 수 있는지 전체 그림을 먼저 보여주는 개요입니다. 개별 단계의 코드와 시행착오는 각 편으로 링크를 걸어 두었으니, 필요한 단계만 골라 들어가면 됩니다. 제가 운영하는 방식의 밑바탕은 인스타그램 운영을 독립된 단계로 쪼개 둔 데 있습니다. 콘텐츠를 생성하고, 인증을 통과하고, 발행하고, 릴스 영상을 편집하는 흐름을 각각 따로 자동화해 두면, 한 단계가 막혀도 나머지는 그대로 돌아갑니다. 파이썬 인스타그램 자동화의 묘미는 이 분리에 있습니다. 사람은 기획과 검수만 하고, 반복되는 작업은 코드가 결정론적으로 처리합니다. 전체 흐름은 이렇게 이어집니다. [1] 콘텐츠 생성 캐러셀: 주제/문장 → HTML 슬라이드 → Playwright PNG 캡처 릴스: 화면녹화 → ffmpeg(crop·배속·자막) → 9:16 mp4 │ ▼ [2] 인증·셋업 메타 앱 등록 → 단기 토큰 → 60일 장기 토큰(ig_exchange_token) → 앱 라이브(Live) 전환 │ ▼ [3] 발행 이미지 공개 URL 호스팅 → media 컨테이너 생성 → media_publish (그래프 API) │ ▼ [4] 릴스 / 운영 릴스 mp4 업로드, 캡션·해시태그, 인게이지먼트 루프 콘텐츠 생성 자동화 — 캐러셀(HTML→PNG)과 릴스(ffmpeg) 콘텐츠 생성은 자동화 이득이 가장 큰 단계입니다. 정보성 계정을 운영하면 콘텐츠 자체보다 이미지·영상 제작이 병...

만들 줄 아는데 왜 매출 0원일까 — 1인개발자가 빠지는 마케팅 공포

[시리즈] Voice To Notion 개발기 · 전체 8편 중 3편 1편 떠오른 생각을 Notion에 박제하고 싶었다 발행 2편 다 만들고도 제출조차 못 한 첫 버전 발행 3편 준비 중독, 그리고 마케팅 공포 현재 편 4편 그래서 나는 또 도망쳤다 — v2 전면 재작성 발행 5편 Claude Code로 앱을 다시 짓다 발행 6편 앱스토어 2회 반려가 진짜로 말한 것 발행 7편 너무 느려서 내가 안 썼다 — 온디바이스 STT 피벗 발행 8편 매출 $0에서 다시 정한 우선순위 발행 1인개발자의 마케팅 공포는 게으름이 아니라 분명한 패턴입니다. 저는 음성 메모를 AI가 분류해 Notion에 저장하는 앱(이 시리즈에서 부르는 익명은 Voice To Notion , 앱 이름은 JustBlurt )을 만들었습니다. 코어 기능은 동작했고, 결제 연동까지 끝냈습니다. 앱스토어 제출을 위한 심사 서류도 준비가 다 됐습니다. 그런데 매출은 0원이었습니다. 그 숫자를 처음 마주했을 때 저는 제 코드를 탓했습니다. 더 나은 STT 모델이 필요한 게 아닐까, Whisper API 응답 속도가 너무 느린 건 아닐까, 아이콘 디자인이 너무 단순한 건 아닐까 하고요. 하지만 그건 전부 틀린 진단이었습니다. 문제는 기술 스택이 아니었습니다. 문제는 저였습니다. 정확히는, 무서운 일을 피하려고 안전한 일에 숨는 반복적인 행동 패턴 이었습니다. 이 글은 그 패턴을 미화하지 않고 기록합니다. 1인개발자가 코딩 뒤에 숨는 세 단계, 준비 중독과 마케팅 공포와 제품 중독을 실제 자가진단 문서와 당시 기록을 바탕으로 적습니다. 같은 자리에 있는 분이라면 본인 이야기로 읽힐 것입니다. 1단계: 준비 중독 첫 번째 함정은 가장 눈에 띄지 않는 형태로 찾아왔습니다. 저는 매일 아침 앉아서 생산적으로 일했습니다. 그것이 문제였습니다. 정확히는, 그 "일"의 정체를 오랫동안 착각했다는 것이 문제였습니다. 전략 문서를 썼습...