FLOAT AI API · VIBE CODING SERIES 01 · 무료 공개

바이브코딩으로 뚝딱 만드는
나만의 사주앱

코드 한 줄 몰라도, 오늘 밤 배포까지. 복사해서 붙여넣으면 AI가 대신 만들어주는 실행형 전자책.
이 책의 프롬프트 상자는 전부 AI에게 붙여넣는 프롬프트입니다. 컨셉만 정하면 — 페르소나, 채팅 UI, 만세력 화면, 배포, 보안까지 — 어떤 개발 AI든 이 문서를 읽고 그대로 만들어냅니다. 필요한 것: FLOAT 콘솔 + GitHub + Vercel + Supabase 계정 4개. 회원가입·로그인까지 갖추면서 DB 테이블은 0개입니다.
FLOAT 콘솔 가입하고 시작하기 — ₩1,000 크레딧 즉시 지급

PART 0 — 이 책의 사용법 (꼭 읽고 시작)

이 책은 "읽는 책"이 아니라 "실행하는 책"입니다

이 책의 프롬프트 상자는 전부 AI에게 그대로 복사해서 붙여넣는 프롬프트입니다. 여러분이 할 일은 세 가지뿐입니다.

코드를 이해할 필요가 없습니다. 다만 순서는 지켜야 합니다. 각 장의 프롬프트는 앞 장의 결과물 위에서 동작하도록 설계되어 있습니다.

완성하면 갖게 되는 것

준비물 (전부 무료로 시작)

계정용도비용
FLOAT 콘솔 (console.float.do)사주 AI 엔진가입 시 ₩1,000 크레딧 — 이 책 실습 전체가 커버됨
GitHub코드 보관무료
Vercel배포(호스팅)무료 (개인 Hobby 플랜)
Supabase회원가입/로그인무료 (Free 플랜) — 테이블은 안 만듭니다

DB는 지금 안 합니다. 상담 기억은 FLOAT API가 서버에서 보관하고(RIDM Memory), 생년월일 프로필은 Supabase 회원 정보에 얹기 때문에, 테이블 설계 없이도 회원제 앱이 됩니다 — 폰을 바꿔도 로그인만 하면 AI가 지난 상담을 기억한 채 이어갑니다. 혹시 초보자가 아니라면, AI에게 "부록 E를 참고해서 DB를 붙여줘"라고 말해주면 됩니다.

PART 1 — 15분 준비운동

1-1. FLOAT 콘솔 가입과 API 키 발급

(잃어버리면? 폐기하고 다시 발급하면 됩니다. 키는 여러 개 만들 수 있어요)

⚠️ 이 키는 곧 "돈"입니다. 키를 아는 사람은 여러분의 크레딧으로 API를 쓸 수 있습니다. PART 7에서 이 키를 안전하게 다루는 법을 배우는데, 그 전까지 절대 아무 데도 붙여넣지 마세요. 카톡 나에게 보내기도 안 됩니다.

1-2. 도구 선택 — 셋 중 하나면 충분

이 책의 프롬프트는 어느 도구에서든 동작하도록 작성되어 있습니다. v0(Vercel)는 UI 시안 뽑을 때 보조로 좋습니다.

1-3. Supabase 프로젝트 만들기 (로그인 담당)

