바이브코딩 앱은 첫 사용자가 들어오는 순간부터 무너진다
겉보기엔 완성된 채팅 앱이었는데, 한 사용자가 30분 만에 공개 코드 속 비밀 API 키를 찾아냈다. 바이브코딩으로 만든 앱은 실제 사용자에게 열기 전에 무엇을 시험해 봐야 할까?

- 실제 사용자에게 공개된 바이브코딩 앱에서는 인증 메일, 데이터 권한, 결제 반영, API 사용량 문제가 반복해서 드러났다.
- 로그인 성공은 데이터 접근 권한을 보장하지 않으며, 결제 화면 성공은 구독 권한이 정확히 반영됐다는 뜻이 아니다.
- 공개 전에는 외부 이메일, 서로 다른 두 사용자 계정, 검증된 Stripe 이벤트, 사용자별 AI 호출 제한으로 운영 경계를 직접 시험해야 한다.
겉으로는 완성된 채팅 앱이었다. 화면이 열렸고 예상보다 기능도 많았다. 하지만 Reddit에 경험을 공유한 한 사용자는 30분 동안 살펴본 뒤 공개 JavaScript에 들어간 비밀 API 키, 누구나 읽고 쓸 수 있는 데이터베이스 규칙, 브라우저 로컬 저장소(localStorage)에 의존한 신원 확인, 서버가 검증하지 않는 관리자·차단 기능을 발견했다고 밝혔다.
다른 작성자는 Supabase를 사용하는 앱에서 로그인한 일반 사용자가 다른 사람의 데이터를 읽고 자신의 역할을 관리자로 바꿀 수 있었다고 했다. 앱은 멈추지 않았다. 정상적으로 작동하는 화면 뒤에서 권한이 무너졌다.
바이브코딩이 빠르게 해결하는 것은 화면과 기능을 만드는 일이다. 이메일 전달, 사용자 격리, 결제 상태 동기화, 사용량 통제까지 자동으로 완성되는 것은 아니다. 데모가 끝나는 순간부터 운영 점검이 시작된다.
첫 사용자는 화면이 아니라 서비스의 경계를 건드린다
개발자는 같은 이메일 주소로 가입하고 자기 데이터만 보며 결제 테스트 카드로 한 번 결제한다. 실제 사용자는 다른 메일 서비스와 브라우저를 쓰고, 링크를 두 번 누르며, 결제 도중 창을 닫고, 같은 버튼을 연속으로 누른다.
Reddit에서 앱 출시 뒤 실패를 기록한 백엔드 엔지니어는 인증 메일, RLS 정책, Stripe 웹훅, AI API 사용량, 에이전트의 수정 범위, 재방문 흐름을 문제로 꼽았다. 세 개의 바이브코딩 앱을 검토했다는 엔지니어링 매니저도 느슨한 RLS, 요청 제한 부재, 정상 경로 밖의 오류 처리 부재를 공통 문제로 제시했다.
이메일·권한·결제·비용에서 드러난 실패 지점은 Supabase·Stripe·OpenAI의 공식 운영 문서가 요구하는 점검 항목과도 겹친다.
| 데모에서 보이는 것 | 실제 운영에서 확인할 것 | 실패하면 생기는 일 |
|---|---|---|
| 가입 버튼이 작동함 | 외부 주소로 인증·재설정 메일이 도착하고 링크가 완료됨 | 가입했지만 로그인할 수 없는 사용자가 생김 |
| 로그인 후 목록이 보임 | 사용자 A가 사용자 B의 행을 읽거나 바꿀 수 없음 | 타인 데이터 노출과 권한 변경이 가능해짐 |
| Stripe 결제 화면이 성공함 | 검증된 이벤트가 내부 구독 상태에 한 번만 반영됨 | 무료 계정에 권한이 생기거나 정상 결제가 누락됨 |
| AI 생성 버튼이 답을 만듦 | 키가 서버에 있고 사용자별 요청량과 총비용이 제한됨 | 반복 호출이 장애와 예상 밖 비용으로 이어짐 |
인증 메일이 도착해야 가입이 끝난다
개발자의 이메일로 인증 메일 한 통을 받아본 것은 운영 점검이 아니다. Supabase 공식 문서는 기본 SMTP가 운영용이 아니라고 명시한다. 기본 발송 서비스는 팀에 사전 승인된 주소만 받을 수 있고 발송량 제한과 전달·가동 시간 보장도 없다.
이메일과 비밀번호, OTP, 매직 링크, 초대, 이메일 확인을 실제 사용자에게 제공하려면 별도 SMTP를 연결해야 한다. 발신 도메인의 SPF·DKIM·DMARC를 설정하고 인증 메일과 마케팅 메일의 발신 체계도 분리해야 한다.
서로 다른 메일 서비스의 외부 주소로 가입, 이메일 확인, 비밀번호 재설정, 링크 만료와 재발송을 실행한다. 발송 성공 로그만 보지 않고 받은편지함 도착과 링크 완료까지 확인한다.
| 확인 항목 | 실패 판정 | 통과 증거 |
|---|---|---|
| 가입 확인 메일 | 외부 주소에 메일이 도착하지 않음 | 받은편지함 수신과 확인 링크 완료 |
| 비밀번호 재설정 | 재설정 메일 또는 이동 경로가 끊김 | 새 비밀번호로 다시 로그인 성공 |
| 재발송·만료 | 여러 번 누른 링크가 잘못된 상태를 만듦 | 만료 안내와 최신 링크의 정상 동작 |
| 발송 장애 | 사용자는 기다리지만 운영자는 실패를 모름 | 공급자 로그, 앱 오류 기록, 재시도 상태 |
로그인과 데이터 권한은 서로 다른 기능이다
Supabase는 인증과 권한 부여를 구분한다. 인증은 사용자가 누구인지 확인한다. 권한 부여는 그 사용자가 어떤 행을 읽고 만들고 바꾸고 지울 수 있는지 결정한다. 로그인이 성공했다는 사실만으로 자기 데이터만 볼 수 있게 되는 것은 아니다.
노출된 테이블에는 RLS를 켜고 역할별 권한을 좁힌 뒤 auth.uid() 같은 사용자 식별값을 소유자 열과 연결해야 한다. SELECT·INSERT·UPDATE·DELETE도 각각 확인해야 한다. 특히 UPDATE는 어떤 기존 행을 바꿀 수 있는지와 바꾼 뒤 행이 허용된 상태인지가 모두 맞아야 한다.
- 로그아웃 상태에서 비공개 데이터 읽기와 쓰기가 거부되는지 확인한다.
- 사용자 A가 자신의 행에서 허용된 작업만 수행할 수 있는지 확인한다.
- 사용자 A가 사용자 B의 행에 같은 작업을 시도했을 때 모두 거부되는지 확인한다.
한 Reddit 작성자는 Supabase 기반 앱에서 다른 사용자의 데이터를 읽고 역할과 관리자 레코드까지 바꿀 수 있었다고 밝혔다. 또 다른 채팅 앱 사례에서는 화면에서 관리자 버튼을 숨겼지만 데이터와 권한 판정은 서버에서 막히지 않았다. 브라우저 UI는 권한 통제 장치가 아니다.
결제 화면 성공과 유료 권한 반영은 다른 사건이다
Stripe Checkout이 결제 성공 화면을 보여줬다고 해서 앱의 구독 상태까지 정확히 반영된 것은 아니다. 결제는 Stripe에서 일어나고 앱의 유료 권한은 웹훅을 받은 서버가 내부 데이터에 반영한다.
Stripe 공식 문서는 이벤트를 처리하기 전에 가공하지 않은 요청 본문, Stripe-Signature 헤더, 웹훅 엔드포인트의 서명 비밀 키로 출처를 확인하라고 요구한다. 서명을 확인하지 않으면 외부 요청이 결제 완료 이벤트처럼 들어와 주문 처리와 계정 권한 부여를 유발할 수 있다.
Stripe는 같은 이벤트를 두 번 이상 보낼 수 있고 생성 순서대로 도착한다고 보장하지 않는다. 처리한 이벤트 ID를 기록해 중복 반영을 막고, 특정 순서에 기대지 않으며, 필요한 결제·구독 객체를 다시 조회해야 한다.
| 결제 경계 | 공개 전 실패 시험 | 통과 조건 |
|---|---|---|
| 출처 | 서명이 없거나 틀린 요청 전송 | 권한 변경 없이 4xx 반환 |
| 중복 | 같은 이벤트를 두 번 전달 | 결제·크레딧·구독이 한 번만 반영 |
| 순서 | 이벤트가 예상과 다른 순서로 도착 | 현재 Stripe 객체를 조회해 최종 상태 결정 |
| 내부 상태 | 결제 취소·환불·실패 뒤 앱 권한 확인 | Stripe와 앱의 구독 상태가 일치 |
무료 AI 버튼에는 네 겹의 비용 통제가 필요하다
OpenAI는 API 키를 브라우저나 모바일 앱에 넣지 말고 자체 백엔드 서버를 통해 요청하라고 안내한다. 키가 공개되면 다른 사람이 앱 밖에서 그 키로 요청할 수 있다.
공급자가 적용하는 분당 요청·토큰 제한은 공급자의 시스템을 보호하는 한도다. 한 사용자가 제품의 무료 호출량을 전부 쓰는 일을 막는 사용자별 정책은 아니다.
| 통제 층 | 막는 문제 |
|---|---|
| 서버 측 키 보관 | 브라우저 번들·모바일 앱에서 공급자 키 탈취 |
| 사용자·IP·API 키별 요청 제한 | 자동 반복 호출과 한 사용자의 독점 |
| 사용자별 일일·월간 제품 한도 | 무료 기능이 무제한 유료 API 호출로 변하는 문제 |
| API 공급자 계정·프로젝트의 강제 지출 상한 | 애플리케이션 통제가 뚫렸을 때 총비용 증가 |
비용 알림은 과금 중단 장치가 아니다. OpenAI는 지출 알림과 강제로 요청을 중단하는 상한을 구분한다. 강제 상한도 즉시 집행되지는 않아 기록 금액이 설정값을 조금 넘을 수 있다.
오류가 사라지면 운영자도 문제를 알 수 없다
세 앱을 검토한 Reddit 작성자는 타사 API 시간 초과나 데이터베이스 쓰기 실패 뒤에 아무 처리도 없는 코드를 공통 문제로 꼽았다. 예외를 잡아도 기록, 사용자 안내, 재시도, 운영자 알림으로 연결되지 않으면 실패는 그대로 사라진다.
- 사용자가 다음 행동을 결정할 수 있는 오류 메시지
- 요청 ID와 사용자·작업 식별자가 포함된 서버 로그
- 다시 실행해도 결과가 중복되지 않는 처리 방식
- 자동 재시도가 끝내 실패했을 때 운영자에게 가는 알림
공개 뒤에는 AI의 수정 범위부터 좁힌다
한 작성자는 작은 변경을 요청했는데 AI 에이전트가 인증 흐름을 다시 쓰고 관련 없는 기능 세 개를 망가뜨린 경험을 소개했다. 실제 사용자가 생긴 뒤에는 수정 속도보다 변경 범위를 통제하는 일이 중요하다.
허용할 파일과 금지할 파일, 유지해야 할 기존 동작, 통과해야 할 회귀 테스트를 함께 적는다. 변경 전 커밋을 남기고 인증·결제·권한 파일이 의도 없이 바뀌지 않았는지 diff로 확인한다.
실제 사용자에게 열기 전 확인할 운영 관문
| 관문 | 반드시 실행할 검사 | 출시 차단 조건 |
|---|---|---|
| 인증 | 외부 메일에서 가입·확인·재설정·재발송 | 메일 미도착, 링크 오류, 실패 로그 부재 |
| 권한 | 로그아웃·사용자 A·사용자 B의 CRUD | 타인 행 접근, 역할·잔액·구독 직접 변경 |
| 결제 | 잘못된 서명·중복·순서가 뒤바뀐 이벤트·환불 | 가짜 이벤트 수락, 중복 권한, 상태 불일치 |
| AI 비용 | 키 노출, 반복 호출, 사용자 한도, 강제 지출 상한 | 클라이언트 키, 무제한 호출, 알림만 존재 |
| 장애 | 메일·결제·AI·DB 장애를 의도적으로 유발 | 무한 대기, 성공으로 잘못 표시, 로그·알림 없음 |
| 변경 | AI 수정 전후 파일과 핵심 경로 회귀 검사 | 요청 밖 파일 수정, 핵심 경로 파손 |
이 표를 통과하지 못했다면 앱은 미완성이다. 디자인과 기능이 부족해서가 아니다. 사용자의 신원, 데이터, 돈, API 비용을 맡을 운영 경계가 아직 만들어지지 않았기 때문이다.
앱 제작 시간이 줄어도 운영 책임은 줄지 않는다
바이브코딩은 첫 화면과 기능을 만드는 시간을 크게 줄였다. 그만큼 개발자는 이메일 전달, 권한, 결제, 비용 같은 운영 문제를 더 일찍 만난다. 코드를 빨리 만들었다고 보안과 운영까지 건너뛸 수 있는 것은 아니다.
출시 기준은 내 계정에서 작동하는지가 아니다. 외부 사용자가 가입을 끝내고, 자기 데이터만 다루고, 실제 결제 상태에 맞는 권한을 얻으며, 정해진 한도 안에서 AI 기능을 사용하는지 확인해야 한다.
첫 사용자는 앱을 망가뜨리는 사람이 아니다. 데모가 가려둔 미완성 영역을 처음으로 드러내는 사람이다.
자주 묻는 질문
Supabase에서 RLS만 켜면 다른 사용자 데이터가 보호되나?
아니다. 노출된 테이블에 RLS를 켠 뒤 SELECT·INSERT·UPDATE·DELETE 정책이 사용자 소유권을 올바르게 검사하는지 확인해야 한다. 로그아웃 상태와 서로 다른 사용자 계정 두 개로 직접 시험해야 한다.
Stripe 결제가 성공하면 앱에서 바로 유료 권한을 줘도 되나?
브라우저의 성공 화면만 기준으로 권한을 주면 안 된다. 서버가 Stripe 웹훅 서명을 검증하고 중복 이벤트를 걸러낸 뒤 현재 결제·구독 상태를 기준으로 내부 권한을 바꿔야 한다.
AI API 공급자의 요청 제한만 있으면 비용이 통제되나?
아니다. 서버 측 키 보관, 사용자·IP별 요청 제한, 사용자별 제품 한도, 공급자 강제 지출 상한이 서로 다른 문제를 막는다.
공개 전 몇 명에게 테스트를 맡기면 충분한가?
인원수보다 서로 다른 운영 경계를 실행했는지가 중요하다. 외부 이메일, 두 사용자 간 권한 분리, 웹훅 이벤트의 중복과 도착 순서 변경, 반복 AI 호출, 외부 서비스 실패를 각각 시험해야 한다.