尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
호담 (HODAM) — 오늘을 담은 잠자리 그림책 아이의 하루를 입력하고 첫 4쪽을 읽은 뒤, 행동을 선택하면 결말 4쪽이 이어지는 개인 맞춤형 그림책 서비스입니다. /sample에서는 로그인 없이 읽기 흐름을 체험할 수 있습니다. • Node.js 22 최신 패치 (.nvmrc 기준, 22.13 이상) • 패키지 관리자는 npm이며 package-lock.json을 기준으로 npm ci를 사용합니다. 3. 필요한 환경변수 설정 (.env.example 참고) AI 개발 도구인 Cursor에서 MCP 서버를 사용하려면: 1. Supabase 대시보드에서 Personal Access Token 생성 2. .cursor/mcp.json 파일에 Supabase 설정 추가 1. Figma에서 Personal Access Token 생성: • Figma 설정 → Account → Personal Access Tokens • 새 토큰 생성 및 복사 2. .cursor/mcp.json 파일에 Figma 설정 추가 ⚠️ 보안 주의사항: .cursor/ 폴더는 .gitignore에 포함되어 있어 Git에 추적되지 않습니다.檢視完整 README ↗
아이의 하루 또는 단짝과 떠나는 상상 모험으로 만드는 개인 맞춤형 그림책 서비스입니다. 첫 4쪽을 읽고 행동을 선택하면 결말 4쪽이 이어집니다. 모험은 장소와 단짝만 골라도 시작할 수 있고, 완성한 책에서 같은 단짝과 다음 모험을 고를 수 있습니다. /sample에서는 로그인 없이 읽기 흐름을 체험할 수 있습니다.
완성한 그림책은 표지·삽화·한글 본문을 담은 PDF로 저장할 수 있습니다. 책장은 아이 이름·읽기 상태·완성 상태 필터, 최근 읽은 순·제목순 정렬, 단짝별 모아보기와 즐겨찾기를 지원하며, 읽던 위치와 즐겨찾기는 로그인한 계정에 저장해 다른 기기에서 이어볼 수 있습니다. 이전 브라우저 기록은 사용자가 ‘계정에 옮기기’를 선택하면 가져옵니다. 읽기 화면에서는 큰 글씨, 기기 음성 읽기, 잠자리 모드와 책을 읽은 뒤 함께 나눌 질문을 제공합니다. 완독 후 선택형 반응은 본인 책에만 저장·수정할 수 있습니다. 반응 기능의 DB 적용과 집계는 운영 문서, 읽기 기록 동기화는 동기화 문서를 참고하세요.
22 최신 패치 (.nvmrc 기준, 22.13 이상)package-lock.json을 기준으로 npm ci를 사용합니다.cp .env.example .env.local
npm run check:env
# 권장값까지 강제 점검하려면
npm run check:env:strict
.env.example 참고)AI 개발 도구인 Cursor에서 MCP 서버를 사용하려면:
.cursor/mcp.json 파일에 Supabase 설정 추가.cursor/mcp.json 파일에 Figma 설정 추가전체 설정 예시:
{
"mcpServers": {
"supabase": {
"command": "npx",
"args": ["-y", "@supabase/mcp-server-supabase@latest", "--access-token"],
"env": {
"SUPABASE_ACCESS_TOKEN": "your_personal_access_token"
}
},
"figma": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-figma"],
"env": {
"FIGMA_ACCESS_TOKEN": "your_figma_access_token"
}
}
}
}
⚠️ 보안 주의사항: .cursor/ 폴더는 .gitignore에 포함되어 있어 Git에 추적되지 않습니다.
.env.local 파일은 절대 Git에 커밋하지 않음.gitignore에 추가grep -r "sk_\|secret\|key\|token" . 명령어로 정기 검사이전 구현·검증 기록: 릴리스 분할 계획, 소셜 로그인 테스트 가이드, 2025년 로그인 결과, 초기 결제 설정 가이드. 당시 기준 자료이므로 현재 배포 설정과 검증 완료 범위는 위 운영 런북과 베타 QA 기록을 따릅니다.
보관된 운영 보고서: GitHub 실패 실행, 해결된 실패를 포함한 실행, Supabase 인덱스 조사, 결제 웹훅 조사. 현재 상태를 확인하려면 각 점검 명령을 다시 실행합니다.
npm ci
npm run dev
CI 워크플로우가 push/PR 시 자동으로 아래를 실행합니다.npm run check:audit:prodnpm run lintnpm run testnpm run buildnpm run check:threads:local (로컬 계약 검증)Security Check 워크플로우는 수동 실행/주간 실행으로 npm run check:supabase:security를 수행합니다.npm run check:supabase:security:strict를 실행합니다.HODAM_SUPABASE_SECURITY_IGNORE_LINTS로 strict 무시 목록을 설정할 수 있습니다.auth_leaked_password_protection,vulnerable_postgres_version,missing_service_role_key,missing_management_credentials입니다.Security Check의 GitHub failed-runs 진단 결과는 Step Summary와 github-failed-runs-main-report artifact로 저장됩니다.Security Check / E2E Auth / E2E Payments는 required secret이 없으면 기본적으로 skip(성공 종료)됩니다.HODAM_ENFORCE_SECRET_CHECKS=1을 설정하면 required secret 누락 시 즉시 fail 처리됩니다.HODAM_ENFORCE_SECRET_CHECKS를 1로 올리세요.E2E Payments 워크플로우는 수동 실행/주간 실행으로 아래를 수행합니다.npm run test:e2e:payments:local (인증 결제 + webhook 타임라인)npm run check:payments:webhook-coverage:strict (최근 완료 결제 webhook 누락 진단)npm run test:e2e:payments:unauth:local (비인증 401 경계)payments-webhook-coverage-report artifact로 저장됩니다.E2E Auth 워크플로우는 실행 로그를 e2e-auth-local-log artifact로 업로드하며, 실패 시 최근 로그 요약을 Step Summary에 남깁니다.E2E Payments에 필요한 주요 secret:NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEYHODAM_TEST_ACCESS_TOKEN (직접 토큰 주입) 또는 HODAM_TEST_USER_EMAIL + HODAM_TEST_USER_PASSWORD (워크플로우가 토큰 자동 발급)HODAM_TEST_DEFAULT_USER_EMAIL + HODAM_TEST_DEFAULT_USER_PASSWORD# 필수/권장 환경변수 점검
npm run check:env
npm run check:env:strict
# 로컬 배포 전 통합 점검(환경+보안 audit+lint+test+build)
npm run check:all
# 릴리즈 게이트(로컬)
npm run check:release:gate
# Supabase 수동 업그레이드 직후 후속 검증(통합)
npm run check:post-upgrade
# runtime origin 지정 예시
npm run check:post-upgrade -- --runtime-origin=https://your-domain.com
# 성능 advisor 단계 생략(긴급 점검/권한 제한 환경)
npm run check:post-upgrade -- --runtime-origin=https://your-domain.com --skip-supabase-performance
# Postgres patch upgrade 완료 후 strict 검증 모드
npm run check:post-upgrade -- --runtime-origin=https://your-domain.com --post-db-upgrade
# 의존성 취약점 점검
npm run check:audit:prod
npm run check:audit:all
# 결제 활성화에는 SUPABASE_SERVICE_ROLE_KEY, TOSS_PAYMENTS_SECRET_KEY,
# NEXT_PUBLIC_TOSS_PAYMENTS_CLIENT_KEY가 모두 필요
# OAuth 설정/프로바이더 진단
npm run check:oauth
# runtime origin이 현재 접속 주소와 다르면 명시
npm run check:oauth -- --runtime-origin=http://localhost:3000
# check-oauth는 provider 활성화 여부(google/kakao)도 함께 검사
# 로그인 실패 메시지의 [시도 ID]로 callback 메트릭 조회
npm run check:auth:attempt -- --attempt-id=YOUR_ATTEMPT_ID
# runtime origin이 다른 환경이면 명시
npm run check:auth:attempt -- --attempt-id=YOUR_ATTEMPT_ID --runtime-origin=https://your-domain.com
# 로컬 인증 E2E (sign-in CTA + recovery route + threads/list/detail 401 경계 + OAuth provider authorize)
npm run test:e2e:auth:local
# 인증 토큰 필수 모드(토큰 해석 실패 시 즉시 실패)
npm run test:e2e:auth:local:auth
# callback 오류 경로(예: invalid_grant, invalid_request) 렌더링 점검 포함
# 서비스 role + 테스트 계정이 있으면 e2e 시작 전에 테스트 사용자 자동 보정
HODAM_E2E_ENSURE_TEST_USER=1 HODAM_TEST_USER_EMAIL=... HODAM_TEST_USER_PASSWORD=... SUPABASE_SERVICE_ROLE_KEY=... npm run test:e2e:auth:local
# 서비스 role이 있고 고정 테스트 계정을 별도 지정하지 않으면 기본 테스트 유저 자동 생성/사용
HODAM_TEST_AUTO_USER=1 SUPABASE_SERVICE_ROLE_KEY=... npm run test:e2e:auth:local
# auto ensure 비활성화(명시된 자격증명만 사용)
HODAM_E2E_AUTO_ENSURE_TEST_USER=0 npm run test:e2e:auth:local
# 포트/프로바이더 커스터마이즈
HODAM_E2E_APP_PORT=3004 HODAM_E2E_OAUTH_PROVIDERS=google,kakao npm run test:e2e:auth:local
# Supabase 보안 점검 (DB RPC + Auth provider + Management advisor)
npm run check:supabase:security
# Supabase 성능 advisor 점검 (unused_index 리포트)
npm run check:supabase:performance
# strict 모드: unresolved unused index가 있으면 실패
npm run check:supabase:performance:strict
# unused index 자동 분류 리포트 생성 (로컬: reports/local/supabase-unused-indexes-report.md)
npm run check:supabase:performance:report
# CI artifact 경로로 생성
npm run check:supabase:performance:report:ci
# service role key가 없어도 anon 민감 RPC 차단 여부(권한 경계)는 검사됨
# 엄격 모드: WARN(예: OTP 만료, leaked password protection 비활성화)도 실패 처리
npm run check:supabase:security:strict
# 엄격 모드 + 무시 목록(예: Free 플랜 HIBP 제한)
HODAM_SUPABASE_SECURITY_IGNORE_LINTS=auth_leaked_password_protection,missing_service_role_key npm run check:supabase:security:strict
# 엄격 모드 + 운영 baseline(플랜/DB 패치 수동 이슈 임시 무시)
npm run check:supabase:security:strict:baseline
# 엄격 모드 + Postgres 패치 완료 후 게이트(취약 DB 버전은 더 이상 무시하지 않음)
npm run check:supabase:security:strict:post-upgrade
# Free 플랜 편의 스크립트
npm run check:supabase:security:strict:free
# 참고: 대표 issue name
# - auth_leaked_password_protection
# - vulnerable_postgres_version
# - missing_service_role_key
# CLI 옵션으로도 무시 목록 전달 가능
npm run check:supabase:security:strict -- --ignore-lints=auth_leaked_password_protection
# 성능 advisor 무시 목록 전달 예시(인덱스명 또는 cache key)
npm run check:supabase:performance -- --ignore-indexes=idx_messages_created_at
npm run check:supabase:performance -- --ignore-cache-keys=unused_index_public_messages_idx_messages_created_at
# Management advisor까지 보려면 SUPABASE_ACCESS_TOKEN 필요
# SUPABASE_PROJECT_REF는 없으면 NEXT_PUBLIC_SUPABASE_URL에서 자동 추론
# KPI 조회 (activity/retention 지표)
# 인증 토큰이 필요하며, 기본은 HODAM_TEST_ACCESS_TOKEN 사용
# 미설정 시 scripts/get-test-access-token.mjs 로 자동 해석 시도
npm run check:kpi
# 조회 개수 조정 (기본 14, 최대 90)
npm run check:kpi -- --limit=30
# auth callback 진단 수집이 활성화된 경우, kpi_daily에 아래 필드가 추가 집계됩니다.
# - auth_callback_success
# - auth_callback_error
# 위 필드 존재를 강제 검증하려면
npm run check:kpi:auth:strict
# provider 분해 집계(google/kakao) 필드 존재까지 강제하려면
npm run check:kpi:auth:provider:strict
# Supabase Auth 보안 하드닝 (OTP 만료/유출 비밀번호 보호)
# 기본: dry-run (변경 없음)
npm run supabase:auth:harden
# 실제 반영
npm run supabase:auth:harden:apply
# 참고: HIBP(leaked password protection)는 Supabase Pro 이상 플랜에서만 활성화 가능.
# 현재 플랜에서 미지원이면 OTP 만료만 부분 적용되고 HIBP는 경고 후 스킵됨.
# 타겟 커스터마이즈 예시
HODAM_AUTH_MAILER_OTP_EXP=3600 HODAM_AUTH_PASSWORD_HIBP_ENABLED=true npm run supabase:auth:harden:apply
# API v1 스모크 테스트 (테스트 access token 필요)
HODAM_TEST_ACCESS_TOKEN=... npm run test:smoke:v1
HODAM_TEST_ACCESS_TOKEN=... npm run test:smoke:v1 -- --story --translate
# 결제 경로 스모크 (prepare/history)
HODAM_TEST_ACCESS_TOKEN=... npm run test:smoke:v1 -- --payments
# 결제 confirm까지 포함 (로컬 mock Toss 권장)
HODAM_TEST_ACCESS_TOKEN=... HODAM_TEST_PAYMENT_CONFIRM=1 HODAM_TEST_PAYMENT_KEY=mock_key npm run test:smoke:v1 -- --payments --payments-confirm
# --payments-confirm는 동일 결제 2회 confirm idempotency(alreadyProcessed=true)까지 검증
# 서버 실행 시 TOSS_PAYMENTS_API_BASE_URL을 mock endpoint로 지정하면 안전하게 confirm 검증 가능
# 로컬 Toss mock 서버
npm run mock:toss
# 다른 포트 예시
npm run mock:toss -- --port=4011
# mock Toss + next dev + 결제 smoke를 한 번에 실행
HODAM_TEST_ACCESS_TOKEN=... npm run test:e2e:payments:local
# 인증 토큰 필수 모드(토큰 해석 실패 시 즉시 실패)
npm run test:e2e:payments:local:auth
# 또는 테스트 계정으로 토큰 자동 발급
HODAM_TEST_USER_EMAIL=... HODAM_TEST_USER_PASSWORD=... npm run test:e2e:payments:local
# 또는 로컬 fallback 계정 env 사용
HODAM_TEST_DEFAULT_USER_EMAIL=... HODAM_TEST_DEFAULT_USER_PASSWORD=... npm run test:e2e:payments:local
# 서비스 role + 테스트 계정이 있으면 결제 e2e 시작 전에 테스트 사용자 자동 보정
HODAM_E2E_ENSURE_TEST_USER=1 HODAM_TEST_USER_EMAIL=... HODAM_TEST_USER_PASSWORD=... SUPABASE_SERVICE_ROLE_KEY=... npm run test:e2e:payments:local
# 서비스 role이 있고 고정 테스트 계정을 별도 지정하지 않으면 기본 테스트 유저 자동 생성/사용
HODAM_TEST_AUTO_USER=1 SUPABASE_SERVICE_ROLE_KEY=... npm run test:e2e:payments:local
# auto ensure 비활성화(명시된 자격증명만 사용)
HODAM_E2E_AUTO_ENSURE_TEST_USER=0 npm run test:e2e:payments:local
# 포트 커스터마이즈
HODAM_TEST_ACCESS_TOKEN=... HODAM_E2E_APP_PORT=3002 HODAM_E2E_TOSS_PORT=4010 npm run test:e2e:payments:local
# webhook 정산/타임라인 이벤트까지 강제 검증
HODAM_TEST_ACCESS_TOKEN=... HODAM_TEST_PAYMENT_WEBHOOK=1 npm run test:e2e:payments:local
# webhook e2e + strict coverage를 한 번에 실행(필수 env 자동 검증/테스트유저 자동 보정 포함)
npm run test:e2e:payments:webhook:local
# HODAM_TEST_PAYMENT_WEBHOOK=1 모드에는 SUPABASE_SERVICE_ROLE_KEY가 필요
# 없으면 즉시 실패하며, 로컬에서만 스킵하려면 HODAM_E2E_ALLOW_WEBHOOK_SKIP=1 사용 가능
# 로컬 optional 모드(서비스 롤 키가 없으면 strict coverage를 경고 후 건너뜀)
npm run test:e2e:payments:webhook:local:optional
# 결제 API 비인증 접근 차단(401) 검증
npm run test:e2e:payments:unauth:local
# 최근 완료 결제의 webhook transmission 누락 진단
npm run check:payments:webhook-coverage
# strict: 누락이 있으면 실패
npm run check:payments:webhook-coverage:strict -- --lookback-minutes=180 --max-orders=10
# 리포트 파일 생성(마크다운)
npm run check:payments:webhook-coverage -- --report-file=reports/webhook-coverage.md
# 로컬 권장 경로 예시
npm run check:payments:webhook-coverage -- --report-file=reports/local/webhook-coverage.md
# access token 미지정 시 get-test-access-token 스크립트로 자동 해석 시도
# get-test-access-token은 기본 계정 하드코딩 없이 env 기반으로만 동작
# 결제 타임라인 API: orderId 또는 paymentFlowId 둘 중 하나로 조회 가능
# (예) GET /api/v1/payments/timeline?orderId=HODAM_...
# (예) GET /api/v1/payments/timeline?paymentFlowId=order:HODAM_...
src/lib/server/logger.ts)는 SENTRY_DSN이 설정된 경우 Sentry로 예외를 전송합니다.requestId가 자동 태그(hodam.request_id)로 전송됩니다.SENTRY_DSNSENTRY_ENVIRONMENT (예: production, staging)SENTRY_TRACES_SAMPLE_RATE (0~1, 기본 0.1)src/lib/client/api/*를 사용합니다.src/app/api/*.ts는 하위 호환을 위한 얇은 re-export 래퍼입니다.src/lib/client/api/* 경로를 직접 import 하세요.POST /api/v1/story/start 성공 응답: threadId, turn, beadCount, includeEnglish, includeImage, notice, imageUrl, messages[], selections[]POST /api/v1/story/continue 성공 응답: threadId, turn, beadCount, notice, messages[], selections[]POST /api/v1/story/translate 성공 응답: threadId, beadCount, messages[]story/start 주요 에러 코드:AUTH_UNAUTHORIZED, REQUEST_JSON_INVALID, STORY_START_RATE_LIMITED, STORY_START_KEYWORDS_*, STORY_START_BLOCKED_TOPIC, STORY_START_BLOCKED_OUTPUT, AI_SERVICE_NOT_CONFIGURED, AI_DAILY_BUDGET_EXCEEDED, BEADS_INSUFFICIENT, STORY_START_FAILEDstory/continue 주요 에러 코드:AUTH_UNAUTHORIZED, REQUEST_JSON_INVALID, STORY_CONTINUE_RATE_LIMITED, STORY_CONTINUE_THREAD_ID_INVALID, STORY_CONTINUE_SELECTION_*, STORY_CONTINUE_BLOCKED_TOPIC, STORY_CONTINUE_BLOCKED_OUTPUT, AI_SERVICE_NOT_CONFIGURED, AI_DAILY_BUDGET_EXCEEDED, BEADS_INSUFFICIENT, THREAD_NOT_FOUND, STORY_CONTINUE_FAILEDstory/translate 주요 에러 코드:AUTH_UNAUTHORIZED, REQUEST_JSON_INVALID, STORY_TRANSLATE_RATE_LIMITED, STORY_TRANSLATE_THREAD_ID_INVALID, STORY_TRANSLATE_BLOCKED_OUTPUT, AI_SERVICE_NOT_CONFIGURED, AI_DAILY_BUDGET_EXCEEDED, BEADS_INSUFFICIENT, THREAD_NOT_FOUND, STORY_TRANSLATE_FAILED카카오/구글로 시작하기 클릭 후 콜백 페이지에서 로그인 처리 중... 정체npm run check:oauth -- --runtime-origin=http://localhost:30003001이면 --runtime-origin=http://localhost:3001로 확인NEXT_PUBLIC_SITE_URL과 실제 접속 origin 불일치invalid_grant, code verifier 계열)3000)하거나, runtime origin 기준으로 재점검GET /api/v1/auth/providers 응답에서 provider별 enabled/reason 확인
disabled_in_supabase_auth_settings: Supabase Auth에서 provider 비활성missing_in_supabase_auth_settings: provider 구성 누락/api/v1/threads 장애 진단GET /api/v1/threads 응답 헤더 확인:
x-hodam-threads-source: rpc | fallback | nonex-hodam-threads-degraded: 1 이면 일부 조회 경로가 실패했음을 의미x-hodam-threads-degraded-reasons: rpc_error, fallback_error, keywords_error, unexpected_exception 등source=rpc: 정상 RPC 경로source=fallback: RPC 실패 후 테이블 직접 조회로 복구source=none: RPC/복구 경로 모두 실패하여 빈 목록 응답GET /api/v1/threads/:id)도 동일한 diagnostics 헤더를 사용:
x-hodam-threads-sourcex-hodam-threads-degradedx-hodam-threads-degraded-reasonsnpm run check:threads:local (서버가 떠있는 상태에서 401/invalid/authorized 경로를 순차 점검)npm run check:threads:local -- --require-authorized (authorized 경로 토큰이 없으면 실패 처리)npm run test:e2e:auth:localnpm run check:supabase:securityHODAM_TEST_ACCESS_TOKEN=... npm run test:e2e:auth:local (유효 토큰 경로의 실제 /api/v1/threads 상태코드/헤더까지 점검)HODAM_TEST_USER_EMAIL=... HODAM_TEST_USER_PASSWORD=... npm run test:e2e:auth:local (토큰 자동 발급 후 동일 점검)이 프로젝트는 MIT 라이선스 하에 있습니다.
Node.js 22 최신 패치와 npm을 사용합니다. .nvmrc로 버전을 맞춘 뒤 npm ci로 설치합니다. CI의 lint는 경고도 실패로 처리하며, npm test는 기존 src·scripts 테스트와 tests의 그림책 QA 테스트를 함께 실행합니다.
빌드에는 개발 의존성인 sass와 next-sitemap이 필요합니다. CI·Vercel의 빌드 단계에서 개발 의존성을 제외하지 않으며, NODE_ENV=production을 미리 설정한 설치 환경에서는 npm ci --include=dev를 사용합니다.
npm run lint
npm run typecheck
npm test
npm run build
npm run start
로컬 PostgreSQL의 initdb, pg_ctl, psql이 있다면 npm run test:db로 권한 마이그레이션을 격리된 임시 DB에서 검증할 수 있습니다. 운영 Supabase에는 연결하지 않습니다.
src/app/api/story-actions.ts의 인증된 서버 액션을 사용합니다. 이전 동화와 /api/v1/story/* API도 유지합니다.sessionStorage에 보관합니다. 마지막 수정 후 2시간 이내 같은 탭에서 복원하며, 로그인 CTA에서만 방문자 초안을 일회용으로 전달합니다. 미확인 생성 요청 복구가 초안보다 우선하고, 초안 비우기는 생성 요청·책장 기록을 변경하지 않습니다.그림책 무료로 읽어보기는 /sample로 연결됩니다. 미리 작성한 8쪽 이야기와 선택지별 5쪽 삽화를 포함한 WebP 10개를 public/sample/little-courage/에서 제공하며, 열람할 때 AI API를 호출하지 않습니다. 삽화 제작 기록에 장면별 프롬프트와 수정 내역을 보관합니다./bedtime-stories와 /bedtime-stories/[slug]는 로그인 없이 읽는 창작 동화 3편을, /ai-storybook은 제작 방법과 이용 조건을 제공합니다. 공개 원고는 src/content/public-stories.ts, 대표 삽화는 public/stories/*.webp에 보관하며 열람할 때 AI API를 호출하지 않습니다./api/v1 클라이언트와 API 계약을 사용합니다. /api/routes/payment/confirm도 같은 v1 승인 핸들러로 연결됩니다./api/v1/payments/webhook입니다..env.example에 필요한 항목을 정리했습니다. OpenAI 키는 서버 환경변수 OPENAI_API_KEY로만 설정합니다. 기존 OPEN_AI_API_KEY도 호환되지만 공개 빌드 설정에 넣지 않습니다. 초안 모델은 OPENAI_STORY_MODEL, 독립 검수·부분 교정 모델은 OPENAI_STORY_REVIEW_MODEL로 분리합니다. 두 기본값은 검증한 gpt-5.4-2026-03-05 snapshot이며 서로 별도 요청으로 실행합니다. 검수에 실패한 원고는 저장하지 않고, 지적된 쪽을 한 번 교정한 뒤 전체를 다시 검수합니다. 그림책 품질 운영 가이드에 통과 기준과 npm run qa:picturebook -- --run-live --fixtures 유료 회귀 평가를 정리했습니다.
그림 모델은 OPENAI_IMAGE_MODEL로 설정합니다. 기본은 gpt-image-2.5-flare이며 1024×1024, low 품질을 사용합니다. 새 책은 인물·의상·소품의 공통 외형을 저장하고 각 쪽의 그림 요청에 함께 전달합니다.
결제에는 SUPABASE_SERVICE_ROLE_KEY, TOSS_PAYMENTS_SECRET_KEY, NEXT_PUBLIC_TOSS_PAYMENTS_CLIENT_KEY가 모두 필요합니다. 누락되면 /api/routes/payment/config는 enabled: false를 반환하고 충전 버튼은 비활성화됩니다. 이 문서는 운영 키 설정 완료를 의미하지 않습니다. 서비스 역할 키는 생성 저장 실패 시 자동 복구에도 필요하며 서버 환경에만 설정합니다. OAuth 리다이렉트 허용 목록에는 배포 도메인의 /auth/callback과 사용 중인 로컬 주소를 등록합니다.
4월 마이그레이션 이력이 반영된 기존 DB에 호환 서버를 배포한 뒤, 같은 릴리스에서 아래 두 마이그레이션을 순서대로 적용합니다. npm run build나 Vercel 배포는 SQL을 자동 적용하지 않습니다.
20260914010000_harden_hodam_accounting.sql: 회계 권한과 생성 요청 중복을 제어하고 운영 권한 스모크 검사도 갱신합니다. 기존 그림책 요청 ID 중복이 있으면 적용이 중단되므로 먼저 확인합니다.20260914020000_private_picturebook_storage.sql: image·profiles를 비공개로 설정하고 새 경로와 기존 사용자별 경로에 소유권 정책을 적용합니다.적용 전 서버 키와 DB 이력, 복구 가능한 백업을 확인하고 적용 후 권한 검사·기존 책 삽화·프로필·결제를 확인합니다. 적용 후에는 공개 object URL과 이전 브라우저 결제에 의존하는 버전으로 코드만 되돌릴 수 없습니다. 호환 버전을 유지하는 복구 절차는 배포 체크리스트를 따릅니다.
서비스 개선 기록과 2차 QA 기록은 통합 전 검증 이력입니다. 당시 신규 AI 생성 성공 검증은 API 이용 한도로 완료하지 못했으므로, 실제 배포 완료 여부와 공급자 검증 결과는 해당 릴리스의 배포 기록에서 확인합니다.
SUPABASE_SERVICE_ROLE_KEYE2E Payments job은 자동으로 skip 됩니다.npm run check:github:failed-runs -- --limit=20 --branch=main --max-age-hours=240npm run check:github:failed-runs:mainnpm run check:github:failed-runs:main:report:localnpm run check:github:failed-runs:main:reportnpm run check:github:failed-runs:main:budget:zero--include-resolved-failures를 추가하세요.no_jobs_started(workflow_file_or_trigger_issue) 감지 시 fail 하고 싶다면:npm run check:github:failed-runs:strict-no-jobs -- --limit=20 --branch=main --max-age-hours=240npm run check:github:failed-runs:strict-no-jobs:mainHODAM_ENFORCE_GITHUB_NO_JOBS_GUARD=1로 켤 수 있습니다.HODAM_ENFORCE_GITHUB_FAILED_RUN_BUDGET=1HODAM_GITHUB_FAILED_RUN_MAX_REPORT_FAILURES (기본 0)HODAM_GITHUB_FAILED_RUN_MAX_NO_JOBS_STARTED (기본 0)HODAM_GITHUB_FAILED_RUN_IGNORE_SHAS에 쉼표 구분 prefix 목록을 넣으세요.