19장 — 배포
CHAPTER 19

배포: 인터넷에 공개한다

부끄러울 만큼 일찍 — 린 스타트업과 E1 회색지대의 외부 노출

📑 이 챕터에서 다룰 내용

새 18장에서 수익화 모델이 SPEC §7에 박혔습니다. 이제 실제 인터넷에 공개하는 자리입니다. 이 장의 핵심은 단순히 "Vercel·Supabase 설정"이 아니라 E1 회색지대 결정의 외부 노출 — About 페이지·약관·면책 텍스트가 어떻게 본문에서 외부 세계로 나가는지입니다.

사전 지식 체크이 장의 목적완료 후 결과물
Phase 1 BUILD 완료 / 새 18장 수익화 모델 확정 / Vercel·Supabase 계정 앱을 실제 인터넷에 배포 + 환경변수 안전 설정 + E1 회색지대의 외부 노출 (About·약관·면책) 공개 URL + 안전한 환경변수 + E1 외부 노출 일관 + 첫 사용자 접근 가능
📘 이론적 배경 — 린 스타트업 (에릭 리스, 2011)

에릭 리스의 핵심 원칙: "부끄러울 만큼 일찍 출시하라." 완벽해질 때까지 기다리지 말고, 출시 후 피드백을 받아 개선하세요.

왜 일찍 출시할까요? 아무리 잘 만들어도 실제 사용자가 원하는 것과 다를 수 있기 때문입니다. 빠른 출시 → 실제 피드백 → 개선의 사이클이 느린 완성보다 항상 더 나은 결과를 냅니다.

E2 (1인 페이스)와의 결합: 1인 운영자에게 "완벽주의"는 가장 위험합니다. "이것까지 만들고 출시"가 반복되면 영영 출시 못합니다. 린 스타트업 원칙이 1인 운영자의 R4 (번아웃)를 막는 핵심입니다.

📘 표준 도구의 자리
도구배포 명령
GSD/gsd-ship N (Phase N 출시)
Spec-Kit(배포는 별도 단계)
OpenSpecproposal archive (코드 머지 = 배포)

5파일+의 "배포"는 BUILD.md의 마지막 entry + 환경변수 + URL 공개입니다. 표준 도구의 "ship"보다 가볍습니다.

19-1 환경변수 — 가장 흔한 보안 사고 🔗

배포 전 가장 흔한 보안 사고 두 가지를 먼저 짚어드립니다.

사고원인결과
API 키 GitHub 노출.env 파일 git commit유출 후 몇 분 안에 악용
Supabase service_role 키 프론트엔드 사용클라이언트 코드에 키 직접 입력RLS 우회 가능, DB 전체 노출

이 두 사고는 매일 수백 건 발생합니다. 새 11장 CLAUDE.md §3 Critical Constraints에 "API 키 노출 금지"가 박혀 있으므로 Claude Code가 자동 점검하지만, 인간 검토도 필수입니다.

환경변수 안전 분류 표

환경변수어디 사용어디 저장노출 가능 여부
NEXT_PUBLIC_* (Next.js)프론트엔드 OKVercel Environment Variables공개 가능
EXPO_PUBLIC_* (Expo)프론트엔드 OKEAS Secrets공개 가능
SUPABASE_ANON_KEY프론트엔드 OK공개 가능
SUPABASE_SERVICE_ROLE_KEYEdge Function만Supabase Secrets절대 비공개
STRIPE_SECRET_KEYEdge Function만Supabase Secrets절대 비공개
ANTHROPIC_API_KEYEdge Function만Supabase Secrets절대 비공개
⚠️ *_SECRET_*, *_SERVICE_ROLE_* 패턴은 모두 백엔드 전용

CLAUDE.md §8 Forbidden Patterns에 정규식으로 차단합니다 (새 11장 11-2절 참조).

19-2 Vercel·Supabase·EAS — 30분 배포 🔗

Next.js (Vercel) 30분 배포

💻 Next.js → Vercel 배포
# 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) — 모바일 앱

💻 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 배포

💻 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...
19-3 E1 회색지대의 외부 노출 🔗

배포 시점이 5확장 E1 (회색지대 결정)의 본문 → 외부 세계 전환 자리입니다. SPEC.md에 박힌 결정이 About 페이지·약관·면책으로 외부에 노출됩니다.

외부 노출 체크리스트

📘 4자리 외부 노출 체크리스트

[About 페이지]

  • E1 회색지대 결정 한 줄 명시
  • 포함 / 배제 항목 명시
  • 변호 논리 (단속·신고 시) 한 줄

[약관 (Terms of Service)]

  • E1 결정 반영 (예: "신청 결과 보장하지 않음")
  • 환불 정책 (수익화 있으면)
  • 사용자 의무·권리

[개인정보처리방침 (Privacy Policy)]

  • 수집 정보 목록 (E1 결정 일치)
  • 미수집 명시 (예: "IDFA 수집 안 함")
  • 보관 기간·삭제 정책

[Footer 또는 콘텐츠 면책]

  • 콘텐츠 페이지마다 짧은 면책 (E1 한 줄)
  • 약관·개인정보 링크
⚠️ 4자리에 같은 메시지가 일관되게 노출돼야 합니다

4가지 자리에 같은 메시지가 일관되게 노출돼야 변호 논리가 사실로 성립합니다.

줍줍 외부 노출 사례

📘 줍줍 — 4자리 외부 노출 텍스트

[About 페이지]

