Amy 앱의 최악의 한 주 — Supabase 5시간 다운, Sonar 15분 다운, 그리고 프리미엄 뚫린 이야기
Amy 앱의 최악의 한 주 — Supabase 5시간 다운, Sonar 15분 다운, 그리고 프리미엄 뚫린 이야기
이 영상은 Chris가 자기 생산성 앱 Amy(Apple Notes 스타일 칼로리 트래커)의 출시 이후 최악의 한 주를 그대로 공개한 13분 짜리 실전 후기이다. 그는 이 채널의 원칙을 투명성이라고 정의한다. 수치가 오르는 모습만 보여주는 대신, 서비스 2번 다운 + 보안 사고 1건을 어떻게 겪고 어떻게 고쳤는지 그대로 공개한다. 이 리포트는 원본을 보지 않아도 그가 겪은 사고와 그 이후에 구축한 방어책을 그대로 자기 앱에 이식할 수 있도록 재조립한 것이다.
핵심 요지 미리보기
· 출시 3개월, 현재 MRR $1,700 · 지난 30일 매출 $2,200 · 유료 사용자 166명. 1주차 리텐션 10% → 12.9%.
· 사고 1: Supabase 5시간 다운. Amy는 오프라인 모드가 있었지만 인증 예외 케이스로 5시간 동안 흰 화면만 표시.
· 사고 2: Perplexity Sonar 15분 다운. AI 콜이 안 됨. OpenRouter fallback을 수동 구현으로 옮긴 이유가 있다.
· 사고 3: Supabase RLS의 함정. 유저가 자기 row 전체를 수정 가능 → 프리미엄 자가 부여 + rate limit 조작으로 API 청구 폭탄 위험.
· 상태 페이지를 다운된 인프라 위에 올리지 말라. 커스텀 메시지도 같은 Supabase에 저장하면 다운 시 안 보임.
· 보안 스택 4개: rate limit(계정+IP), kill switch, 서비스별 예산 상한선, 환경변수 백엔드 격리.
목차
- 1. 투명성이 채널의 원칙 — 왜 이 영상을 만들었나
- 2. 3개월 성적표 — MRR $1,700과 리텐션 12.9%
- 3. 사고 1: Supabase 5시간 다운 — 인증 예외의 함정
- 4. 5시간 흰 화면과 웹사이트 이메일로 몰려온 유저들
- 5. 다시는 흰 화면이 안 뜨도록 — 상단 상태 배지 설계
- 6. 상태 페이지의 재귀적 함정 — 다운된 곳에 저장하지 마라
- 7. 사고 2: Perplexity Sonar 15분 다운 — 왜 fallback을 수동으로 만들었나
- 8. 사고 3: Supabase RLS의 프리미엄 & rate limit 노출
- 9. 48시간 vibe coding의 후과 — 별도 테이블이 정답이었다
- 10. 보안 4단 스택: Rate Limit · Kill Switch · Budget Cap · Env Vars
- 11. Whisperflow와 음성 코딩 워크플로우
- 12. 다크모드로 숨쉬기 — Cecilia의 일러스트 재작업
- 핵심 요약 (Takeaways)
1. 투명성이 채널의 원칙 — 왜 이 영상을 만들었나
Chris는 2주 전에 이미 Amy 업데이트 영상을 올렸다. 하지만 그 사이 출시 이후 최악의 한 주가 지나갔다. 그는 "수치가 오르고 모든 게 순조로운 모습만 보여주는 것보다, 힘든 한 주가 어떤 모습인지 그리고 그 문제를 어떻게 해결하는지 보여주는 것이 더 중요하다"고 판단했다. 이 태도가 이 영상 전체의 축이다.
2. 3개월 성적표 — MRR $1,700과 리텐션 12.9%
먼저 좋은 소식. Amy 출시 3개월 시점의 지표:
Amy의 3개월 지표
- 월간 반복 수익(MRR): 약 $1,700
- 지난 30일 매출: 약 $2,200
- 지난 2주 동안 MRR +$200 증가
- 1주차 리텐션: 10% → 12.9% (100명 가입 → 13명이 1주 후에도 사용)
- 유료 사용자: 166명
Chris가 진짜 신경 쓰는 지표는 매출이 아니라 1주차 리텐션이다. 2주 만에 3%p 증가는 통계적으로 의미 있게 다루기엔 짧지만, 방향이 위인 것만으로 그는 만족한다.
3. 사고 1: Supabase 5시간 다운 — 인증 예외의 함정
이번 주 첫 번째 사고는 백엔드 제공업체 Supabase의 5시간 다운이다. Chris는 안심하고 있었다. Amy에는 이미 오프라인 모드가 있었기 때문이다. Subway 같은 곳에서 인터넷이 끊기면 오프라인 표시가 뜨고, 주문한 항목은 큐에 쌓였다가 재접속 시 처리된다. 그런데:
Chris가 미처 몰랐던 것
Supabase가 완전히 오프라인 상태가 되면 인증(auth)이 제대로 작동하지 않는 예외 케이스가 있다.
결과: 앱이 5시간 동안 빈 흰색 화면만 표시.
4. 5시간 흰 화면과 웹사이트 이메일로 몰려온 유저들
흰 화면 자체도 끔찍하지만 더 심각한 문제는 고객 지원 버튼에도 접근할 수 없다는 것이었다. 원래 유저는 앱 설정에서 지원 버튼을 눌러 연락하는데, 앱 자체가 안 뜨니 불가능.
결국 유저들은 웹사이트로 가서 지원 이메일을 찾아 이메일·인스타그램·트위터로 연락을 보냈다. Chris는 유저 이메일로 자기 앱이 다운됐다는 사실을 처음 알았다. 다행히 이 앱의 유저 상당수가 개발자·기술 전문가라서 Supabase 다운이라고 설명하니 다들 이해해 주었다. "아, 그렇군요, 알려주셔서 감사합니다."
5. 다시는 흰 화면이 안 뜨도록 — 상단 상태 배지 설계
Chris가 사고 이후 즉시 만든 방어책:
상단 상태 배지 (Amy에 추가된 것)
- 흰 화면 완전 제거. Supabase가 다운돼도 앱은 뜬다.
- 상단에 작은 상태 표시. 클릭하면 상세 정보가 열린다.
- Chris가 원격으로 커스텀 메시지를 넣을 수 있는 섹션. "저희 DB 제공업체가 다운됐어요, 몇 시간 후 확인해 주세요."
- 아래쪽에는 서비스별 이용 가능·불가능을 보여주는 창.
- 30초마다 상태 재확인. 유저가 앱을 껐다 켰다 하지 않아도 자동 복구.
Chris의 통찰: "내가 다른 앱들에서 오프라인 상태일 때 있었으면 하는 기능이었다." 사고 뒤 자기 백엔드 엔드포인트가 다운될 경우를 대비한 두 번째 상태 배지도 추가했다. 시뮬레이션 토글도 설정에 넣어 실기기에서 테스트할 수 있게 했다.
6. 상태 페이지의 재귀적 함정 — 다운된 곳에 저장하지 마라
Chris가 배지를 구현한 지 하루 만에 문득 든 생각이 이 영상에서 가장 중요한 교훈 중 하나다.
재귀적 함정
"Supabase에서 커스텀 메시지를 가져오고 있는데, Supabase가 오프라인이면 그 메시지는 어떻게 되지?"
큰 회사들의 상태 페이지가 다운된 인프라 위에 올려져 있어서 함께 죽는 걸 여러 번 목격했다.
저는 그럴 때마다 어리석다고 생각했는데, 정확히 같은 실수를 저지르고 있었다.
교훈: 상태 페이지·커스텀 메시지는 지금 모니터링하는 인프라 밖에 저장하라. 이걸 놓치면 사고가 났을 때 사고 알림 자체가 실패한다.
7. 사고 2: Perplexity Sonar 15분 다운 — 왜 fallback을 수동으로 만들었나
이틀 후 두 번째 사고. Amy가 칼로리 계산에 쓰는 Perplexity Sonar가 15분 다운. Perplexity 팀이 15분 만에 복구했지만, 그 15분 동안 이메일이 폭주했다.
Chris는 OpenRouter를 AI 인프라 프로바이더로 쓴다. OpenRouter에는 fallback model 기능이 있다. 첫 번째 모델이 실패하면 자동으로 두 번째로 넘어간다. 그런데 Chris는 이 기능을 일부러 안 쓴다. 이유가 결정적이다.
OpenRouter 자동 fallback을 안 쓰는 이유
자동 fallback으로 넘어간 두 번째 모델이 JSON 지침을 제대로 따르지 않는 경우가 있다.
그래서 Chris는 fallback을 수동으로 구현했다.
Sonar 실패 시 → Gemini 2.5 Flash Lite + Exa로 넘어간다.
가격은 Sonar보다 약간 비싸지만 결과는 거의 동등.
유저는 fallback 발동을 전혀 눈치채지 못한다.
Chris의 회고: "사실 이 두 번의 사고가 지금 일어난 게 다행이다. 유료 사용자 166명일 때 겪었으니. 1,000명이었으면 훨씬 스트레스였을 것."
8. 사고 3: Supabase RLS의 프리미엄 & rate limit 노출
세 번째는 보안 사고였다. 다행히 데이터 유출은 없었지만, 누군가 취약점을 발견하고 제보했다. Chris는 이 이야기를 공개하는 이유를 명시한다. "제 실수에서 배우실 수 있게."
기본 세팅은 표준적이었다. Supabase의 Row Level Security (RLS) — 유저가 자기 데이터만 접근할 수 있게. 그리고 스팸 요청을 막기 위한 rate limit — Amy는 유저가 음식을 입력할 때마다 Perplexity Sonar를 호출하는데 1건당 약 $0.005가 든다. 1,000건 스팸이면 상당한 청구서.
Chris의 실수는 이 두 값을 users 테이블에 저장한 것이었다.
실수의 구조
- users 테이블에 구독 상태 + rate limit을 저장.
- RLS 정책: 유저가 자기 row 전체를 수정 가능.
- 이름·선호도 수정은 문제없음.
- 그러나 구독 상태를 프리미엄으로 바꾸는 것도 가능.
- 더 심각한 것: rate limit을 100만 건으로 올려서 Chris의 엔드포인트에 스팸 공격 가능 → API 청구서 폭탄.
Chris의 반응이 흥미롭다. 프리미엄 자가 부여에는 크게 신경 안 썼다. "15살 때 나도 폰을 탈옥해서 프리미엄 앱을 다 썼다. 스스로 알아냈으면 자격 있는 거다." 한 유저가 프리미엄을 뚫었다고 알려왔을 때도 "걱정 마세요, 계속 가지세요"라고 답했다.
그러나 rate limit 수정 가능성은 완전히 다른 문제였다. 이건 그의 계좌에서 돈이 나가는 문제다.
9. 48시간 vibe coding의 후과 — 별도 테이블이 정답이었다
Chris의 해법:
- 임시 해법: 특정 컬럼은 읽기만 허용. 쓰기는 백엔드 같은 신뢰 소스에서만.
- 더 나은 해법: 별도 테이블에 구독 상태·rate limit을 분리 저장.
그의 회고: "솔직히 너무 서둘렀던 탓이다. 이 앱 전체를 48시간에 만들었다. 이건 엄청난 실수였지만 덮고 싶지 않았다. 저도 이런 실수를 한다는 걸 보여주고 싶다."
10. 보안 4단 스택: Rate Limit · Kill Switch · Budget Cap · Env Vars
Chris가 지금 Amy에 걸어둔 보안 스택 정리. 이 부분이 이 영상의 실용 정수다.
Chris의 4단 보안 스택
- Rate Limit (2중)
- 계정별 사용량 제한 (AI 콜마다 체크, 초과 시 차단 + 지원팀 안내).
- IP 기반 제한 — 계정을 여러 개 만들어도 같은 IP면 안 됨.
- 대시보드에서 유저별 제한을 수정·확인 가능.
- Kill Switch
- 특정 유저의 AI 접근을 완전히 차단하는 플래그.
- 한도 근접·의심 행동 시 사용.
- Budget Cap (서비스별 예산 상한)
- OpenRouter에 hard budget cap 설정.
- Amy는 월 $300~400 사용, 상한은 $1,000.
- 상한 초과 시 모든 AI 서비스가 자동 오프. 이메일 알림.
- OpenAI·OpenRouter·Anthropic 모두 이 기능이 있다. 기본이 아니니 반드시 켜라.
- Environment Variables 백엔드 격리
- 민감 env vars를 코드베이스나 프런트엔드에 저장하지 마라. vibe-coded 앱에서 흔한 실수.
- Claude/Cursor에게 "보안 검사 좀 해줘"라고 물어라. 대개 잡아준다.
- 절대 하지 말 것: 프런트엔드에서 AI 프로바이더를 직접 호출. 리버스 엔지니어링으로 키가 뽑힌다.
- 정답 구조: Frontend → Backend → AI Provider.
11. Whisperflow와 음성 코딩 워크플로우
Chris가 사용하는 워크플로우: Claude/Cursor에 음성으로 모든 지시. 이렇게 하면 타이핑보다 훨씬 자세한 프롬프트를 낼 수 있다.
도구: Whisperflow. Chris가 특별히 선택한 이유는 개발자 용어 인식이 뛰어나기 때문이다. "supabase useEffect mcp" 같은 조합을 정확히 잡아낸다. Claude Code·Cursor·Windsurf 통합도 우수 — 파일과 변수를 말로 태그할 수 있다. "openrouter.js에 있는 구현부 좀 봐줘"라고 말하면 파일 태그가 자동으로 붙는다.
iOS + Android 모두 지원(안드로이드는 이번 영상 촬영일에 막 출시). Slack·iMessage·WhatsApp에서도 쓰고, Chris는 YouTube 댓글 답변에도 항상 쓴다.
12. 다크모드로 숨쉬기 — Cecilia의 일러스트 재작업
힘든 한 주 끝에 Chris는 재미있는 걸 하기로 했다. Amy에 다크모드를 구현. 색상 조정은 빨랐지만, 일러스트 재작업이 시간을 잡아먹었다. 이 일을 다 해준 약혼자 Cecilia에게 공개 감사.
다크모드는 그에게 보안 작업들 사이의 좋은 숨돌리기였다.
클로징: 이번 주에 3건의 주요 사고가 있었지만, 백업 시스템·장애 알림 메시지·강화된 보안 등 처음부터 있었어야 할 것들이 이제 다 갖춰졌다. 수치는 상승 추세. Chris는 지금 이 앱의 상태에 매우 만족한다.
핵심 요약 (Takeaways)
이 후기에서 챙겨야 할 실질 인사이트
- Supabase의 완전 다운은 인증까지 죽인다. 오프라인 모드가 있어도 인증 실패 예외 경로를 실제로 시뮬레이션해서 흰 화면이 뜨지 않는지 확인해라. 상단 상태 배지 + 30초 자동 재시도가 표준.
- 상태 페이지·커스텀 메시지를 다운된 인프라 위에 올리지 마라. 같은 Supabase에 저장한 커스텀 메시지는 Supabase 다운 시 함께 죽는다. 재귀적 함정이다.
- OpenRouter 자동 fallback은 JSON 응답 앱에서는 조심하라. 두 번째 모델이 JSON 지침을 안 따르는 경우가 있다. 수동 fallback이 안전. Sonar 실패 → Gemini 2.5 Flash Lite + Exa 조합이 Chris의 실전 스택.
- Supabase RLS에서 users 테이블에 구독 상태·rate limit을 저장하지 마라. 유저가 자기 row 수정 가능이면 rate limit을 100만으로 올려 API 청구서 폭탄을 유발할 수 있다. 별도 테이블 + 백엔드에서만 쓰기 허용이 정답.
- 보안 4단 스택: Rate Limit(계정+IP) · Kill Switch · Budget Cap · Env Vars 백엔드 격리. 이 4개는 vibe coding으로도 반드시 갖춰야 한다.
- OpenRouter/OpenAI/Anthropic의 hard budget cap은 기본이 아니다. 반드시 켜라. 월 사용액의 2~3배로 상한을 잡고, 초과 시 자동 오프 + 이메일 알림. 이게 최후의 안전장치다.
- 프리미엄 자가 부여는 참을 수 있어도 rate limit 조작은 참을 수 없다. Chris가 15살 자기 자신을 회상하며 프리미엄 뚫기에는 관대했던 반면, rate limit 위반에는 즉시 대응한 이유는 명확하다. 돈이 나가는 위험만이 진짜 위험이다.
참조
- 원본 영상: How My App Is Doing (Not Good) — Chris (Amy calorie tracker)
- 대상 앱: Amy — Apple Notes 스타일 칼로리 트래커
- 사용 스택: Supabase (백엔드·auth·DB), OpenRouter (AI 라우터), Perplexity Sonar (기본 모델), Gemini 2.5 Flash Lite + Exa (fallback), Claude Code · Cursor · Windsurf, Whisperflow (음성-텍스트)