‹ 게임

계정·클라우드 세이브

내부 기획

계정·클라우드 세이브

아카데미 메타 진행(허브, 도감, 공적, 시길 등)을 Supabase Auth + player_saves 에 동기화합니다. 시련 중 런 상태는 기존처럼 세션 메모리이며, 런 종료 후 메타만 저장됩니다.

흐름 (테트리오식)

  1. 타이틀에서 플레이어 ID + 비밀번호로 로그인 또는 계정 만들기
  2. 플레이어 ID는 3~16자, 영문 소문자로 시작, a-z / 0-9 / _ 만 허용
  3. Supabase에는 내부 이메일 {player_id}@player.chaosforge 로 저장 (실제 메일 발송 없음)
  4. syncAccountSaveForUser — 로컬 캐시와 원격 savedAtMs 비교 후 최신본 적용
  5. 게임 중 accountStore 변경 → 로컬 cf_account[:userId] + debounced cloud push (upsert_player_save RPC)

Supabase env가 없으면(VITE_SUPABASE_* 미설정) 기존처럼 로컬 전용 cf_account만 사용합니다.


Supabase 설정 (1회)

1. 이메일/비밀번호 로그인 켜기

대시보드: Authentication → Providers → Email

설정
Enable Email provider ON
Confirm email OFF (필수 — @player.chaosforge 는 실제 메일이 없음)

Confirm email 이 켜져 있으면 가입은 되어도 로그인이 Invalid login credentials 로 실패합니다.

또는 스크립트 (Management API — mailer_autoconfirm: true):

SUPABASE_ACCESS_TOKEN=sbp_… pnpm supabase:auth-email

SUPABASE_ACCESS_TOKEN: Supabase 대시보드 → Account → Access Tokens (sbp_…, 프로젝트 secret key 아님).

2. DB 마이그레이션

pnpm db:apply-migrations

supabase/migrations/20260711210000_player_saves.sqlauth.users 행당 세이브 1개.

supabase/migrations/20260716130000_player_sessions.sqlauth.users 행당 활성 session_id 1개(단일 기기 로그인). 클라이언트가 claim/heartbeat로 upsert·조회하며, RLS로 본인 행만 접근합니다. 상세: SINGLE_SESSION_PLAN.md.

supabase/migrations/20260717160000_player_save_revisions.sql — 클라우드 upsert는 upsert_player_save RPC 경유. 성공 10회마다 player_save_revisions 스냅샷(reason=stack), 전투 직전·직후는 battle_pre/battle_post, 유저당 최근 20건만 유지. sparse가 rich를 덮으려 하면 적용 거부·자동 복구(restored: true). Admin 보드 Player currencies 탭에서 revision 목록(meta) 조회·수동 복구(POST /admin/players/:userId/revisions/:revisionId/restore). 통화 패치 시 현재 본문은 admin_patch revision으로 남김. 상세: PLAYER_SAVE_HISTORY_PLAN.md.

3. 클라이언트 env

packages/client/.env:

VITE_SUPABASE_URL=https://….supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=eyJ…

동작 확인

계정 만들기 (내부 이메일):

curl -s -X POST "$VITE_SUPABASE_URL/auth/v1/signup" \
  -H "apikey: $VITE_SUPABASE_PUBLISHABLE_KEY" \
  -H "Authorization: Bearer $VITE_SUPABASE_PUBLISHABLE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"testplayer@player.chaosforge","password":"secret12","data":{"username":"testplayer"}}'

signup_disabled / email signups are disabled → §1 Email provider 미설정.

로그인 후 세션이 없고 confirm 메일 안내가 나오면 Confirm email 이 아직 켜져 있는 것.


관련 코드

경로 역할
packages/client/src/auth/playerCredentials.ts 플레이어 ID 규칙·내부 이메일 매핑
packages/client/src/auth/supabaseAuth.ts Auth 클라이언트·프로필
packages/client/src/auth/playerSave.ts pull/merge/push
packages/client/src/auth/playerSession.ts 기기 단일 세션 claim/heartbeat
packages/client/src/auth/tabLeader.ts 동일 origin 탭 리더(BroadcastChannel)
public.player_sessions (Supabase) 기기당 활성 session_id — 단일 세션 claim/heartbeat
packages/client/src/components/PlayerAuthForm.tsx 타이틀 로그인/가입 폼
packages/client/src/state/authStore.ts bootstrap·로그인/가입/로그아웃
scripts/supabase/enable-email-password-auth.mjs Email provider + autoconfirm

마이그레이션·계정 전환

  • 세이브 키: cf_account:<userId> (계정마다 분리). 예전 cf_account 단일 키는 클라우드 계정에 자동 병합하지 않음 (신규 가입 = Lv1 새 세이브).
  • 로컬 전용 플레이(VITE_SUPABASE_* 없음)일 때만 cf_account 를 읽습니다.
  • 계정 전환 시 해당 유저의 로컬·클라우드 세이브만 로드합니다.
  • 이미 잘못 병합된 클라우드 세이브는 Supabase player_saves 에서 해당 행을 지우거나, 새 플레이어 ID로 가입하세요.

리더보드 playerId

로그인 시 Supabase user.id를 사용합니다. 미로그인 시 기존 bp_player_id 로컬 UUID를 유지합니다.

(선택) Google / 게스트

  • Google OAuth — 사용 안 함. 필요 시 scripts/supabase/configure-google-auth.mjs
  • 게스트 익명 — 사용 안 함. scripts/supabase/enable-anonymous-auth.mjs

스팀 출시 시 SteamID 연동은 STEAM_RELEASE_PLAN.md 참고.