Skip to main content
Summary

출시 전 점검의 목표는 '무사고'가 아니다

이제 우리 서비스에는 리뷰 업로드, AI 분석 리포트, 공유 링크, 1회 분석권 결제 흐름이 붙었다. 여기서 바로 공개 링크를 뿌리고 싶겠지만, 출시 전에는 한 번 멈춰야 한다.

목표는 모든 장애를 없애는 것이 아니다. 1인 창업가에게 그 기준은 현실적이지 않다. 대신 기준을 이렇게 잡는다.

문제가 생겼을 때 고객 데이터가 새지 않고, 비용이 폭주하지 않으며, 내가 원인을 찾아 복구할 수 있는 상태.

출시 전 점검은 큰 회사의 보안 심사가 아니다. 우리가 만든 작은 AI SaaS가 실제 고객 10명, 100명, 1,000명을 만났을 때 혼자 감당할 수 있는지 확인하는 과정이다.

이 장에서는 네 가지를 정한다.

  1. 초보자가 자주 놓치는 보안 구멍

  2. 사용자 수에 따른 월 비용 계산

  3. 오류를 발견하고 재현하는 루틴

  4. AI 응답 실패와 지연에 대한 대체 흐름

출시 전에 챙기는 보안, 비용, 품질AI 생성 이미지

가장 먼저 막아야 할 보안 실수

첫 출시에서 가장 위험한 보안 문제는 고급 해킹보다 기본 실수에서 나온다. 특히 API 키, 데이터 권한, 파일 업로드, 결제 웹훅을 확인해야 한다.

API 키가 브라우저에 노출되지 않았는가

Next.js와 Supabase를 함께 쓰면 환경 변수를 여러 곳에 둔다. 여기서 중요한 구분은 공개되어도 되는 값과 절대 공개되면 안 되는 값이다.

NEXT_PUBLIC_으로 시작하는 값은 브라우저 코드에 포함될 수 있다. Supabase의 publishable key처럼 클라이언트에서 쓰도록 설계된 값은 여기에 둘 수 있다. 반대로 Supabase service_role 키, AI API 키, 결제 시크릿 키는 절대 NEXT_PUBLIC_으로 시작하면 안 된다. Supabase의 Next.js 예제도 클라이언트에서 사용할 공개 URL과 publishable key를 환경 변수로 두는 구조를 사용한다1.

출시 전에는 다음을 확인한다.

  • .env.local이 GitHub에 올라가 있지 않은가

  • Vercel Production 환경 변수에 실제 운영 키가 들어가 있는가

  • Preview와 Development에 운영 결제 키를 넣지 않았는가

  • AI API 키가 클라이언트 컴포넌트나 브라우저 호출 코드에 쓰이지 않았는가

  • 로그에 API 키 일부가 출력되지 않는가

한 번이라도 비밀 키를 GitHub에 올렸다면 삭제만으로 끝내면 안 된다. 해당 키는 폐기하고 새로 발급해야 한다.

사용자 데이터가 서로 섞이지 않는가

우리 서비스에는 stores, review_batches, reviews, analysis_jobs, reports, payments, entitlements 같은 테이블이 있다. 대부분은 특정 사용자에게 속한 데이터다. 이 데이터는 서버 코드에서만 조심한다고 안전해지지 않는다. 데이터베이스 단계에서 막아야 한다.

Supabase의 Row Level Security, 즉 RLS는 테이블의 각 행마다 접근 조건을 거는 기능이다. RLS를 켜면 정책이 없는 한 API를 통한 접근이 막히고, 정책은 쿼리마다 조건문처럼 적용된다2.

MVP 기준 점검표는 단순하다.

  • 사용자 데이터가 있는 모든 테이블에 RLS를 켠다.

  • user_id가 있는 테이블은 auth.uid() = user_id 조건으로 보호한다.

  • SELECT, INSERT, UPDATE, DELETE에 필요한 정책을 각각 만든다.

  • 브라우저에서는 service role 키를 절대 쓰지 않는다.

  • 공유 리포트는 로그인 사용자 권한과 별도로 share_token, is_public 조건으로만 읽게 한다.