- Project URL (https://xxxx.supabase.co)

- anon public

1-4. GitHub와 Vercel 연결

PART 2 — 바이브코딩 기본기 (10분 이론)

2-1. 바이브코딩이 실패하는 세 가지 이유

2-2. 마법의 문장들 (불평 사전)

상황이렇게 말하세요
뭔가 깨졌는데 뭔지 모름"지금 브라우저에 이런 에러가 떠. [에러 복사] 원인을 먼저 설명하고 고쳐줘"
디자인이 촌스러움"지금 디자인이 AI가 만든 티가 나. [원하는 느낌] 무드로 과감하게 다시"
AI가 이상한 걸 만들기 시작"멈춰. 방금 요청은 취소하고, 변경한 파일 원래대로 되돌려줘"
잘 됐는지 모르겠음"방금 작업이 실제로 동작하는지 네가 직접 확인하고 증거를 보여줘"

PART 3 — 컨셉과 페르소나: 앱의 영혼 만들기

같은 사주 엔진이라도 "누가 말해주느냐"가 앱의 전부입니다. 30분을 여기에 쓰면 나머지가 전부 쉬워집니다.

3-1. 컨셉 한 줄 공식

[누구를 위한] + [어떤 캐릭터가] + [무엇을 해주는] 앱

3-2. 페르소나 설계 공식 (이게 systemPrompt가 됩니다)

페르소나 = ①정체 ②말투 규칙 ③해석 태도 ④경계선 ⑤형식

📋 AI에게 복사해서 붙여넣기
[페르소나 생성 프롬프트 — AI에 붙여넣기]
나는 사주 상담 앱을 만들고 있어. 아래 공식대로 내 앱의 페르소나(systemPrompt)를 작성해줘.
컨셉: {여기에 3-1에서 만든 한 줄}

공식:
① 정체: 이름, 나이대, 배경 스토리 한 줄
② 말투 규칙: 어미(반말/존댓말/하오체), 이모지 사용 여부와 빈도, 자주 쓰는 추임새 3개, 절대 안 쓰는 말
③ 해석 태도: 사주 데이터를 근거로 말하되 단정 대신 경향으로, 나쁜 운도 대처법과 함께
④ 경계선: 의료·법률·투자 확답 금지, 죽음·파멸 단정 금지, 불안 조장 금지
⑤ 형식: 답변 길이(3~5문단), 마지막은 한 줄 처방으로 마무리

결과는 "당신은 ~" 으로 시작하는 지시문 형태로, 500자 이내로.

3-3. 완성 페르소나 예시 3종 (그대로 써도 됨)

① 애기무당 "단이" — 반말, 직설, "흠~ 보인다 보여", 이모지 가끔 🔮. 나쁜 운은 "피하는 법"과 세트로만.

② 한학자 "청담 선생" — 하오체, 고사성어 하나씩, 이모지 금지. 대운·세운 중심의 큰 흐름 해석.

③ 다정한 언니 "루나" — 존댓말, 공감 먼저("많이 지쳤죠"), 사주는 위로의 근거로. 마지막에 오늘의 한 가지 실천.

3-4. 디자인 고르기 — 갤러리에서 훔쳐오는 법

"다크에 골드 포인트로 해줘" 같은 말만으로는 AI가 평범한 결과를 냅니다. 잘된 디자인을 보여주는 것이 백 마디 설명보다 낫습니다. 아래 갤러리들은 전부 무료로 구경할 수 있습니다 (2026-08 접속 확인).

갤러리뭘 고르기 좋은가이렇게 쓴다
mobbin.com실제 앱들의 화면·온보딩·채팅 UI마음에 드는 화면 스크린샷 캡처
land-book.com랜딩(첫 화면) 레이아웃히어로 구성 캡처
curated.design웹사이트 전체 무드·컬러무드 잡을 때 2~3장 캡처
screensdesign.com모바일 앱 화면 패턴온보딩·결과 화면 참고
21st.dev복붙 가능한 UI 컴포넌트컴포넌트의 프롬프트를 그대로 복사 → AI에 붙여넣기
tweakcn.com색 테마(팔레트)테마 골라서 CSS 변수 복사

3단계 워크플로:

📋 AI에게 복사해서 붙여넣기
[프롬프트 — 디자인 무드 이식]
첨부한 스크린샷들의 공통된 디자인 무드를 분석해줘:
배경색·포인트색, 폰트 느낌(세리프/고딕), 모서리 둥글기, 여백의 밀도, 버튼 스타일.
분석 결과를 "디자인 토큰"으로 정리한 다음, 그 토큰으로 내 앱의 전 화면을 다시 입혀줘.
조건:
- 콘텐츠·기능·API 연동은 절대 건드리지 말고 스타일만
- 스크린샷을 그대로 베끼지 말고 무드만 가져올 것 (남의 디자인 복제 금지)
- 작업 후 라이트/다크 모두에서 글자가 잘 보이는지 확인해줘

이렇게 뽑은 무드 요약 한 줄(예: "크림 배경 + 먹색 세리프, 여백 넉넉한 동양화 무드")이 PART 4 마스터 스펙의 {디자인 방향} 자리에 들어갑니다. 스크린샷을 마스터 스펙과 같이 첨부하면 정확도가 훨씬 올라갑니다.

PART 4 — 마스터 스펙 한 방: 앱 뼈대가 통째로 나온다

이 장이 이 책의 심장입니다. 아래 마스터 스펙을 AI에 통째로 붙여넣으면, 앱의 뼈대(프로젝트 구조·프록시·온보딩·기본 화면)가 한 번에 만들어집니다.

4-1. 붙여넣기 전에 채울 것 (딱 3칸)

4-2. 마스터 스펙 프롬프트 (부록 A에 전문 — 여기선 실행)

부록 A의 전문을 복사 → 3칸 채우기 → AI에 붙여넣기 → 기다리기 (5~15분)

4-3. 나왔는지 검증 (완료 기준 체크)

AI가 끝났다고 하면, 이렇게 물으세요:

📋 AI에게 복사해서 붙여넣기
다음을 순서대로 직접 실행해서 결과를 보여줘:
1. npm run build (에러 0이어야 함)
2. cat .gitignore | grep env (.env가 나와야 함)
3. grep -r "float_live_" app/ components/ (아무것도 안 나와야 함 — 키가 코드에 없다는 증거)

3번이 이 책의 보안 핵심입니다. 뭔가 나온다면 PART 7로 직행하세요.

PART 5 — 만세력 화면: 사주판이 뜨는 순간

만세력(사주 원국)은 이 앱의 "와" 포인트입니다. FLOAT 만세력 API는 4기둥만 주는 게 아니라 십성·지장간·운성·신살·대운·세운·신강약·격국까지 30개 가까운 항목을 계산해서 줍니다. 여러분은 뭘 보여줄지 고르기만 하면 됩니다.

5-1. 기본 사주판 (필수)

📋 AI에게 복사해서 붙여넣기
[프롬프트]
온보딩에서 받은 생년월일시로 만세력 화면을 만들어줘.

데이터: POST /api/float/manseryeok (이미 만든 프록시 경유)
요청 body: { year, month, day, hour, minute, timezone: "Asia/Seoul", gender }
응답은 { success, data } 형태이고 실제 데이터는 data 안에 있어.

화면 요구사항:
1. 4기둥 카드 (연주·월주·일주·시주): data.wonkuk.year/month/day/hour 사용
   - 큰 한자: stemHanja + branchHanja (세로로 위아래 배치)
   - 카드 배경색을 ohaeng(오행)별로: 목=청록계, 화=적색계, 토=황토계, 금=회백계, 수=남색계 — 우리 앱 무드에 맞는 톤으로
   - 카드 하단 작은 글씨: stemSipsin/branchSipsin (십성)
2. 오행 분포: data.ohaengBalance (목·화·토·금·수 개수) — 가로 바 5개
3. 일간 한 줄 요약: data.ilganSummary 를 카드로
4. 시간을 모르는 사용자는 hour를 안 보냈을 수 있어 — 시주 카드는 "시간 미상"으로 우아하게 처리
완료 기준: 1990-03-15 08:30 male 로 실제 호출해서 렌더된 스크린샷 확인

5-2. 심화 위젯 골라 담기 (원하는 것만)

위젯쓰는 데이터프롬프트 한 줄
현재 대운data.daeun[] (isCurrent)"대운 타임라인을 가로 스크롤로, isCurrent 구간은 강조"
세운(올해 운)data.seun[] (grade)"올해·내년 세운 카드를 길흉 grade 색상과 함께"
신강약 게이지data.strength"신강약을 게이지로, result와 score 표시"
격국 카드data.gyeokguk"격국 이름과 summary를 배지 스타일로"
신살 태그wonkuk.*.majorSinsal"기둥별 신살을 작은 태그 칩으로"
팁: 다 넣으면 복잡해집니다. 기본판 + 위젯 2개가 앱스토어 감성.

PART 6 — 상담 채팅: 기억하는 AI 만들기

6-1. 두 가지 상담 모드 (앱 성격에 따라 선택)

모드엔드포인트특징이런 앱에
사주 상담/reading/memory사주 데이터가 자동 주입된 전문 상담. 같은 회원(user.id)이면 지난 상담 기억상담 중심 앱 (추천)
원샷 리포트/reading/report버튼 1회 → 전 섹션 사주 리포트 (nova 모델, 사주 자동 주입). 단품 판매용리포트 상품 (부록 B-6)
자유 채팅/chat/completions페르소나 자유 대화 (OpenAI 호환). 사주 주입은 없음캐릭터 챗 앱

6-2. 사주 상담 채팅 [메인 레시피]

📋 AI에게 복사해서 붙여넣기
[프롬프트]
상담 채팅 화면을 만들어줘.

데이터: POST /api/float/reading/memory (프록시 경유)
요청 body: {
  year, month, day, hour, minute, gender,   // 온보딩 값
  question: 사용자가 입력한 질문,
  tone: "casual",                            // warm(기본)/casual/formal 중 페르소나에 맞게
  endUserId: 로그인한 회원의 user.id          // ★ 이게 기억의 열쇠 — 기기 바꿔도 유지
}
응답 data: { interpretation(답변 텍스트), memory: { used, notesInjected(주입된 기억 수), tags[] }, costPoints }

화면 요구사항:
1. 말풍선 UI — 사용자 오른쪽, AI 왼쪽(페르소나 이름·아바타 이모지 표시)
2. 전송 후 로딩: 페르소나에 맞는 문구 순환 ("사주판을 펼치는 중…", "별자리를 짚는 중…")
3. memory.used가 true면 AI 말풍선 위에 작은 배지: "지난 상담을 기억하고 있어요"
4. 대화 내역은 localStorage에 저장해서 새로고침에도 유지
5. 질문 입력 아래 추천 질문 칩 3개: "올해 연애운 어때?", "이직해도 될까?", "요즘 왜 이렇게 지치지?"
완료 기준: 같은 계정으로 두 번 질문했을 때 두 번째 답변이 첫 대화를 참조하는지 실제 확인 (로그아웃 후 재로그인해도 유지되어야 함)

6-3. 페르소나를 채팅에 입히기

/reading/memory는 사주 전문가 톤이 기본입니다. 페르소나 말투를 더 강하게 입히려면 question 앞에 지시를 얹는 게 아니라(그건 사용자 몫), 자유 채팅(/chat/completions)에 systemPrompt로 페르소나를 넣고, 사주 정보는 첫 메시지에 요약해 넣는 하이브리드도 가능합니다:

📋 AI에게 복사해서 붙여넣기
[프롬프트 — 캐릭터성 강한 앱용]
자유 채팅 모드를 추가해줘.
POST /api/float/chat/completions
body: {
  model: "ridm/gpt-5.6-terra",
  user: endUserId(= user.id),             // 같은 회원 = 기억 유지
  systemPrompt: `{페르소나 블록}`,
  messages: [...대화 배열]
}
주의: 이 엔드포인트만 응답이 {success,data} 래핑 없이 OpenAI 형식 그대로야.
답변은 choices[0].message.content 에 있어.

6-4. 흔한 함정

PART 7 — 보안: 절대 털리지 않는 구조 ★필독★

7-1. 실화 하나

2024년, 한 개발자가 사이드 프로젝트를 GitHub에 올리면서 .env 파일을 함께 커밋했습니다. 15분 안에 자동 크롤러가 키를 수확했고, 깨어나 보니 API 요금이 수십만 원 찍혀 있었습니다. GitHub은 매달 수천 개의 저장소에서 노출된 API 키를 자동 탐지합니다 — 그만큼 흔한 사고라는 뜻입니다.

여러분의 FLOAT 키도 똑같습니다. 키 = 여러분의 크레딧 = 돈. 다행히 이 책의 구조를 그대로 따랐다면 이미 안전합니다. 이 장은 "왜 안전한지"를 이해하고, 스스로 점검하는 장입니다.

7-2. 3중 방어선

방어선 1 — 키는 서버에만 존재한다

브라우저(클라이언트)에서 api.float.do를 직접 부르면 두 가지 문제가 생깁니다: ①키가 사용자 브라우저에 노출됨 ②CORS 차단(FLOAT이 의도적으로 막아둠). 그래서 모든 호출은 Vercel 서버리스 함수(프록시)를 거칩니다. 키는 process.env.FLOAT_API_KEY — 서버 메모리에만 존재합니다.

📋 AI에게 복사해서 붙여넣기
// app/api/float/[...path]/route.ts — 이 파일이 방어선 1의 실체
import { NextRequest, NextResponse } from 'next/server';

const hits = new Map<string, { n: number; t: number }>();

export async function POST(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) {
  // 방어선 3: 아주 단순한 IP 요청 제한 (분당 10회)
  const ip = req.headers.get('x-forwarded-for') ?? 'local';
  const now = Date.now();
  const h = hits.get(ip) ?? { n: 0, t: now };
  if (now - h.t > 60_000) { h.n = 0; h.t = now; }
  if (++h.n > 10) return NextResponse.json({ error: '잠시 후 다시 시도해주세요' }, { status: 429 });
  hits.set(ip, h);

  const { path } = await ctx.params;
  const body = await req.json();
  const upstream = await fetch(`https://api.float.do/api/v1/partners/v1/${path.join('/')}`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Partner-Api-Key': process.env.FLOAT_API_KEY!, // 서버에서만 존재
    },
    body: JSON.stringify(body),
  });
  return NextResponse.json(await upstream.json(), { status: upstream.status });
}

