12주 만에 혼자서 구축한 크로스 플랫폼 AI SaaS — 2,258번의 커밋, 그리고 50달러를 날린 경험
요약
1인 개발자가 AI 코딩 에이전트를 활용해 12주 만에 크로스 플랫폼 AI SaaS인 SubcueAI를 구축한 과정과 기술 스택을 공유합니다. 아키텍처 설계의 중요성과 실시간 STT 제공업체 선정 시 겪은 시행착오를 통해 실전 개발 교훈을 전달합니다.
핵심 포인트
- 매몰 비용에 집착하지 말고 초기 아키텍처를 과감히 수정할 것
- 실시간 STT 선정 시 데모가 아닌 실제 사용 환경의 제약 조건을 검증할 것
- Cloudflare Workers와 Tauri를 활용한 효율적인 풀스택 구성
- AI 에이전트는 도구일 뿐, 엔지니어링 규율이 결과물의 품질을 결정함
12주 전: 빈 저장소(repo). 오늘 아침: 2,258번의 커밋, macOS와 Windows용 데스크톱 앱, 결제 시스템, 관리자 대시보드, 26개 언어로 된 마케팅 사이트 — 이 모든 것이 프로덕션(production) 환경에서 운영 중입니다. 혼자서 해냈습니다. 공동 창업자도, 팀도, 투자도 없었습니다.
저는 풀스택 개발자(full-stack dev)이며, 네, AI 코딩 에이전트(AI coding agent)가 많은 키 입력을 대신했습니다. 하지만 "#showdev: AI가 내 앱을 만들었다"라는 말은 게으른 헤드라인이며 틀린 말입니다. 흥미로운 부분은 1인 개발자 + 에이전트(solo + agent) 설정이 실제로 무너지지 않는 결과물을 만들어낼 수 있게 하는 엔지니어링 규율(engineering discipline), 그리고 실제로 완전히 무너졌던 몇 번의 순간들입니다.
여기 사용된 스택(stack), 막다른 길(dead ends), 실제 설정(config), 그리고 저에게 50달러의 비용을 치르게 한 교훈을 소개합니다.
스택 (궁금한 분들을 위해)
- 백엔드(Backend): Cloudflare Workers (Hono + TypeScript), 데이터용 D1 (SQLite), 세션/캐시용 KV, 업로드용 R2, 임베딩(embeddings)용 Vectorize.
- 클라이언트(Clients): macOS에서는 SwiftUI, Windows에서는 Tauri 2 (React + Rust). 실시간 오디오 캡처(시스템 + 마이크), STT(Speech-to-Text)로 스트리밍, 답변은 플로팅 오버레이(floating overlay)에 렌더링.
- 결제(Payments): 결제 대행사(Merchant of Record)로 Paddle 사용.
- 제품(The product): SubcueAI (subcue.ai) — 실시간 AI 인터뷰 어시스턴트. 통화 양측의 내용을 실시간으로 전사(transcribe)하고 오버레이에 대화 주제를 제안합니다. 모의 인터뷰 연습 모드도 있습니다.
이제 고통스러웠던 부분들입니다.
교훈 1 — 이틀 만에 v1을 버렸다
첫 번째 버전은 "제대로 된" 방식으로 구축된 네이티브 macOS 앱이었습니다. 그것은 약 36시간 동안 유지되었습니다. 전체 히스토리에서 두 번째 커밋은 말 그대로 _"백엔드(Cloudflare Workers)로 마이그레이션 + macOS 재작성, 레거시 프로젝트 삭제"_였습니다.
대담한 전략적 결정은 아니었습니다. 단지 하루 만에 이 아키텍처(architecture)가 이후의 모든 과정(인증(auth), 동기화(sync), 웹사이트, 관리자 패널)을 세 배 더 어렵게 만들 것이라는 사실을 깨달았을 뿐입니다. 그래서 삭제했습니다.
매몰 비용(sunk-cost)에 대한 본능은 당신이 배포할 버그 중 가장 값비싼 버그입니다. 1주 차에 이틀 치 작업물을 삭제하는 것은 저렴합니다. 3개월 차에 삭제하는 것은 장례식과 같습니다.
교훈 2 — 5개의 STT 제공업체, 그리고 유닛 테스트(unit-test)가 불가능한 버그
실시간 전사 (Real-time transcription)는 이 앱의 성패를 결정짓는 핵심 요소입니다. Git 히스토리는 제공업체들의 공동묘지와 같습니다: Apple의 내장 음성 인식 → 로컬 whisper.cpp → Deepgram → ElevenLabs → OpenAI realtime transcribe. 무려 다섯 개입니다. 각각은 90초짜리 데모에서는 훌륭해 보였지만, 지연 시간 (latency), 억양, 끊기는 웹소켓 (websockets), 또는 비용 문제로 인해 무너졌습니다.
저에게 교훈을 준 버그는 다음과 같습니다: 한 제공업체에 세션당 **60분 하드 캡 (hard cap)**이 조용히 설정되어 있었습니다. 데모는 5분 내외로 진행되기에 저는 한 번도 이 한계에 도달한 적이 없었습니다. 그러다 누군가 실제로 70분 동안 인터뷰를 진행하면, 60분 시점에 전사가 중단되면서 전체 파이프라인이 먹통이 되는 연쇄 오류가 발생합니다.
이런 문제는 테스트로 잡아낼 수 없습니다. 제가 이를 발견할 수 있었던 이유는 클라이언트 텔레메트리 (client telemetry)를 연결해 두었고, 실패 사례들이 정확히 1시간 지점에 몰려 있는 것을 확인했기 때문입니다. 해결책은 한계치에 도달하기 _전_에 선제적으로 세션을 교체하는 와치독 (watchdog)을 구현하는 것입니다:
// 55분에 교체 — 의도적으로 5분 일찍 수행하여, 전환이 보이지 않도록 함.
const SESSION_LIMIT_MS = 60 * 60 * 1000
const ROTATE_BEFORE_MS = 5 * 60 * 1000
...
"데모에서 잘 돌아간다"와 "불안정한 호텔 와이파이 환경의 50분 세션에서 잘 돌아간다"는 완전히 다른 제품입니다. 프로덕션 환경에 계측 도구 (instrument)를 심지 않으면 눈을 가리고 비행하는 것과 같습니다.
교훈 3 — Windows UI를 네 번이나 다시 만들었다 (그리고 둥근 모서리 하나 때문에 하루를 날렸다)
저는 Windows 클라이언트가 macOS 클라이언트와 일치하기를 원했습니다. 즉, 반투명하고 블러(blur) 처리가 된 둥근 모서리의 플로팅 윈도우(floating window) 말입니다. 사소해 보이지만, 전혀 그렇지 않습니다.
저는 WinUI 3 → Qt → Avalonia → Tauri 2 순으로 옮겨갔습니다. WinUI는 레이어드 투명도 (layered transparency)를 구현할 수 없었습니다. Avalonia는 상당한 노력을 기울였음에도 Windows 데스크톱에서 macOS 수준의 둥근 모서리 + 투명도 + 블러 효과를 구현하지 못했습니다. 윈도우 크롬 (window chrome)과 싸우기만 했던 커밋들이 오후 내내 이어졌습니다: 잘못된 정수 너비로 입력된 DWM 상수, Mica 대 Acrylic, vibrancy, CSS backdrop-filter, 그리고 끝까지 사라지지 않던 1px의 흰색 테두리까지. 한 커밋 메시지는 이랬습니다: "CSS 크롬 테두리 제거 — DWM 가장자리와 겹쳐서 2-3px 두께로 보임." 모서리 하나 때문에 꼬박 하루를 썼습니다.
해결책은 의지력이 아니었습니다. 저는 둥근 모서리 처리가 단 한 줄이면 끝나는 웹뷰 쉘 (webview shell, Tauri)로 이동했습니다.
.window-chrome {
border-radius: 12px;
background: rgba(16, 16, 26, 0.72);
...
만약 미적인 요소를 위해 프레임워크와 하루 종일 싸우고 있다면, 프레임워크가 바로 버그입니다. 의지력을 바꾸지 말고 도구를 바꾸세요.
레슨 4 — 결제는 진짜 거대한 산이다 (그리고 가장 지루한 산이다)
모두가 AI에 대해 이야기하고 싶어 합니다. 하지만 당신이 사업을 할 수 있을지를 결정하는 요소, 즉 '돈을 받는 일'에 대해서는 아무도 이야기하고 싶어 하지 않습니다.
저는 Stripe로 시작했다가, 판매 기록 대행업자 (Merchant of Record, MoR)로서 Paddle로 옮겼습니다. 1인 개발자에게 이 차이는 매우 큽니다. MoR이 법적 판매자가 되어 전 세계의 판매세 및 부가가치세 (VAT)를 처리해주기 때문입니다. 저는 40개 관할 구역의 세무 전문가가 될 생각이 없습니다. 들어본 적도 없는 세무 당국으로부터 통지서를 받지 않는 대가로 마진 몇 퍼센트를 포기하겠습니다.
그다음은 라이프사이클(lifecycle) 문제였는데, 저는 커밋 하나하나를 거치며 이를 헤쳐 나갔습니다: 일일 할당량 (daily-quota) → 크레딧 잔액 (credits balance); 즉시 비례 배분 (prorate)되는 업그레이드; 기간 종료 시까지 _유예 (defer)_되어야 하는 다운그레이드 (제공업체에
# wrangler.toml — 금전적 안전을 위한 계약 (money-safety contract)
[[queues.consumers]]
queue = "content-publish"
...
// Consumer: 멱등성(idempotent)을 유지하며, 항상 ack(승인)하고, 절대 다시 던지지(re-throw) 말 것.
export async function queue(batch, env) {
for (const msg of batch.messages) {
...
당신의 로봇에게 폭발 반경 (blast radius)을 부여하세요. 비용이 발생하는 작업을 자동화하기 전에, "이 작업이 루프를 돌 경우 최악의 상황은 무엇인가?"라고 자문하고, 그 한도를 설정하세요.
내가 실제로 AI 에이전트를 구동하는 방법
"AI를 사용했다"라는 말은 아무런 정보도 주지 않습니다. 대량의 작업을 지속 가능하게 만든 메커니즘은 다음과 같습니다.
1. 살아있는 지침 파일(instructions file)이 전부입니다. 저장소(repo)에서 가장 레버리지가 높은 산출물은 코드가 아닙니다. 에이전트가 매 세션마다 읽는, 불변성(invariants)을 인코딩한 길고 밀도 높은 지침 문서(저의 경우 CLAUDE.md)입니다. 여기에는 인증(auth)이 어떻게 작동하며 절대 바뀌어서는 안 되는지, 결제 식별 정보, 앞서 언급한 금전적 안전 규칙, 명명 규칙(naming conventions) 등이 포함됩니다. 이는 에이전트의 세션별 건망증 속에서도 살아남는 외부 두뇌 역할을 합니다. 항목의 예시는 다음과 같습니다:
- change-plan 엔드포인트는 기존의 provider_subscription_id를 PATCH하거나
pending_* 컬럼을 작성할 수만 있음 — 절대로 두 번째 구독을 생성하지 말 것.
(이것이 이중 결제의 근본 원인이었음. "도움이 되려고" 리팩터링하지 말 것.)
2. 'YES'뿐만 아니라 'NO'를 기록하세요. 가치의 절반은 부정적 공간(negative space), 즉 _"이 스키마 타입을 추가하지 마세요, 의도적으로 삭제된 것입니다"_와 같은 명시적인 거부권(vetoes)에 있습니다. 이것이 없다면, 선의를 가진 에이전트는 당신이 삭제했던 바로 그 요소를 매번 다시 도입할 것입니다.
3. 가이드라인(rails)이 존재한다면, 서브시스템 전체를 맡기세요. 저는 에이전트가 구독 라이프사이클 전체와 콘텐츠 파이프라인을 거의 엔드 투 엔드(end-to-end)로 구축하도록 내버려 두었습니다. 이것이 가능한 이유는 불변성(invariants)이 먼저 작성되었기 때문입니다. 속도보다 가이드라인이 우선입니다. 가이드라인 없는 속도는 '교훈 5(Lesson 5)'가 될 뿐입니다.
4. 세션 전반에 걸쳐 학습 내용을 유지하세요. "아, 그래서 그랬구나"라고 깨닫는 모든 것들 — 주의 사항(gotchas), 결정 사항, 실제 버그와 예상된 이상 현상 사이의 차이점 등을 모두 저장합니다. 이를 통해 미래의 나 자신과 에이전트는 똑같은 것을 다시 배우는 일을 멈추게 됩니다.
5. 멘탈 모델 (The mental model): 이것은 하룻밤 사이에 모든 것을 잊어버리고 절대 반박하지 않는 아주 똑똑한 주니어 개발자입니다. 당신의 역할은 단순히 _타이핑하는 것_에서 사양(spec)을 작성하고, 경계를 설정하며, 모든 차이점(diff)을 검토하고, 에이전트가 내릴 수 없는 판단(judgment calls)을 책임지는 것으로 전환됩니다. 당신은 시니어의 역할 중 시니어다운 부분만을 수행하는 시니어가 되는 것입니다.
그리고 광고처럼 들리지 않게 주의사항을 덧붙이자면: 안목 없는 속도는 위험합니다. 에이전트는 틀린 일을 아주 자신 있게, 그리고 빠르게 수행할 것입니다 (예: 50달러를 불태워 먹은 경험 참조). 가드레일(guardrails), 아키텍처(architecture), 그리고 판단은 여전히 전적으로 당신의 몫입니다. 에이전트는 타이핑을 삭제할 뿐, 엔지니어링(engineering)을 삭제하지는 않습니다.
1인 빌더를 위한 요약 (TL;DR)
- 커밋 횟수는 자랑거리가 아닙니다. 무엇을 삭제할지 아는 것이 실력입니다.
- 지루한 80%(결제, 인증, 국제화 (i18n), 인프라 (infra))가 해자 (moat)가 됩니다. 누구나 LLM 호출을 연결할 수는 있습니다.
- 포지셔닝 (Positioning)은 기능 (features)보다 더 빠르게 복리로 성장합니다.
- AI 에이전트는 타이핑을 제거하고 판단력을 증폭시킵니다 — 더 좋게 혹은 더 나쁘게 말이죠. 판단력을 가져오세요.
이 모든 과정의 결과물인 제품은 SubcueAI → subcue.ai입니다. 저는 GitHub에서는 @imaaroncao, X에서는 @real_aaron_cao를 사용합니다. 댓글로 질문 환영합니다 — 바보 같은 질문이라도 괜찮습니다. 저도 분명히 아주 많이 가지고 있으니까요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기