특히 공유 링크가 위험하다. 리포트 공유 기능을 만들 때 "토큰만 알면 볼 수 있는 읽기 전용 페이지"를 의도했다면, 대시보드용 조회 정책과 공유용 조회 정책을 섞지 말아야 한다. 로그인한 사용자는 자기 리포트만 보고, 공유 페이지는 공개 설정된 리포트만 토큰으로 읽어야 한다.

파일 업로드가 비용과 보안 구멍이 되지 않는가

리뷰 업로드는 작아 보이지만 공격 지점이 될 수 있다. 누군가 500MB 파일을 올리거나, CSV 안에 이상한 문자를 넣거나, 같은 리뷰를 반복 업로드할 수 있다.

첫 버전의 업로드 제한은 명확해야 한다.

항목출시 전 기준
파일 형식.csv, .txt만 허용
파일 크기예: 2MB 이하
리뷰 수1회 업로드 30~300개
리뷰 길이너무 짧거나 긴 리뷰 제외 또는 경고
중복content_hash로 같은 본문 감지
저장 위치비공개 버킷 기본값
원본 파일필요 없으면 분석 후 삭제 또는 보관 기간 명시

CSV를 화면에 다시 보여줄 때도 주의한다. 파일명이나 리뷰 본문을 HTML로 그대로 렌더링하지 말고 텍스트로 처리한다. 엑셀 수식처럼 보이는 값은 다운로드 파일을 만들 때 문제가 될 수 있으므로, 내보내기 기능을 붙일 때 별도 처리가 필요하다.

결제 웹훅을 그대로 믿지 않는가

지난 장에서 결제 성공 페이지가 아니라 웹훅을 기준으로 권한을 부여하자고 했다. 여기서 한 가지가 더 필요하다. 웹훅 요청이 진짜 결제사에서 왔는지 확인해야 한다.

웹훅 URL은 비밀이 아니다. 누군가 URL을 알면 비슷한 요청을 보낼 수 있다. 따라서 결제사 문서에서 제공하는 서명 검증 또는 인증 방식을 따라야 한다. 일반적으로 안전한 웹훅 검증은 원본 요청 본문, 서명 헤더, 공유 시크릿을 사용해 HMAC 값을 계산하고, 타이밍 공격에 안전한 방식으로 비교한 뒤 맞지 않으면 401로 거부하는 흐름이다3.

결제 웹훅 처리 기준은 다음과 같다.

  • 성공 페이지 진입만으로 entitlements를 주지 않는다.

  • 웹훅 서명 또는 결제사 권장 인증을 검증한다.

  • order_id, payment_key, amount, product_id가 우리 기록과 맞는지 확인한다.

  • 같은 웹훅이 여러 번 와도 한 번만 처리되게 만든다.

  • 결제 금액이 다르면 권한을 주지 않고 오류로 남긴다.

웹훅은 중복으로 올 수 있다. 이것은 비정상이 아니다. 그래서 payments.provider_event_id나 payment_key에 unique 제약을 걸고, 이미 처리한 이벤트면 성공 응답만 반환하는 방식이 필요하다.

비용은 기능 단위가 아니라 사용자 행동 단위로 계산한다

AI SaaS의 비용은 "사용자 수"보다 "사용자가 몇 번 분석을 돌리는가"에 더 크게 좌우된다. 그래서 출시 전에는 가격표가 아니라 사용량표를 먼저 만들어야 한다.

우리 서비스의 예시 가정을 이렇게 둔다.

  • 사용자 1명은 월 2회 리포트를 생성한다.

  • 리포트 1건은 리뷰 100개를 분석한다.

  • 리포트 1건당 AI 비용은 안전계수를 포함해 0.03달러로 잡는다.

  • 리포트 1건당 DB와 스토리지 증가량은 1MB로 잡는다.

  • 사용자 1명당 월 트래픽은 리포트 조회와 공유를 포함해 30MB로 잡는다.

  • 사용자 1명당 월 이메일은 로그인, 결제, 리포트 알림을 포함해 5통으로 잡는다.

실제 단가는 사용하는 AI 모델, Vercel·Supabase 요금제, 이메일 도구에 따라 바뀐다. 아래 표는 의사결정을 위한 계산 예시다.

월 활성 사용자월 리포트 수AI API 예상DB/스토리지 증가배포 트래픽이메일 발송
10명20건약 $0.60약 20MB약 0.3GB50통
100명200건약 $6약 200MB약 3GB500통
1,000명2,000건약 $60약 2GB약 30GB5,000통

