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

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

앱 출시 블로커가 사업자등록이라는 것 — App Store 현실 경험담

JustBlurt 다시 짓다 — 클라우드 STT 전환기 · 전체 5편 중 5편

  1. 1편 내가 만든 앱을 내가 안 쓴다 — 온디바이스 STT의 한계발행
  2. 2편 ElevenLabs Scribe v2 Realtime으로 갈아탄 이유발행
  3. 3편 맥미니를 AI 정제 서버로 만들다발행
  4. 4편 무료 사용자 3회 제한을 Supabase Edge Function으로 구현하다발행
  5. 5편 앱 출시 블로커가 사업자등록이라는 것현재 편

STT 엔진을 교체하고, AI 정제 서버를 올리고, 무료/유료 게이트까지 구현했습니다. 코드 쪽은 마무리됐습니다. 그런데 앱이 아직 스토어에 없습니다. 발목을 잡은 건 코드가 아니었습니다.

구독 상품이 포함된 앱을 App Store에 처음 올리는 입장에서, eas submit을 실행하기 전에 어떤 행정 절차가 선행되어야 하는지, 그리고 제가 어디서 막혔는지를 겪은 그대로 적습니다.


1. 기술이 아니라 행정이 막았다

RevenueCat으로 구독 상품을 연동하는 것 자체는 어렵지 않았습니다. SDK를 붙이고, Entitlement를 정의하고, paywall을 만드는 코드 작업은 하루 이틀 만에 끝났습니다.

문제는 App Store Connect 쪽이었습니다. 구독 상품이 있는 앱을 제출하려면 Paid Apps Agreement가 Active 상태여야 합니다. 이 약관이 활성화되어 있지 않으면 제출 자체가 거부됩니다. 에러 코드는 2.1b로, "구독 결제 관련 설정이 완료되지 않았다"는 내용입니다.

Paid Apps Agreement가 Active 상태가 아닌 채로 제출하면, 심사를 거치기도 전에 2.1b 에러로 반려됩니다.

그리고 Paid Apps Agreement를 수락하려면 사업자등록이 필요합니다. 개인 개발자도 가능하지만, 세금 정보와 은행 계좌를 입력하는 과정에서 사업자등록번호가 요구됩니다. 한국 기준으로는 개인사업자 등록을 먼저 해야 합니다.

코드는 다 됐는데, 사업자등록증이 없어서 못 냅니다. 이것이 현재 상황입니다.


2. Paid Apps Agreement와 사업자등록의 연결고리

App Store Connect에서 유료 앱 또는 인앱 구독을 사용하려면 다음 경로를 따라야 합니다.

App Store Connect → Agreements, Tax, and Banking → Paid Apps 섹션

여기서 Paid Apps Agreement를 처음 수락할 때 Apple이 요구하는 정보는 다음과 같습니다.

  • 법인 또는 개인사업자 정보 (이름, 등록번호, 주소)
  • 세금 정보 (한국의 경우 사업자등록번호 기반 양식)
  • 정산 은행 계좌 (Apple이 지급하는 수익을 받을 계좌)

사업자등록이 없으면 세금 정보 입력 단계에서 막힙니다. 반드시 사업자등록을 먼저 완료해야 이 단계를 통과할 수 있습니다.

개인 개발자는 국세청 홈택스 또는 세무서에서 간이과세자나 일반과세자로 개인사업자 등록을 하게 됩니다. 업종은 앱 개발·배포로 잡는 쪽이 가장 무난했습니다.


3. 구독 상품 설정 순서

Paid Apps Agreement가 Active 상태가 된 이후에야 구독 상품을 App Store Connect에 등록할 수 있습니다. 순서를 지키지 않으면 어느 단계에서든 막힙니다.

단계 작업 선행 조건
1 사업자등록 없음
2 Paid Apps Agreement 수락 + 세금/은행 정보 입력 사업자등록 완료
3 App Store Connect에서 구독 상품 생성 Paid Apps Agreement Active
4 RevenueCat에서 App Store 상품 ID 연결 구독 상품 생성 완료
5 eas submit 빌드 업로드 빌드 존재
6 버전 페이지에서 구독 상품 포함 설정 후 제출 4, 5 모두 완료

구독 상품을 App Store Connect에 등록할 때는 다음 항목이 필요합니다.

  • Reference Name: 내부 식별용 이름 (심사에 노출되지 않음)
  • Product ID: RevenueCat과 연결할 고유 ID (예: com.yourapp.monthly)
  • Subscription Group: 월간/연간 상품을 하나의 그룹으로 묶음
  • Display Name / Description: 사용자에게 보이는 이름과 설명 (지역화 필요)
  • Price: 월간(Monthly), 연간(Annual) 각각 설정
  • Promotional Offer 스크린샷: paywall 화면 캡처 1장 이상

