이 책의 프롬프트 상자는 전부 AI에게 그대로 복사해서 붙여넣는 프롬프트입니다. 여러분이 할 일은 세 가지뿐입니다.
코드를 이해할 필요가 없습니다. 다만 순서는 지켜야 합니다. 각 장의 프롬프트는 앞 장의 결과물 위에서 동작하도록 설계되어 있습니다.
| 계정 | 용도 | 비용 |
|---|---|---|
| FLOAT 콘솔 (console.float.do) | 사주 AI 엔진 | 가입 시 ₩1,000 크레딧 — 이 책 실습 전체가 커버됨 |
| GitHub | 코드 보관 | 무료 |
| Vercel | 배포(호스팅) | 무료 (개인 Hobby 플랜) |
| Supabase | 회원가입/로그인 | 무료 (Free 플랜) — 테이블은 안 만듭니다 |
DB는 지금 안 합니다. 상담 기억은 FLOAT API가 서버에서 보관하고(RIDM Memory), 생년월일 프로필은 Supabase 회원 정보에 얹기 때문에, 테이블 설계 없이도 회원제 앱이 됩니다 — 폰을 바꿔도 로그인만 하면 AI가 지난 상담을 기억한 채 이어갑니다. 혹시 초보자가 아니라면, AI에게 "부록 E를 참고해서 DB를 붙여줘"라고 말해주면 됩니다.
float_live_로 시작하는 키가 한 번만 표시됩니다 → 복사해서 메모장에 임시 보관(잃어버리면? 폐기하고 다시 발급하면 됩니다. 키는 여러 개 만들 수 있어요)
⚠️ 이 키는 곧 "돈"입니다. 키를 아는 사람은 여러분의 크레딧으로 API를 쓸 수 있습니다. PART 7에서 이 키를 안전하게 다루는 법을 배우는데, 그 전까지 절대 아무 데도 붙여넣지 마세요. 카톡 나에게 보내기도 안 됩니다.
이 책의 프롬프트는 어느 도구에서든 동작하도록 작성되어 있습니다. v0(Vercel)는 UI 시안 뽑을 때 보조로 좋습니다.
my-saju-app, 리전 Seoul) - Project URL (https://xxxx.supabase.co)
- anon public 키
my-saju-app, Private 선택npm run dev 켜고 브라우저로 직접 확인. 이 책의 프롬프트에는 "완료 기준"이 박혀 있어서 AI가 스스로 검증하게 만듭니다.| 상황 | 이렇게 말하세요 |
|---|---|
| 뭔가 깨졌는데 뭔지 모름 | "지금 브라우저에 이런 에러가 떠. [에러 복사] 원인을 먼저 설명하고 고쳐줘" |
| 디자인이 촌스러움 | "지금 디자인이 AI가 만든 티가 나. [원하는 느낌] 무드로 과감하게 다시" |
| AI가 이상한 걸 만들기 시작 | "멈춰. 방금 요청은 취소하고, 변경한 파일 원래대로 되돌려줘" |
| 잘 됐는지 모르겠음 | "방금 작업이 실제로 동작하는지 네가 직접 확인하고 증거를 보여줘" |
같은 사주 엔진이라도 "누가 말해주느냐"가 앱의 전부입니다. 30분을 여기에 쓰면 나머지가 전부 쉬워집니다.
[누구를 위한] + [어떤 캐릭터가] + [무엇을 해주는] 앱
페르소나 = ①정체 ②말투 규칙 ③해석 태도 ④경계선 ⑤형식
[페르소나 생성 프롬프트 — AI에 붙여넣기]
나는 사주 상담 앱을 만들고 있어. 아래 공식대로 내 앱의 페르소나(systemPrompt)를 작성해줘.
컨셉: {여기에 3-1에서 만든 한 줄}
공식:
① 정체: 이름, 나이대, 배경 스토리 한 줄
② 말투 규칙: 어미(반말/존댓말/하오체), 이모지 사용 여부와 빈도, 자주 쓰는 추임새 3개, 절대 안 쓰는 말
③ 해석 태도: 사주 데이터를 근거로 말하되 단정 대신 경향으로, 나쁜 운도 대처법과 함께
④ 경계선: 의료·법률·투자 확답 금지, 죽음·파멸 단정 금지, 불안 조장 금지
⑤ 형식: 답변 길이(3~5문단), 마지막은 한 줄 처방으로 마무리
결과는 "당신은 ~" 으로 시작하는 지시문 형태로, 500자 이내로.
① 애기무당 "단이" — 반말, 직설, "흠~ 보인다 보여", 이모지 가끔 🔮. 나쁜 운은 "피하는 법"과 세트로만.
② 한학자 "청담 선생" — 하오체, 고사성어 하나씩, 이모지 금지. 대운·세운 중심의 큰 흐름 해석.
③ 다정한 언니 "루나" — 존댓말, 공감 먼저("많이 지쳤죠"), 사주는 위로의 근거로. 마지막에 오늘의 한 가지 실천.
"다크에 골드 포인트로 해줘" 같은 말만으로는 AI가 평범한 결과를 냅니다. 잘된 디자인을 보여주는 것이 백 마디 설명보다 낫습니다. 아래 갤러리들은 전부 무료로 구경할 수 있습니다 (2026-08 접속 확인).
| 갤러리 | 뭘 고르기 좋은가 | 이렇게 쓴다 |
|---|---|---|
| mobbin.com | 실제 앱들의 화면·온보딩·채팅 UI | 마음에 드는 화면 스크린샷 캡처 |
| land-book.com | 랜딩(첫 화면) 레이아웃 | 히어로 구성 캡처 |
| curated.design | 웹사이트 전체 무드·컬러 | 무드 잡을 때 2~3장 캡처 |
| screensdesign.com | 모바일 앱 화면 패턴 | 온보딩·결과 화면 참고 |
| 21st.dev | 복붙 가능한 UI 컴포넌트 | 컴포넌트의 프롬프트를 그대로 복사 → AI에 붙여넣기 |
| tweakcn.com | 색 테마(팔레트) | 테마 골라서 CSS 변수 복사 |
3단계 워크플로:
[프롬프트 — 디자인 무드 이식] 첨부한 스크린샷들의 공통된 디자인 무드를 분석해줘: 배경색·포인트색, 폰트 느낌(세리프/고딕), 모서리 둥글기, 여백의 밀도, 버튼 스타일. 분석 결과를 "디자인 토큰"으로 정리한 다음, 그 토큰으로 내 앱의 전 화면을 다시 입혀줘. 조건: - 콘텐츠·기능·API 연동은 절대 건드리지 말고 스타일만 - 스크린샷을 그대로 베끼지 말고 무드만 가져올 것 (남의 디자인 복제 금지) - 작업 후 라이트/다크 모두에서 글자가 잘 보이는지 확인해줘
이렇게 뽑은 무드 요약 한 줄(예: "크림 배경 + 먹색 세리프, 여백 넉넉한 동양화 무드")이 PART 4 마스터 스펙의 {디자인 방향} 자리에 들어갑니다. 스크린샷을 마스터 스펙과 같이 첨부하면 정확도가 훨씬 올라갑니다.
이 장이 이 책의 심장입니다. 아래 마스터 스펙을 AI에 통째로 붙여넣으면, 앱의 뼈대(프로젝트 구조·프록시·온보딩·기본 화면)가 한 번에 만들어집니다.
{앱이름} — 예: 단이사주{한 줄 컨셉} — PART 3에서 만든 것{페르소나 블록} — PART 3에서 AI가 만들어준 500자{디자인 방향} — 예: "다크 배경 + 골드 포인트, 신비롭지만 촌스럽지 않게" / "한지 질감 + 먹색, 여백 많은 동양화 무드" / "네온 보라 + 검정, 힙한 무속"부록 A의 전문을 복사 → 3칸 채우기 → AI에 붙여넣기 → 기다리기 (5~15분)
AI가 끝났다고 하면, 이렇게 물으세요:
다음을 순서대로 직접 실행해서 결과를 보여줘: 1. npm run build (에러 0이어야 함) 2. cat .gitignore | grep env (.env가 나와야 함) 3. grep -r "float_live_" app/ components/ (아무것도 안 나와야 함 — 키가 코드에 없다는 증거)
3번이 이 책의 보안 핵심입니다. 뭔가 나온다면 PART 7로 직행하세요.
만세력(사주 원국)은 이 앱의 "와" 포인트입니다. FLOAT 만세력 API는 4기둥만 주는 게 아니라 십성·지장간·운성·신살·대운·세운·신강약·격국까지 30개 가까운 항목을 계산해서 줍니다. 여러분은 뭘 보여줄지 고르기만 하면 됩니다.
[프롬프트]
온보딩에서 받은 생년월일시로 만세력 화면을 만들어줘.
데이터: 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 로 실제 호출해서 렌더된 스크린샷 확인
| 위젯 | 쓰는 데이터 | 프롬프트 한 줄 |
|---|---|---|
| 현재 대운 | data.daeun[] (isCurrent) | "대운 타임라인을 가로 스크롤로, isCurrent 구간은 강조" |
| 세운(올해 운) | data.seun[] (grade) | "올해·내년 세운 카드를 길흉 grade 색상과 함께" |
| 신강약 게이지 | data.strength | "신강약을 게이지로, result와 score 표시" |
| 격국 카드 | data.gyeokguk | "격국 이름과 summary를 배지 스타일로" |
| 신살 태그 | wonkuk.*.majorSinsal | "기둥별 신살을 작은 태그 칩으로" |
팁: 다 넣으면 복잡해집니다. 기본판 + 위젯 2개가 앱스토어 감성.
| 모드 | 엔드포인트 | 특징 | 이런 앱에 |
|---|---|---|---|
| 사주 상담 | /reading/memory | 사주 데이터가 자동 주입된 전문 상담. 같은 회원(user.id)이면 지난 상담 기억 | 상담 중심 앱 (추천) |
| 원샷 리포트 | /reading/report | 버튼 1회 → 전 섹션 사주 리포트 (nova 모델, 사주 자동 주입). 단품 판매용 | 리포트 상품 (부록 B-6) |
| 자유 채팅 | /chat/completions | 페르소나 자유 대화 (OpenAI 호환). 사주 주입은 없음 | 캐릭터 챗 앱 |
[프롬프트]
상담 채팅 화면을 만들어줘.
데이터: 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개: "올해 연애운 어때?", "이직해도 될까?", "요즘 왜 이렇게 지치지?"
완료 기준: 같은 계정으로 두 번 질문했을 때 두 번째 답변이 첫 대화를 참조하는지 실제 확인 (로그아웃 후 재로그인해도 유지되어야 함)
/reading/memory는 사주 전문가 톤이 기본입니다. 페르소나 말투를 더 강하게 입히려면 question 앞에 지시를 얹는 게 아니라(그건 사용자 몫), 자유 채팅(/chat/completions)에 systemPrompt로 페르소나를 넣고, 사주 정보는 첫 메시지에 요약해 넣는 하이브리드도 가능합니다:
[프롬프트 — 캐릭터성 강한 앱용]
자유 채팅 모드를 추가해줘.
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 에 있어.
2024년, 한 개발자가 사이드 프로젝트를 GitHub에 올리면서 .env 파일을 함께 커밋했습니다. 15분 안에 자동 크롤러가 키를 수확했고, 깨어나 보니 API 요금이 수십만 원 찍혀 있었습니다. GitHub은 매달 수천 개의 저장소에서 노출된 API 키를 자동 탐지합니다 — 그만큼 흔한 사고라는 뜻입니다.
여러분의 FLOAT 키도 똑같습니다. 키 = 여러분의 크레딧 = 돈. 다행히 이 책의 구조를 그대로 따랐다면 이미 안전합니다. 이 장은 "왜 안전한지"를 이해하고, 스스로 점검하는 장입니다.
방어선 1 — 키는 서버에만 존재한다
브라우저(클라이언트)에서 api.float.do를 직접 부르면 두 가지 문제가 생깁니다: ①키가 사용자 브라우저에 노출됨 ②CORS 차단(FLOAT이 의도적으로 막아둠). 그래서 모든 호출은 Vercel 서버리스 함수(프록시)를 거칩니다. 키는 process.env.FLOAT_API_KEY — 서버 메모리에만 존재합니다.
// 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에 존재하지 않는다
.env.local에만. .gitignore에 .env*가 있는지 확인.env.example만 커밋FLOAT_API_KEY 입력 (Git과 무관하게 주입됨)키 두 종류의 취급 차이 (헷갈리기 쉬움)
| 키 | 어디에 | NEXT_PUBLIC_ 허용? |
|---|---|---|
| FLOAT_API_KEY (float_live_...) | 서버 환경변수만 | ❌ 절대 금지 — 이 키 = 내 크레딧 |
| Supabase URL / anon 키 | 클라이언트 포함 OK | ✅ 정상 — 공개 전제로 설계된 값 |
"NEXT_PUBLIC이 붙은 게 있는데 괜찮나요?" — Supabase 두 값은 괜찮습니다. FLOAT 키에만 붙어 있으면 사고입니다.
방어선 3 — 남용을 막는다
프록시는 로그인한 회원의 요청만 통과시킵니다(비로그인 401) — 배포된 주소를 누가 알아내도 회원가입 없이는 내 크레딧을 못 씁니다. 추가로 프록시의 분당 10회 제한. 공개 서비스로 키울 땐 Vercel의 Firewall/Rate Limiting 또는 Upstash로 업그레이드.
[프롬프트] 보안 점검을 실행하고 각 항목의 실제 명령 결과를 보여줘: 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 → 경로가 출력되면 정상
과거 커밋에 키가 박혔다면 저장소를 지우고 새로 만드는 게 히스토리 세탁보다 빠릅니다 (어차피 키는 폐기했으니 급하진 않음).
[프롬프트]
이 프로젝트를 GitHub에 올릴 준비를 해줘:
1. 보안 점검(7-3) 4개 항목을 먼저 통과시켜
2. git init(이미 되어 있으면 생략) → 전체 커밋 → 내 저장소 https://github.com/{내계정}/my-saju-app 에 push
3. push 전에 .env.local 이 스테이징에 없는 걸 git status 로 증명해줘
my-saju-app ImportFLOAT_API_KEY = 발급받은 키, NEXT_PUBLIC_SUPABASE_URL·NEXT_PUBLIC_SUPABASE_ANON_KEY = PART 1-3에서 복사한 두 값https://my-saju-app.vercel.app 완성내도메인/api/float/...로만 나가는지 확인 (api.float.do가 직접 보이면 프록시를 안 탄 것)Vercel → Settings → Domains → 보유 도메인 연결. 없으면 vercel.app 도메인으로 충분히 장사 시작 가능.
사주 상담 1회 = 질문 ~300자 + 사주 컨텍스트(무료) + 답변 ~1,500자
앱에서 카드 결제를 받으려면 PG(결제대행사)에 가입하면 됩니다. 대표적으로 토스페이먼츠 — 개발 문서가 쉽고, 가입만 하면 테스트 키가 바로 발급되어 계약 전에도 전체 결제 흐름을 개발·시연할 수 있습니다. (실제 돈이 오가는 라이브 전환·정산에는 사업자등록이 필요합니다)
결제의 정석 흐름은 세 줄입니다:
clientKey는 공개되어도 되는 키[프롬프트 — 토스페이먼츠 결제 붙이기] 심층 상담 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로.
0테이블 구조는 임시방편이 아니라 "첫 수만 명까지의 최단 경로"입니다:
그래도 앱이 진짜 커지면 Supabase에 DB를 붙여서 운영하는 단계로 넘어갑니다. 증축 신호 세 가지:
| 신호 | 증축 내용 | 어디에 |
|---|---|---|
| "다른 기기에서도 대화 목록이 보였으면" | 첫 테이블 chat_logs | 부록 E-1 |
| 결제를 받기 시작함 (9-4) | payments 테이블 | 부록 E-2 |
| 트래픽 폭증으로 분당 제한이 헐거워짐 | 인메모리 Map → Upstash Redis | AI에게 "레이트리밋을 Upstash로 바꿔줘" |
이미 Supabase 계정·프로젝트가 있으니 이사 없이 그 자리에서 증축입니다. FLOAT 크레딧 사용량이 확 늘면 콘솔에서 구독(보너스)으로 전환하고, 월 수백만 원 규모면 엔터프라이즈 문의로 볼륨 단가를 협의하세요.
당신은 시니어 풀스택 개발자입니다. 아래 스펙대로 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 설정 포함) 기재
각 레시피는 마스터 스펙으로 만든 앱 위에 얹는 프롬프트입니다.
홈 상단에 "오늘의 운세" 카드를 추가해줘. - POST /api/float/reading 사용, question은 고정: "오늘 하루의 전반적인 흐름과 조심할 것, 활용하면 좋은 시간대를 짧게 알려줘" - 답변은 3줄 요약으로, 하루 1회만 호출하고 localStorage에 날짜와 함께 캐시 (같은 날 재방문 시 캐시 사용 — 크레딧 절약) - 카드에 오늘 날짜와 페르소나 아바타 표시
궁합 페이지(/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 없이 공유 텍스트 복사로 단순하게)
성격유형 페이지(/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개
페르소나 블록만 바꾸면 같은 코드가 완전히 다른 앱이 됩니다.
페르소나를 아래로 교체하고, 로딩 문구·추천 질문 칩·앱 이름·컬러 무드도 새 페르소나에 맞게 일괄 조정해줘:
{새 페르소나 블록}
만세력 화면에 "인생의 큰 흐름" 섹션을 추가해줘. - data.daeun 배열로 가로 타임라인: 각 구간 startAge~endAge, 간지(stemHanja+branchHanja), 십성 - isCurrent 구간은 페르소나 포인트 컬러로 강조 + "지금 여기" 라벨 - 클릭하면 그 대운에 대해 상담 채팅으로 질문이 자동 입력되게: "OO~OO세 대운에 대해 알려줘"
채팅형과 정반대 상품입니다. 질문을 주고받는 게 아니라, 버튼 하나 누르면 전체 풀이가 한 방에 좌르륵 나오는 방식 — 국내 사주 앱들이 검증한 히트 공식입니다. 이 책의 앱은 두 상품을 나눠 팔 수 있습니다:
| 대화용 (상담 채팅) | 리포트형 (사주 리포트) | |
|---|---|---|
| 방식 | 주고받는 채팅, 기억 유지 | 버튼 1회 → 전 섹션 리포트 |
| 모델 | ridm/gpt-5.6-terra · /reading/memory | ridm/saju-report (리포트 특화) |
| 과금 감각 | 회당 소액, 반복 사용 | 1회 단품 결제와 궁합 (9-4) |
1회 원가는 실측 약 ₩150입니다 (8개 섹션 3,000자 리포트 기준 — 저자가 실제 호출로 검증). 섹션을 두 배로 길게 뽑아도 ₩350 안쪽. 990원 단품이면 원가의 6배가 남고, "프리미엄 사주 리포트 4,900원"이면 마진이 확 커집니다.
[프롬프트 — 사주 리포트 페이지]
/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개 섹션이 전부 채워진 리포트가 나오고, 새로고침해도 다시 보이는지 확인
| 엔드포인트 | 용도 | 과금(콘솔 요금표 기준) |
|---|---|---|
| POST /manseryeok | 만세력 계산 (30여 항목) | 계산형 — 저렴 |
| POST /reading | AI 사주 해석 | 입 ₩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 | 성격유형 | 요금표 참조 |
앱이 커져서(9-5의 신호) DB가 필요해지면, 이미 쓰고 있는 Supabase에 테이블을 만들면 됩니다. 단 하나의 철칙: 테이블을 만드는 순간 RLS(Row Level Security)를 반드시 켠다. anon 키가 "공개돼도 되는 키"인 것은 RLS가 남의 데이터를 막아준다는 전제입니다. RLS 없는 테이블 = 전 회원 데이터가 누구에게나 열람됩니다.
[프롬프트 — 대화 목록을 기기 간 동기화]
지금 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. 완료 기준: 시크릿 창에서 같은 계정으로 로그인해도 대화 목록이 보일 것
[프롬프트 — 결제 기록 저장] 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_ 금지, 서버 전용.
[프롬프트 — 호스팅 이전 준비] 이 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 데이터는 그대로라 데이터 이사는 없음)