이 표에서 보듯 초기에는 AI 비용보다 기본 요금제가 더 클 수 있다. Supabase, Vercel, Sentry, 이메일 도구의 무료 한도를 넘는 순간 월 고정비가 생긴다. 반대로 사용자가 1,000명 수준으로 늘면 AI 비용, DB 저장량, 트래픽, 이메일이 함께 움직인다.

그래서 결제 전환이 낮은 상태에서 무료 분석을 많이 열어두면 손실이 생긴다. 무료 사용권도 권한 구조 안에서 관리해야 하는 이유가 여기에 있다.

출시 전 비용 제한은 이렇게 둔다.

  • 무료 사용자는 샘플 리포트 또는 1회 제한만 제공한다.

  • 분석 시작 전에 예상 리뷰 수와 사용권 차감 여부를 보여준다.

  • AI 요청 실패로 재시도할 때 무제한 반복하지 않는다.

  • analysis_jobs에 입력 토큰, 출력 토큰, 모델명, 예상 비용을 저장한다.

  • 월 AI 비용이 예산의 50%, 80%, 100%에 도달하면 알림을 받는다.

처음부터 정확한 원가 계산 시스템을 만들 필요는 없다. 하지만 리포트 1건을 만들 때 대략 얼마가 드는지 모르면 가격 실험도 할 수 없다.

오류를 발견하는 루틴을 만든다

오류는 반드시 생긴다. 중요한 것은 고객이 말하기 전에 발견하고, 같은 문제를 다시 만들 수 있어야 한다는 점이다.

출시 전 최소 모니터링 조합은 다음 정도면 충분하다.

  • Vercel 로그: 배포 직후 서버 오류, API 라우트 실패 확인

  • Supabase Dashboard: DB 사용량, 느린 쿼리, 인증 문제 확인

  • Sentry: 프론트엔드와 서버 오류 수집

  • Logtail 또는 유사 로그 도구: 구조화된 운영 로그 검색

Sentry는 Supabase 클라이언트에서 발생한 쿼리와 인증 호출의 오류, 성능 정보를 추적할 수 있다. Supabase 공식 문서도 Supabase JS 클라이언트를 Sentry에 연결해 DB 쿼리와 인증 호출을 계측하는 방식을 안내한다4.

로그에는 모든 것을 넣으면 안 된다. 리뷰 원문, 고객 전화번호, 결제 카드 정보 같은 민감한 정보는 남기지 않는다. 대신 문제를 추적할 수 있는 식별자를 남긴다.

예를 들면 다음 필드가 유용하다.

request_id
user_id_hash
store_id
review_batch_id
analysis_job_id
report_id
payment_id
ai_model
latency_ms
retry_count
error_code

운영 루틴은 네 단계로 고정한다.

  1. 발견: Sentry 알림, Vercel 로그, 고객 문의로 문제를 확인한다.

  2. 분류: 결제·데이터 노출·전체 장애인지, 일부 기능 오류인지 나눈다.

  3. 재현: 같은 analysis_job_id나 payment_id로 로컬 또는 스테이징에서 다시 실행한다.

  4. 조치: 즉시 수정, 임시 우회, 사용자 안내, 재처리 중 하나를 선택한다.

심각도 기준도 미리 정해둔다.

등급예시대응
P0다른 사용자의 리포트 노출, 결제 권한 오지급즉시 기능 중지, 원인 수정, 고객 안내
P1결제 성공 후 분석권 미지급, 리포트 생성 전체 실패당일 수정, 수동 보정
P2특정 CSV 형식 업로드 실패, 일부 AI 출력 오류1~3일 내 수정
P3문구 깨짐, UI 정렬 문제다음 배포에 반영

1인 창업가는 모든 오류를 즉시 고칠 수 없다. 그래서 심각도 기준이 필요하다. 기준이 없으면 작은 UI 오류와 결제 오류를 같은 무게로 다루게 된다.

AI 실패는 예외가 아니라 정상 흐름이다

AI API는 가끔 실패한다. 응답이 늦을 수 있고, JSON 형식이 깨질 수 있으며, 우리가 원하지 않는 표현을 만들 수도 있다. 따라서 AI 실패를 "나중에 처리할 예외"로 두면 출시 후 바로 고객 경험이 무너진다.