"줍줍은 정부 지원금 정보를 정리하고 사용자 후기를 공유하는 미디어입니다. 본 서비스는 정보 제공을 목적으로 하며, 신청 결과를 보장하지 않습니다. 개인정보를 판매하지 않으며, IDFA를 수집하지 않습니다."

[약관 §3 — 서비스 범위]

"본 서비스는 정부 지원사업 공고를 수집·정리하고 사용자가 공유한 경험을 노출합니다. 본 서비스는 신청 대행 서비스가 아니며, 신청 결과를 보장하지 않습니다."

[개인정보처리방침 §1 — 수집 정보]

수집 정보: 닉네임 (자동 부여, 본인 입력 X) / 카카오 ID (로그인용) / 자발적 입력: 사업장 시군구·업종·매출 단계 (필터링용)

수집 안 함: 광고 식별자 (IDFA) / 위치 정보 (자동 수집) / 결제 정보 (Stripe 위임)

[Footer]

"본 서비스는 정보 제공 목적이며, 신청 결과를 보장하지 않습니다. | 약관 | 개인정보처리방침 | About"

TSV 외부 노출 사례

📘 TSV — 4자리 외부 노출 텍스트

[About 페이지]

"TotalSportsView는 스포츠 분석 미디어입니다. 5개 리그의 경기를 2개 페르소나 (STAT·OBSERVER)가 다관점으로 분석합니다. 본 서비스는 분석 콘텐츠 제공이 목적이며, 베팅 사이트로 연결하지 않고 베팅을 권유하는 콘텐츠를 만들지 않습니다 (Position C)."

[약관 §2 — 서비스 범위]

본 서비스는 스포츠 분석 콘텐츠를 제공합니다. 본 서비스는:

  • 베팅 사이트로의 직접 연결을 제공하지 않습니다
  • 배당률을 한국식 배당으로 변환하지 않습니다
  • 픽 (선택) 추천 형식의 콘텐츠를 제공하지 않습니다

[콘텐츠 페이지 상단 면책 (모든 페르소나 글)]

"이 글은 분석 콘텐츠입니다. 베팅 결정의 근거가 아니며, 베팅 결과에 대해 책임지지 않습니다."

[Footer]

"분석 콘텐츠 미디어 | Position C | About | Terms | Privacy"
19-4 배포 직후 점검 — 30분 체크리스트 🔗

배포 완료 후 30분 안에 점검할 항목입니다.

점검방법통과 기준
공개 URL 접근브라우저 시크릿 탭 (캐시 X)200 OK + 페이지 정상
환경변수 누락/api/healthcheck 엔드포인트 호출모든 외부 서비스 OK
HTTPS 적용URL https:// 시작Vercel·Expo 자동
Lighthouse 점수Chrome DevToolsPerformance ≥ 80
모바일 반응형폰에서 직접 접근깨짐 없음
About·약관·개인정보 링크전부 클릭모두 200 OK
E1 외부 노출 4자리 ★About + 약관 + 개인정보 + Footer 일관모두 같은 메시지
Sentry 연결DSN 설정첫 에러 자동 수집
GA / Plausible측정 ID 설정첫 방문 기록
💡 ★ E1 외부 노출이 1탄 v2 추가 항목

배포 직후 가장 중요한 점검 — 본문 결정과 외부 노출 일관성. 이 항목이 빠지면 6개월 후 변호 논리 성립에 문제가 생깁니다.

19-5 줍줍 배포 사례 — 앱스토어 심사 통과 🔗
📘 Day 70 (10주차) BUILD.md 트레이스

결정: 앱스토어 심사 제출 — IDFA 미수집 + 최소 정보 수집 명시

근거: SPEC §9 R3 (애플 심사 거절) 완화책. PIPA 준수 + IDFA 미수집 명시 → 심사 통과 가속.

외부 노출 4자리 점검:

  • About: "정보 격차를 줄임, 신청 결과 보장 X" 노출
  • 약관: "신청 대행 서비스 아님" 명시
  • 개인정보: "IDFA 수집 안 함" 명시
  • Footer: 일관 메시지

Apple Privacy Manifest 작성:

  • PrivacyTrackingEnabled: false
  • PrivacyAccessedAPITypes: 명시

결과: 1차 심사 → 추가 정보 요청 (1건) → 1일 내 답변 → 통과

줍줍 배포 후 첫 사용자 확보

📘 Day 71 — 배포 직후

목표: 1순위 커뮤니티 게시 ("아프니까 사장이다")

전략:

  1. 직접 줍줍 10개 + 1인칭 후기 10개 시딩 (이미 1주차 완료)
  2. 커뮤니티 게시: "[줍줍] 정부 지원금 정보 + 후기 공유 앱 (베타)"
  3. 명시: "베타 단계 무료, 6개월 후 프리미엄 도입 (월 2,900원)"
    ← E2 1인 페이스 + 정직성 (사용자가 미래 비용 인지)

결과 (Day 75): 가입 50명 + 후기 작성 8명 + 줍줍 클릭 약 200건

19-6 TSV 배포 사례 — Cloudflare WAF + Position C About 🔗
📘 Day 1 (Phase 1.0 시작) BUILD.md 트레이스

결정: 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~7 BUILD.md 트레이스 요약

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 · 운영 · 보안 (정정렬)을 다룹니다.

📘
1탄 도우미
질문하기 OK
안녕하세요! 배포에 대해 무엇이든 물어보세요. 본문에서 찾아 답변해드릴게요. 👇