방어선 2 — 키는 Git에 존재하지 않는다

키 두 종류의 취급 차이 (헷갈리기 쉬움)

어디에NEXT_PUBLIC_ 허용?
FLOAT_API_KEY (float_live_...)서버 환경변수만❌ 절대 금지 — 이 키 = 내 크레딧
Supabase URL / anon 키클라이언트 포함 OK✅ 정상 — 공개 전제로 설계된 값

"NEXT_PUBLIC이 붙은 게 있는데 괜찮나요?" — Supabase 두 값은 괜찮습니다. FLOAT 키에만 붙어 있으면 사고입니다.

방어선 3 — 남용을 막는다

프록시는 로그인한 회원의 요청만 통과시킵니다(비로그인 401) — 배포된 주소를 누가 알아내도 회원가입 없이는 내 크레딧을 못 씁니다. 추가로 프록시의 분당 10회 제한. 공개 서비스로 키울 땐 Vercel의 Firewall/Rate Limiting 또는 Upstash로 업그레이드.

7-3. 셀프 보안 점검 (배포 전 3분)

📋 AI에게 복사해서 붙여넣기
[프롬프트]
보안 점검을 실행하고 각 항목의 실제 명령 결과를 보여줘:
1. git ls-files | grep -i env        → .env.example 만 나와야 함
2. grep -rn "float_live_" . --include="*.ts" --include="*.tsx" --include="*.js" | grep -v node_modules  → 0건이어야 함
3. git log --all -S "float_live_" --oneline  → 0건이어야 함 (과거 커밋에도 없어야!)
4. .env.local 이 .gitignore 에 걸리는지: git check-ignore .env.local  → 경로가 출력되면 정상