가격은 Apple이 제공하는 가격표(Price Point)에서 선택합니다. 임의 금액 입력이 아니라 Apple이 정해 둔 구간 중 하나를 고르는 방식입니다. 한국 원화 기준으로 1,900원, 2,900원, 3,900원 등의 구간이 있습니다.


4. eas submit과 제출 절차

빌드 업로드는 다음 명령어로 합니다.

eas submit -p ios --latest

--latest는 EAS에 가장 최근에 올린 빌드를 App Store Connect에 제출하는 옵션입니다. 빌드를 새로 만들지 않고 이미 EAS에 올라간 빌드를 그대로 씁니다.

업로드가 완료되면 App Store Connect의 해당 앱 버전 페이지에 빌드가 잡힙니다. 이후 버전 페이지에서 다음을 확인합니다.

  • 스크린샷 (iPhone 6.5인치 이상 필수, iPad는 앱이 iPad를 지원하는 경우)
  • 앱 설명, 키워드, 지원 URL
  • 구독 상품이 이 버전에 포함되어 있는지 확인
  • App Review Information — 테스터 계정 이메일/비밀번호 입력

구독이 포함된 앱은 App Review 단계에서 심사자가 실제로 구독 플로우를 테스트합니다. 심사자가 테스트할 수 있는 데모 계정을 반드시 제공해야 합니다. 계정을 제공하지 않으면 심사가 진행되지 않거나 4.0 에러(불완전한 정보)로 반려됩니다.

App Review Notes에 포함해야 할 것
- 테스트 계정 이메일과 비밀번호
- 주요 기능 접근 경로 (예: "로그인 후 홈 화면에서 녹음 버튼을 탭하면 STT가 작동합니다")
- 계정 삭제 기능 동작 확인 (GDPR/개인정보 요구 사항) — 화면 녹화 첨부 권장

계정 삭제 기능은 Apple의 심사 기준상 앱 내에 반드시 존재해야 합니다. 없으면 5.1.1 에러로 반려됩니다. 삭제 기능이 구현되어 있다면 그 경로를 Notes에 명시하고, 필요하다면 화면 녹화를 첨부합니다.


5. 제출 전 체크리스트

구독 상품이 포함된 앱을 처음 제출할 때 놓치기 쉬운 항목을 정리했습니다.

항목 확인 내용 상태
사업자등록 개인사업자 등록 완료 여부 대기 중
Paid Apps Agreement App Store Connect에서 Active 상태 확인 대기 중
은행 계좌 세금/은행 정보 입력 완료 대기 중
구독 상품 Monthly/Annual Product ID 생성, RevenueCat 연결 대기 중
paywall 스크린샷 구독 화면 캡처 첨부 대기 중
앱 스크린샷 iPhone 6.5인치 이상 최소 1장 준비됨
데모 계정 이메일/비밀번호, App Review Notes 입력 준비됨
계정 삭제 기능 앱 내 삭제 경로 구현, Notes에 명시 준비됨
빌드 업로드 eas submit -p ios --latest 실행 준비됨

코드 쪽은 모두 준비됐습니다. 사업자등록이 완료되는 시점에 2번부터 순서대로 진행할 예정입니다. 처음 구독 앱을 제출하는 분이라면 행정 쪽이 기술보다 오래 걸린다는 점을 미리 감안하는 것이 좋습니다.

코드를 다 짜고 나서 "이제 제출하면 되겠다"고 생각했다가 사업자등록부터 시작해야 한다는 것을 알게 됩니다. 그 시간이 최소 1-2주입니다. 구독 상품이 들어간다면, 개발 초반부터 행정 절차를 병행하는 편이 훨씬 낫습니다. 다섯 편에 걸쳐 다룬 내용은 전부 개인이 혼자 진행한 사이드 프로젝트에서 실제로 있었던 과정이고, 'JustBlurt 다시 짓다' 시리즈는 여기서 마칩니다.

댓글

이 블로그의 인기 게시물

디스코드 구글캘린더 연동 봇 만들기: 카톡 캡처 OCR 자동 등록 시스템 구축

클로드 코드 비용 줄이는 법 — ollama 로컬 LLM 연동 (M3 맥북)

유튜브 노션 자동 저장 봇 만들기 — Claude CLI MCP + Discord 233일 운영기