배포: 인터넷에 공개한다
부끄러울 만큼 일찍 — 린 스타트업과 E1 회색지대의 외부 노출
📑 이 챕터에서 다룰 내용
새 18장에서 수익화 모델이 SPEC §7에 박혔습니다. 이제 실제 인터넷에 공개하는 자리입니다. 이 장의 핵심은 단순히 "Vercel·Supabase 설정"이 아니라 E1 회색지대 결정의 외부 노출 — About 페이지·약관·면책 텍스트가 어떻게 본문에서 외부 세계로 나가는지입니다.
| 사전 지식 체크 | 이 장의 목적 | 완료 후 결과물 |
|---|---|---|
| Phase 1 BUILD 완료 / 새 18장 수익화 모델 확정 / Vercel·Supabase 계정 | 앱을 실제 인터넷에 배포 + 환경변수 안전 설정 + E1 회색지대의 외부 노출 (About·약관·면책) | 공개 URL + 안전한 환경변수 + E1 외부 노출 일관 + 첫 사용자 접근 가능 |
에릭 리스의 핵심 원칙: "부끄러울 만큼 일찍 출시하라." 완벽해질 때까지 기다리지 말고, 출시 후 피드백을 받아 개선하세요.
왜 일찍 출시할까요? 아무리 잘 만들어도 실제 사용자가 원하는 것과 다를 수 있기 때문입니다. 빠른 출시 → 실제 피드백 → 개선의 사이클이 느린 완성보다 항상 더 나은 결과를 냅니다.
E2 (1인 페이스)와의 결합: 1인 운영자에게 "완벽주의"는 가장 위험합니다. "이것까지 만들고 출시"가 반복되면 영영 출시 못합니다. 린 스타트업 원칙이 1인 운영자의 R4 (번아웃)를 막는 핵심입니다.
| 도구 | 배포 명령 |
|---|---|
| GSD | /gsd-ship N (Phase N 출시) |
| Spec-Kit | (배포는 별도 단계) |
| OpenSpec | proposal archive (코드 머지 = 배포) |
5파일+의 "배포"는 BUILD.md의 마지막 entry + 환경변수 + URL 공개입니다. 표준 도구의 "ship"보다 가볍습니다.
배포 전 가장 흔한 보안 사고 두 가지를 먼저 짚어드립니다.
| 사고 | 원인 | 결과 |
|---|---|---|
| API 키 GitHub 노출 | .env 파일 git commit | 유출 후 몇 분 안에 악용 |
| Supabase service_role 키 프론트엔드 사용 | 클라이언트 코드에 키 직접 입력 | RLS 우회 가능, DB 전체 노출 |
이 두 사고는 매일 수백 건 발생합니다. 새 11장 CLAUDE.md §3 Critical Constraints에 "API 키 노출 금지"가 박혀 있으므로 Claude Code가 자동 점검하지만, 인간 검토도 필수입니다.
환경변수 안전 분류 표
| 환경변수 | 어디 사용 | 어디 저장 | 노출 가능 여부 |
|---|---|---|---|
NEXT_PUBLIC_* (Next.js) | 프론트엔드 OK | Vercel Environment Variables | 공개 가능 |
EXPO_PUBLIC_* (Expo) | 프론트엔드 OK | EAS Secrets | 공개 가능 |
SUPABASE_ANON_KEY | 프론트엔드 OK | 위 | 공개 가능 |
SUPABASE_SERVICE_ROLE_KEY | Edge Function만 | Supabase Secrets | 절대 비공개 |
STRIPE_SECRET_KEY | Edge Function만 | Supabase Secrets | 절대 비공개 |
ANTHROPIC_API_KEY | Edge Function만 | Supabase Secrets | 절대 비공개 |
CLAUDE.md §8 Forbidden Patterns에 정규식으로 차단합니다 (새 11장 11-2절 참조).
Next.js (Vercel) 30분 배포
# 1. GitHub에 코드 push (이미 git commit 되어 있어야 합니다) git push origin main # 2. vercel.com 가입 → New Project → GitHub 연결 → 자동 빌드 # 3. Environment Variables 설정 (Vercel Dashboard) # - NEXT_PUBLIC_SUPABASE_URL = https://xxx.supabase.co # - NEXT_PUBLIC_SUPABASE_ANON_KEY = eyJ... # (SUPABASE_SERVICE_ROLE_KEY와 STRIPE_SECRET_KEY는 Vercel에 NOT) # (위 두 개는 Supabase Edge Function Secrets에) # 4. 배포 완료 시 자동 URL 발급 # https://[프로젝트명].vercel.app # 5. 커스텀 도메인 연결 (선택) # DNS A 레코드 → 76.76.21.21
Expo (EAS Build) — 모바일 앱
# 1. eas.build 설치 npm install -g eas-cli # 2. 로그인 eas login # 3. 프로젝트 설정 eas build:configure # 4. iOS 빌드 (Mac 필요 X) eas build --platform ios # 5. Android 빌드 eas build --platform android # 6. 앱스토어 제출 eas submit --platform ios eas submit --platform android
Supabase Edge Function 배포
# 1. Supabase CLI npm install -g supabase # 2. 로그인 + 프로젝트 연결 supabase login supabase link --project-ref [PROJECT_REF] # 3. Function 배포 supabase functions deploy [function-name] # 4. Secrets 설정 supabase secrets set STRIPE_SECRET_KEY=sk_live_... supabase secrets set ANTHROPIC_API_KEY=sk-ant-... supabase secrets set SUPABASE_SERVICE_ROLE_KEY=eyJ...
배포 시점이 5확장 E1 (회색지대 결정)의 본문 → 외부 세계 전환 자리입니다. SPEC.md에 박힌 결정이 About 페이지·약관·면책으로 외부에 노출됩니다.
외부 노출 체크리스트
[About 페이지]
- E1 회색지대 결정 한 줄 명시
- 포함 / 배제 항목 명시
- 변호 논리 (단속·신고 시) 한 줄
[약관 (Terms of Service)]
- E1 결정 반영 (예: "신청 결과 보장하지 않음")
- 환불 정책 (수익화 있으면)
- 사용자 의무·권리
[개인정보처리방침 (Privacy Policy)]
- 수집 정보 목록 (E1 결정 일치)
- 미수집 명시 (예: "IDFA 수집 안 함")
- 보관 기간·삭제 정책
[Footer 또는 콘텐츠 면책]
- 콘텐츠 페이지마다 짧은 면책 (E1 한 줄)
- 약관·개인정보 링크
4가지 자리에 같은 메시지가 일관되게 노출돼야 변호 논리가 사실로 성립합니다.
줍줍 외부 노출 사례
[About 페이지]
"줍줍은 정부 지원금 정보를 정리하고 사용자 후기를 공유하는 미디어입니다. 본 서비스는 정보 제공을 목적으로 하며, 신청 결과를 보장하지 않습니다. 개인정보를 판매하지 않으며, IDFA를 수집하지 않습니다."
[약관 §3 — 서비스 범위]
"본 서비스는 정부 지원사업 공고를 수집·정리하고 사용자가 공유한 경험을 노출합니다. 본 서비스는 신청 대행 서비스가 아니며, 신청 결과를 보장하지 않습니다."
[개인정보처리방침 §1 — 수집 정보]
수집 정보: 닉네임 (자동 부여, 본인 입력 X) / 카카오 ID (로그인용) / 자발적 입력: 사업장 시군구·업종·매출 단계 (필터링용)
수집 안 함: 광고 식별자 (IDFA) / 위치 정보 (자동 수집) / 결제 정보 (Stripe 위임)
[Footer]
"본 서비스는 정보 제공 목적이며, 신청 결과를 보장하지 않습니다. | 약관 | 개인정보처리방침 | About"
TSV 외부 노출 사례
[About 페이지]
"TotalSportsView는 스포츠 분석 미디어입니다. 5개 리그의 경기를 2개 페르소나 (STAT·OBSERVER)가 다관점으로 분석합니다. 본 서비스는 분석 콘텐츠 제공이 목적이며, 베팅 사이트로 연결하지 않고 베팅을 권유하는 콘텐츠를 만들지 않습니다 (Position C)."
[약관 §2 — 서비스 범위]
본 서비스는 스포츠 분석 콘텐츠를 제공합니다. 본 서비스는:
- 베팅 사이트로의 직접 연결을 제공하지 않습니다
- 배당률을 한국식 배당으로 변환하지 않습니다
- 픽 (선택) 추천 형식의 콘텐츠를 제공하지 않습니다
[콘텐츠 페이지 상단 면책 (모든 페르소나 글)]
"이 글은 분석 콘텐츠입니다. 베팅 결정의 근거가 아니며, 베팅 결과에 대해 책임지지 않습니다."
[Footer]
"분석 콘텐츠 미디어 | Position C | About | Terms | Privacy"
배포 완료 후 30분 안에 점검할 항목입니다.
| 점검 | 방법 | 통과 기준 |
|---|---|---|
| 공개 URL 접근 | 브라우저 시크릿 탭 (캐시 X) | 200 OK + 페이지 정상 |
| 환경변수 누락 | /api/healthcheck 엔드포인트 호출 | 모든 외부 서비스 OK |
| HTTPS 적용 | URL https:// 시작 | Vercel·Expo 자동 |
| Lighthouse 점수 | Chrome DevTools | Performance ≥ 80 |
| 모바일 반응형 | 폰에서 직접 접근 | 깨짐 없음 |
| About·약관·개인정보 링크 | 전부 클릭 | 모두 200 OK |
| E1 외부 노출 4자리 ★ | About + 약관 + 개인정보 + Footer 일관 | 모두 같은 메시지 |
| Sentry 연결 | DSN 설정 | 첫 에러 자동 수집 |
| GA / Plausible | 측정 ID 설정 | 첫 방문 기록 |
배포 직후 가장 중요한 점검 — 본문 결정과 외부 노출 일관성. 이 항목이 빠지면 6개월 후 변호 논리 성립에 문제가 생깁니다.
결정: 앱스토어 심사 제출 — IDFA 미수집 + 최소 정보 수집 명시
근거: SPEC §9 R3 (애플 심사 거절) 완화책. PIPA 준수 + IDFA 미수집 명시 → 심사 통과 가속.
외부 노출 4자리 점검:
- About: "정보 격차를 줄임, 신청 결과 보장 X" 노출
- 약관: "신청 대행 서비스 아님" 명시
- 개인정보: "IDFA 수집 안 함" 명시
- Footer: 일관 메시지
Apple Privacy Manifest 작성:
- PrivacyTrackingEnabled: false
- PrivacyAccessedAPITypes: 명시
결과: 1차 심사 → 추가 정보 요청 (1건) → 1일 내 답변 → 통과
줍줍 배포 후 첫 사용자 확보
목표: 1순위 커뮤니티 게시 ("아프니까 사장이다")
전략:
- 직접 줍줍 10개 + 1인칭 후기 10개 시딩 (이미 1주차 완료)
- 커뮤니티 게시: "[줍줍] 정부 지원금 정보 + 후기 공유 앱 (베타)"
- 명시: "베타 단계 무료, 6개월 후 프리미엄 도입 (월 2,900원)"
← E2 1인 페이스 + 정직성 (사용자가 미래 비용 인지)
결과 (Day 75): 가입 50명 + 후기 작성 8명 + 줍줍 클릭 약 200건
결정: Vultr ($6/월) + Cloudflare WAF (무료) + Position C About 즉시 노출
근거: SPEC v4 G1 (Day 1) 게이트 — legal/about/cookie 200 OK + WAF 활성
외부 노출 4자리 (Day 1 기준):
- About: Position C 한 페이지 (5개 포함 / 4개 배제 / 변호 논리)
- Terms: 약관 (베팅 연결·픽 추천 없음 명시)
- Privacy: 개인정보 (콘텐츠 사용자 비식별)
- Footer: "분석 콘텐츠 미디어 | Position C" 일관
WAF 규칙:
- 도박 관련 검색 키워드 SEO 차단
- API 비용 폭주 방어 (비정상 요청 차단)
결과: Day 1 G1 통과 — 모든 4자리 200 OK + WAF 활성
TSV 배포 후 콘텐츠 자동화 시작
Day 2: 데이터 수집 (TheSportsDB) + 5리그 경기 5건/일 시딩
Day 3: 페르소나 system prompt + cache_control + ANTHROPIC_API_BETA
Day 4~7: SSOT 인터페이스 + match_facts + 자동 일관성 테스트 (E5)
Day 7 자동 글 발행 시작:
- STAT 페르소나: 통계 분석 글 (5리그 × 1글/일 = 5글/일)
- OBSERVER 페르소나: 균형 분석 글 (5리그 × 1글/일 = 5글/일)
- 합계: 10글/일
각 글 상단에 면책 자동 삽입 (Position C 일관)
📌 19장 정리
- 핵심 한 줄: 배포 = 환경변수 안전 + E1 회색지대 외부 노출 4자리 + 30분 체크리스트.
- 린 스타트업 × E2: "부끄러울 만큼 일찍 출시" — 1인 운영자의 완벽주의 = R4 번아웃 가속. 일찍 출시가 페이스 보호입니다.
- 환경변수 안전 분류:
*_PUBLIC_*/ANON_KEY→ 프론트 OK.*_SECRET_*/SERVICE_ROLE_KEY→ Edge Function만, 절대 비공개 - 30분 배포: Next.js → Vercel / Expo → EAS Build / Edge Functions → Supabase
- E1 외부 노출 4자리 (배포 시 핵심 점검): ① About 페이지 ② 약관 ③ 개인정보처리방침 ④ Footer 또는 콘텐츠 면책 — 4자리에 같은 메시지 = 변호 논리 사실 성립
- 두 사례: 줍줍 "정보 격차를 줄임, 신청 결과 보장 X, IDFA 수집 X" 4자리 일관 / TSV "Position C, 베팅 연결·픽 추천 X" 4자리 일관 + WAF 차단
- 배포 직후 30분 체크리스트: URL 200 OK · 환경변수 누락 X · HTTPS · Lighthouse 80+ · 모바일 반응형 · About/약관/개인정보 200 OK · ★ E1 외부 노출 4자리 일관 · Sentry · GA
앱이 인터넷에 공개됐고, E1 회색지대 결정이 외부 4자리에 일관 노출됐습니다. 6개월 후 단속·신고 발생 시 변호 논리가 사실로 성립합니다.
다음 장에서 CI/CD · 운영 · 보안 (정정렬)을 다룹니다.