7-4. 그래도 유출됐다면 (1분 응급처치)

과거 커밋에 키가 박혔다면 저장소를 지우고 새로 만드는 게 히스토리 세탁보다 빠릅니다 (어차피 키는 폐기했으니 급하진 않음).

PART 8 — 배포: 세상에 내보내기 (15분)

8-1. GitHub에 올리기

📋 AI에게 복사해서 붙여넣기
[프롬프트]
이 프로젝트를 GitHub에 올릴 준비를 해줘:
1. 보안 점검(7-3) 4개 항목을 먼저 통과시켜
2. git init(이미 되어 있으면 생략) → 전체 커밋 → 내 저장소 https://github.com/{내계정}/my-saju-app 에 push
3. push 전에 .env.local 이 스테이징에 없는 걸 git status 로 증명해줘

8-2. Vercel 배포

8-3. 배포 후 스모크 테스트

8-4. 커스텀 도메인 (선택)

Vercel → Settings → Domains → 보유 도메인 연결. 없으면 vercel.app 도메인으로 충분히 장사 시작 가능.

PART 9 — 돈 이야기: 원가, 가격, 그리고 다음

9-1. 상담 1회 원가 (실측 기준)

사주 상담 1회 = 질문 ~300자 + 사주 컨텍스트(무료) + 답변 ~1,500자