우리 서비스의 AI 분석 흐름에는 네 가지 실패가 있다.

  1. API 호출 자체가 실패한다.

  2. 응답이 너무 늦다.

  3. JSON 스키마가 맞지 않는다.

  4. 내용은 생성됐지만 부적절하거나 근거가 약하다.

각 상황의 대응을 정해두자.

상황시스템 처리사용자 메시지
일시적 API 실패최대 2회 재시도"분석 요청이 지연되고 있습니다. 잠시 후 다시 시도합니다."
계속 실패작업 상태를 failed로 변경"리포트를 생성하지 못했습니다. 사용권은 차감하지 않았습니다."
응답 지연processing 상태 유지, 새로고침 가능"리뷰 수가 많아 분석 시간이 조금 더 걸리고 있습니다."
JSON 검증 실패1회 재생성 후 실패 시 중단"분석 결과 형식을 확인하는 중 문제가 발생했습니다."
부적절한 출력해당 섹션 제외 또는 재생성"일부 문구는 검토가 필요해 표시하지 않았습니다."

재시도는 반드시 제한해야 한다. 무한 재시도는 비용 폭주로 이어진다. analysis_jobs.retry_count를 두고, 실패한 작업을 사용자가 다시 실행할 수 있게 하되 같은 결제 건에서 이중 차감되지 않도록 처리한다.

또한 AI 결과를 바로 화면에 뿌리지 않는다. 10장에서 만든 것처럼 제품 JSON 스키마로 검증하고, 11장에서 만든 리포트 컴포넌트가 기대하는 형태에 맞는지 확인한 뒤 저장한다. evidence가 없는 강한 결론은 낮은 신뢰도로 표시하거나 제외한다.

사용자 메시지도 중요하다. "AI 오류"라고만 쓰면 고객은 무엇을 해야 할지 모른다. 대신 현재 상태와 다음 행동을 알려준다.

좋은 메시지 예시는 이렇다.

리포트 생성 중 문제가 발생했습니다. 결제된 분석권은 차감하지 않았습니다. 5분 뒤 다시 시도하거나, 같은 문제가 반복되면 문의해 주세요.

이 문장에는 세 가지가 들어 있다. 문제가 있다는 사실, 고객 손해가 없다는 설명, 다음 행동이다.

출시 버튼을 누르기 전 마지막 체크리스트

작게 출시하더라도 아래 항목은 확인하고 넘어가자.

  • 비밀 키가 GitHub와 브라우저 코드에 노출되지 않았다.

  • 사용자 데이터 테이블에 RLS가 켜져 있다.

  • 공유 리포트는 토큰과 공개 여부로만 접근된다.

  • 파일 업로드 크기, 형식, 리뷰 수 제한이 있다.

  • 결제 웹훅은 검증 후 처리한다.

  • 결제 성공 페이지가 아니라 서버 검증으로 권한을 준다.

  • 리포트 1건당 예상 AI 비용을 기록한다.

  • 무료 사용권의 횟수와 만료일이 정해져 있다.

  • Sentry 또는 오류 추적 도구가 연결되어 있다.

  • Vercel과 Supabase 로그를 확인하는 루틴이 있다.

  • AI 실패, 지연, 부적절한 출력에 대한 사용자 메시지가 있다.

  • P0, P1 오류가 생겼을 때 기능을 잠시 멈출 방법이 있다.

이 체크리스트를 통과했다고 해서 서비스가 안정적이라는 뜻은 아니다. 다만 첫 고객을 받을 준비가 되었다는 뜻이다.

다음 장에서는 이 상태의 제품을 조용히 숨겨두지 않고, 첫 고객에게 보여주는 런칭 전략을 만든다. 광고비를 크게 쓰기 전에 누구에게, 어떤 메시지로, 어떤 순서로 연락할지 정해야 한다. 보안과 비용의 바닥을 만든 뒤에야 출시 실험은 사업 실험이 된다.

참고

1Use Supabase with Next.js

2Row Level Security

3How to Implement SHA256 Webhook Signature Verification

4Sentry integration

책 목록

혼자서 다 하는 창업: 1인 창업가용 AI 풀스택

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기