9-2. 크레딧 관리

9-3. 수익화 아이디어 3단계

9-4. 결제 붙이기 — 토스페이먼츠 예시

앱에서 카드 결제를 받으려면 PG(결제대행사)에 가입하면 됩니다. 대표적으로 토스페이먼츠 — 개발 문서가 쉽고, 가입만 하면 테스트 키가 바로 발급되어 계약 전에도 전체 결제 흐름을 개발·시연할 수 있습니다. (실제 돈이 오가는 라이브 전환·정산에는 사업자등록이 필요합니다)

결제의 정석 흐름은 세 줄입니다:

📋 AI에게 복사해서 붙여넣기
[프롬프트 — 토스페이먼츠 결제 붙이기]
심층 상담 1회 이용권(₩1,900) 결제를 붙여줘. 토스페이먼츠 결제위젯 방식으로.

흐름:
1. /pay 페이지: 토스페이먼츠 결제위젯 SDK로 결제창. clientKey는
   NEXT_PUBLIC_TOSS_CLIENT_KEY 환경변수 사용 (공개 가능 키)
2. 성공 리다이렉트(/pay/success)에서 paymentKey, orderId, amount 수신
3. 서버 라우트 /api/pay/confirm 에서 토스 승인 API 호출:
   POST https://api.tosspayments.com/v1/payments/confirm
   인증: TOSS_SECRET_KEY (서버 환경변수 전용 — 절대 클라이언트 금지)
   ★ amount가 서버가 정한 1900과 다르면 승인하지 말고 400 반환
4. 승인 성공 시 그 회원에게 이용권 1회 부여
5. 승인 전까지는 절대 이용권을 주지 마 (리다이렉트만 믿으면 안 됨)

키는 토스페이먼츠 개발자센터의 테스트 키(test_ck_/test_sk_)로 먼저.
.env.example에 두 키 자리를 비워서 추가해줘.

시크릿 키 취급은 FLOAT 키와 동급입니다 — PART 7의 키 구분표에 한 줄이 늘어난 셈: TOSS_SECRET_KEY도 서버 전용, NEXT_PUBLIC_TOSS_CLIENT_KEY는 공개 OK. 그리고 결제 기록을 남기려면 드디어 첫 DB 테이블이 필요해집니다 — 부록 E로.

9-5. 대성공하면? — 이 구조가 버티는 범위와 증축 시점

0테이블 구조는 임시방편이 아니라 "첫 수만 명까지의 최단 경로"입니다:

그래도 앱이 진짜 커지면 Supabase에 DB를 붙여서 운영하는 단계로 넘어갑니다. 증축 신호 세 가지:

신호증축 내용어디에
"다른 기기에서도 대화 목록이 보였으면"첫 테이블 chat_logs부록 E-1
결제를 받기 시작함 (9-4)payments 테이블부록 E-2
트래픽 폭증으로 분당 제한이 헐거워짐인메모리 Map → Upstash RedisAI에게 "레이트리밋을 Upstash로 바꿔줘"

이미 Supabase 계정·프로젝트가 있으니 이사 없이 그 자리에서 증축입니다. FLOAT 크레딧 사용량이 확 늘면 콘솔에서 구독(보너스)으로 전환하고, 월 수백만 원 규모면 엔터프라이즈 문의로 볼륨 단가를 협의하세요.

부록 A — 마스터 스펙 프롬프트 전문 (복사 시작점)

📋 AI에게 복사해서 붙여넣기
당신은 시니어 풀스택 개발자입니다. 아래 스펙대로 Next.js 웹앱을 처음부터 끝까지 만들어주세요. 스펙에 없는 것을 임의로 추가하지 말고, 모호하면 스펙의 완료 기준을 우선하세요.

[앱 개요]
- 앱 이름: {앱이름}
- 컨셉: {한 줄 컨셉}
- 페르소나(캐릭터): {페르소나 블록 — PART 3 결과물}
- 디자인 방향: {디자인 방향} · 모바일 우선 · 한글 폰트 Pretendard (CDN)

[기술 스택 — 변경 금지]
- Next.js 14+ (App Router) + TypeScript + Tailwind CSS
- 인증: Supabase Auth (@supabase/supabase-js + @supabase/ssr) — 이메일+비밀번호 회원가입/로그인
- 배포 대상: Vercel. **DB 테이블 생성 금지** — 사용자 기억은 FLOAT API가 서버에서 호스팅하고, 프로필(생년월일시·성별·이름)은 Supabase user_metadata 에 저장 (supabase.auth.updateUser({ data: {...} }))
- endUserId = 로그인한 사용자의 Supabase user.id (별도 uuid 생성 금지)
- 상태: 서버 소스는 위 둘 뿐. localStorage 는 대화 내역 캐시 용도로만

[보안 규칙 — 절대 위반 금지]
1. FLOAT_API_KEY 는 서버 환경변수로만 사용. 클라이언트 코드·NEXT_PUBLIC_ 접두사 금지
2. 모든 FLOAT API 호출은 app/api/float/[...path]/route.ts 프록시를 경유 (아래 구현 참고)
3. .gitignore 에 .env* 포함 확인 + 키 값을 비운 .env.example 생성
4. 프록시에 IP당 분당 10회 요청 제한 (메모리 Map으로 충분)
5. 프록시는 요청마다 Supabase 세션을 검증하고, 비로그인 요청은 401 로 거부 (내 크레딧을 남이 못 쓰게)
6. 키 구분: NEXT_PUBLIC_SUPABASE_URL / NEXT_PUBLIC_SUPABASE_ANON_KEY 는 공개돼도 되는 값이라 NEXT_PUBLIC_ 이 정상. FLOAT_API_KEY 만 서버 전용

[FLOAT API 계약 — https://api.float.do]
인증 헤더: X-Partner-Api-Key (프록시에서만 부착)
응답 공통: **chat/completions 를 제외한 모든 엔드포인트**가 `{ success, data }` 래핑 — 실데이터는 항상 `data` 안. chat/completions 만 OpenAI 형식 raw (`choices[0].message.content`)

1) 만세력 POST /api/v1/partners/v1/manseryeok
   req { year, month, day, hour?, minute?, timezone: "Asia/Seoul", gender: "male"|"female" }
   **wonkuk 4기둥 공통 구조**: year·month·day·hour 네 기둥이 전부 같은 필드 세트를 가짐 — { stemHanja(천간 한자), branchHanja(지지 한자), ohaeng, stemSipsin, branchSipsin, jijanggan[](지장간), ungSung(운성), napEum(납음), majorSinsal[], minorSinsal[] }. **일주(day)도 십성 필드가 있으며** day.stemHanja+branchHanja 가 일간·일지. 시간 미상으로 hour 를 안 보내면 wonkuk.hour 는 null.

   실제 응답 예시 (요약 — 1999-07-21 10:00 여성):
   { "success": true, "data": {
  "wonkuk": { "day": { "stemHanja": "甲", "branchHanja": "戌", "ohaeng": "목",
    "stemSipsin": "비견", "branchSipsin": "편재", "jijanggan": ["辛","丁","戊"], ... },
    "year": {...}, "month": {...}, "hour": {...} },
  "ohaengBalance": { "목": 2, "화": 1, "토": 3, "금": 1, "수": 1 },
  "ilganSummary": "...", "strength": { "result": "중화", "score": ... },
  "gyeokguk": { "name": "...", "summary": "..." },
     "daeun": [ { "startAge": 4, "endAge": 13, "stemHanja": "...", "isCurrent": false }, ... ]
   } }

   res.data { wonkuk:{year,month,day,hour:{stemHanja,branchHanja,ohaeng,stemSipsin,branchSipsin,jijanggan[],ungSung,napEum,majorSinsal[],minorSinsal[]}}, ohaengBalance:{목,화,토,금,수}, ilganSummary, strength:{result,level,score}, gyeokguk:{name,summary}, daeun:[{startAge,endAge,stemHanja,branchHanja,ohaeng,isCurrent,stemSipsin,branchSipsin,sinsal[]}], seun:[{year,stemHanja,branchHanja,grade,isCurrent}], yongshin, branchRelations[], gongmangDetail, cheonganDetail }

2) AI 사주 리딩 POST /api/v1/partners/v1/reading
   req { year, month, day, hour?, minute?, gender, question, tone?: "warm"|"casual"|"formal" }
   res.data { interpretation, usage:{inputTokens,outputTokens}, costPoints }

3) 기억형 상담 POST /api/v1/partners/v1/reading/memory
   req = reading + endUserId (필수, 같은 값 = 기억 유지)
   res.data = reading + memory:{ used, summaryInjected, notesInjected, tags[] }

3.5) 사주 리포트 POST /api/v1/partners/v1/reading/report
   req { year, month, day, hour?, minute?, gender, tone?, persona?(최대2000자), sections?(최대12개) }
   res.data { report(섹션별 마크다운 전문), sections[], sajuSource, usage, costPoints, model }
   ※ 사주 계산·주입은 서버가 자동 수행 — 만세력을 따로 부를 필요 없음

4) 자유 채팅 POST /api/v1/partners/v1/chat/completions   ※ OpenAI 호환 raw 응답
   req { model: "ridm/gpt-5.6-terra", user: endUserId, systemPrompt, messages:[{role,content}] }
   res { choices:[{message:{content}}], usage, costWon }

[화면 명세]
1. 로그인/회원가입(/login): 이메일+비밀번호. 가입 직후 온보딩으로 이동. 로그아웃 버튼은 설정 아이콘 안에
2. 온보딩(/onboarding): 이름(선택)·생년월일·태어난 시간(모름 체크 가능)·성별 → supabase.auth.updateUser 로 user_metadata 저장. 이미 입력한 회원은 건너뛰고 만세력으로
3. 라우트 보호: 미들웨어에서 비로그인 사용자가 /saju·/chat 접근 시 /login 으로 리다이렉트
4. 만세력(/saju): 4기둥 카드(한자 크게·오행색), 오행 분포 바, 일간 요약, 현재 대운 하이라이트. 시간 미상은 시주 카드 "시간 미상" 처리
5. 상담(/chat): 말풍선 채팅. reading/memory 사용. 페르소나 아바타·이름 표시. memory.used=true면 "지난 상담을 기억하고 있어요" 배지. 로딩 문구는 페르소나 톤. 추천 질문 칩 3개
6. 공통: 하단 탭(만세력|상담), 다크모드 대응, 각 API 에러는 사람 말로("크레딧이 부족해요" 등)

[완료 기준 — 스스로 실행해 증명할 것]
- npm run build 에러 0
- git check-ignore .env.local → 경로 출력
- grep -r "float_live_" app/ components/ lib/ → 0건
- 1990-03-15 08:30 male 만세력 실호출 결과 요약 출력
- README.md 에 Vercel 배포 순서(환경변수 FLOAT_API_KEY 설정 포함) 기재

부록 B — 조합 레시피 모음 ("이런 앱엔 이 코드")

각 레시피는 마스터 스펙으로 만든 앱 위에 얹는 프롬프트입니다.

B-1. 오늘의 운세 위젯 (아침에 열게 만드는 훅)

📋 AI에게 복사해서 붙여넣기
홈 상단에 "오늘의 운세" 카드를 추가해줘.
- POST /api/float/reading 사용, question은 고정: "오늘 하루의 전반적인 흐름과 조심할 것, 활용하면 좋은 시간대를 짧게 알려줘"
- 답변은 3줄 요약으로, 하루 1회만 호출하고 localStorage에 날짜와 함께 캐시 (같은 날 재방문 시 캐시 사용 — 크레딧 절약)
- 카드에 오늘 날짜와 페르소나 아바타 표시

B-2. 궁합 보기 (바이럴 기능)

📋 AI에게 복사해서 붙여넣기
궁합 페이지(/compat)를 추가해줘.
- 상대방 생년월일(시간 선택) 입력 폼 → POST /api/float/compat
- req: { personABirthDate:"1990-03-15", personABirthTime:"08:30", personAGender:"male", personB... }
- res.data: { tti:{userTti, partnerTti, tier, relation}, costPoints }
- 결과: 두 사람 카드 나란히 + tier 배지 + relation 설명. "결과 이미지로 저장" 버튼(html2canvas 없이 공유 텍스트 복사로 단순하게)

B-3. 성격유형 미니 테스트 (가입 전 미끼)

📋 AI에게 복사해서 붙여넣기
성격유형 페이지(/type)를 추가해줘.
- POST /api/float/personality-type, req: { tier: "free", answers: [...] }
- 질문 UI는 한 화면에 하나씩, 진행 바 표시 (무료 티어 질문 24개 세트는 콘솔 문서 https://console.float.do/docs.html 의 성격유형 섹션 참조 — AI에게 "이 문서의 질문 세트를 가져와" 하면 됨)
- res.data: { personalityType, typeLabel, scores } → 유형 카드 + 축별 점수 바 4개

B-4. 페르소나 갈아끼우기 (같은 앱, 다른 장사)

페르소나 블록만 바꾸면 같은 코드가 완전히 다른 앱이 됩니다.

📋 AI에게 복사해서 붙여넣기
페르소나를 아래로 교체하고, 로딩 문구·추천 질문 칩·앱 이름·컬러 무드도 새 페르소나에 맞게 일괄 조정해줘:
{새 페르소나 블록}

B-5. 대운 타임라인 (인생 그래프)

📋 AI에게 복사해서 붙여넣기
만세력 화면에 "인생의 큰 흐름" 섹션을 추가해줘.
- data.daeun 배열로 가로 타임라인: 각 구간 startAge~endAge, 간지(stemHanja+branchHanja), 십성
- isCurrent 구간은 페르소나 포인트 컬러로 강조 + "지금 여기" 라벨
- 클릭하면 그 대운에 대해 상담 채팅으로 질문이 자동 입력되게: "OO~OO세 대운에 대해 알려줘"

B-6. 사주 리포트 — "990 사주" 방식 (리포트형 상품)

채팅형과 정반대 상품입니다. 질문을 주고받는 게 아니라, 버튼 하나 누르면 전체 풀이가 한 방에 좌르륵 나오는 방식 — 국내 사주 앱들이 검증한 히트 공식입니다. 이 책의 앱은 두 상품을 나눠 팔 수 있습니다:

대화용 (상담 채팅)리포트형 (사주 리포트)
방식주고받는 채팅, 기억 유지버튼 1회 → 전 섹션 리포트
모델ridm/gpt-5.6-terra · /reading/memoryridm/saju-report (리포트 특화)
과금 감각회당 소액, 반복 사용1회 단품 결제와 궁합 (9-4)

1회 원가는 실측 약 ₩150입니다 (8개 섹션 3,000자 리포트 기준 — 저자가 실제 호출로 검증). 섹션을 두 배로 길게 뽑아도 ₩350 안쪽. 990원 단품이면 원가의 6배가 남고, "프리미엄 사주 리포트 4,900원"이면 마진이 확 커집니다.

📋 AI에게 복사해서 붙여넣기
[프롬프트 — 사주 리포트 페이지]
/report 페이지를 추가해줘. "내 사주 사주 리포트 보기" 버튼 하나로 전체 리포트가 생성되는 방식.

1. 버튼을 누르면 리포트 전용 엔드포인트 1번만 호출 (프록시 경유):
   POST /api/float/reading/report
   body: { year, month, day, hour?, minute?, gender,
           tone: "casual",
           persona: "{우리 앱 페르소나 요약 — 말투 지시}" }
   사주 계산·주입은 서버가 알아서 하고, 응답 data.report에
   "## 섹션명" 제목의 마크다운 전문이 온다. data.sections가 목차.
2. 기본 8섹션(총평/타고난 기질/재물운/연애·인연/직업·적성/올해와 내년/
   10년 대운 흐름/마지막 한 줄 처방)이 자동 — 바꾸고 싶으면 body에
   sections: ["원하는", "섹션들"] (최대 12개)
3. UI: 생성 중에는 "풀이 중..." 로딩 연출, 완성되면 목차(섹션 앵커)가 있는
   긴 스크롤 리포트로. 인쇄/저장하기 좋게.
4. 생성된 리포트는 저장해서 다시 볼 수 있게 해줘 (돈 주고 본 리포트가
   사라지면 안 됨). 지금은 localStorage, DB를 붙였다면 부록 E 테이블로.
5. (선택) 9-4 결제를 붙였다면: 결제 승인 후에만 생성 시작

완료 기준: 버튼 1회로 8개 섹션이 전부 채워진 리포트가 나오고, 새로고침해도 다시 보이는지 확인

부록 C — FLOAT API 요약표

엔드포인트용도과금(콘솔 요금표 기준)
POST /manseryeok만세력 계산 (30여 항목)계산형 — 저렴
POST /readingAI 사주 해석입 ₩3,413 / 출 ₩27,300 (1M토큰)
POST /reading/memory기억형 상담 (endUserId)×1.3
POST /reading/report사주 리포트 (섹션형, nova 모델)1회 실측 ~150P
POST /chat/completions자유 채팅 (OpenAI 호환)모델별 — 요금표 참조
POST /compat궁합요금표 참조
POST /personality-type성격유형요금표 참조

부록 D — 트러블슈팅 10선

부록 E — 대성공 대비: Supabase에 DB 붙이기

앱이 커져서(9-5의 신호) DB가 필요해지면, 이미 쓰고 있는 Supabase에 테이블을 만들면 됩니다. 단 하나의 철칙: 테이블을 만드는 순간 RLS(Row Level Security)를 반드시 켠다. anon 키가 "공개돼도 되는 키"인 것은 RLS가 남의 데이터를 막아준다는 전제입니다. RLS 없는 테이블 = 전 회원 데이터가 누구에게나 열람됩니다.

E-1. 첫 테이블: 대화 목록 동기화 (chat_logs)

📋 AI에게 복사해서 붙여넣기
[프롬프트 — 대화 목록을 기기 간 동기화]
지금 localStorage에만 저장되는 채팅 대화 목록을 Supabase 테이블로 옮겨서,
어느 기기에서 로그인해도 같은 대화 목록이 보이게 해줘.

1. Supabase SQL Editor에서 실행할 SQL을 만들어줘:
   - chat_logs 테이블: id, user_id(auth.users 참조), role('user'|'assistant'),
     content, created_at
   - RLS 활성화 필수 + 정책: 본인 것만 조회/삽입 (user_id = auth.uid())
2. 앱 수정: 메시지 주고받을 때마다 chat_logs에 insert,
   채팅 화면 진입 시 본인 대화를 시간순 select
3. 기존 localStorage 대화는 최초 1회 업로드 후 비우기
4. 완료 기준: 시크릿 창에서 같은 계정으로 로그인해도 대화 목록이 보일 것

E-2. 결제 기록 테이블 (payments)

📋 AI에게 복사해서 붙여넣기
[프롬프트 — 결제 기록 저장]
9-4에서 만든 토스 결제에 기록을 붙여줘.

1. payments 테이블 SQL: id, user_id(auth.users 참조), order_id, payment_key,
   amount, status, created_at
   - RLS 활성화: 본인 것만 조회 가능
   - insert는 클라이언트에서 금지 — 서버(승인 라우트)에서만
2. /api/pay/confirm 승인 성공 직후 서버에서 payments에 기록
3. 마이페이지에 내 결제 내역 목록 추가

서버에서만 쓰기를 하려면 Supabase의 service_role 키를 서버 환경변수로 씁니다 — 이 키는 RLS를 통째로 무시하는 만능 키라 FLOAT 키·토스 시크릿 키와 동급입니다. NEXT_PUBLIC_ 금지, 서버 전용.

E-3. 증축 후 점검 3종

E-4. 호스팅 이사 (Vercel 마이그레이션)

📋 AI에게 복사해서 붙여넣기
[프롬프트 — 호스팅 이전 준비]
이 Next.js 앱을 Vercel 밖(일반 서버/컨테이너)에서도 돌릴 수 있게 준비해줘.
1. next.config에 output: "standalone" 설정 + Dockerfile 작성
2. 환경변수 목록을 README에 정리 (FLOAT_API_KEY, Supabase 2종, 토스 2종)
3. 로컬에서 docker build & run으로 동작 확인하는 명령까지 알려줘
이사 자체는 새 서버에 컨테이너 띄우고 도메인 DNS만 바꾸면 끝이라는 것도
README에 적어줘 (FLOAT·Supabase 데이터는 그대로라 데이터 이사는 없음)
© 플로트 주식회사 (FLOAT Co., Ltd.) · console.float.do — 이 책의 사주 엔진, 여기서 무료로 시작하세요