본 가이드는 xSlide의 편집기 서브도메인(예: editor.xslide.kr), 메인/템플릿 서브도메인(예: www.xslide.kr), 구독/결제 페이지 등 다중 서브도메인 환경에서 회원 세션을 하나로 동기화하고 유저별 사용량(AI 크레딧 및 프로젝트 제한)을 중앙 통제하는 설계안입니다.
1. 서브도메인 간 회원정보(인증) 연동 방안
세션이 분리된 도메인 간에 로그인 상태를 매끄럽게 유지하는 가장 간결하고 안정적인 방법은 와일드카드 공유 세션 쿠키를 활용하는 것입니다.
A. 세션 쿠키 도메인 와일드카드 설정 (가장 추천)
PHP의 기본 세션 쿠키는 기본적으로 접속한 정확한 도메인(예: editor.xslide.kr)에서만 유효합니다. 이를 부모 도메인(.xslide.kr) 전체로 확장하도록 PHP 공통 설정 파일(api/config.php)의 맨 위에 세팅합니다.
php
// api/config.php 또는 세션 시작부
if (session_status() === PHP_SESSION_NONE) {
ini_set('session.cookie_domain', '.xslide.kr'); // 점(.)으로 시작하는 부모 도메인 지정
session_set_cookie_params([
'lifetime' => 86400 * 7, // 7일 유지
'path' => '/',
'domain' => '.xslide.kr', // 모든 서브도메인에서 쿠키 전송 허용
'secure' => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'),
'httponly' => true,
'samesite' => 'Lax',
]);
session_start();
}
- 동작 원리: 사용자가
www.xslide.kr에서 로그인하면 세션 ID가 담긴 쿠키가.xslide.kr범위로 발행됩니다. 이후 사용자가 에디터인editor.xslide.kr로 이동하면 브라우저가 동일한 세션 ID 쿠키를 자동으로 전송하므로 별도의 로그인 처리 없이 즉시 로그인 상태가 공유됩니다. - 전제 조건: 두 서비스가 물리적으로 동일한 서버(또는 세션 스토리지 파일/Redis 서버를 공유하는 서버 환경)에 호스팅되고 있어야 합니다. XAMPP 로컬 환경에서는 동일 디렉토리/동일 DB를 사용하므로 이 방법으로 즉시 100% 동작합니다.
2. 쿼터(Quota) 및 사용량 관리 데이터베이스 설계
사용자의 요금제 등급, 프로젝트 보유 한도, 그리고 AI 크레딧 사용량을 통제하기 위해 users 테이블에 아래 필드들을 추가하거나 user_quotas 테이블을 신설하여 연동합니다.
데이터베이스 필드 확장 (users 테이블 기준)
sql
ALTER TABLE users
ADD COLUMN subscription_tier ENUM('free', 'pro', 'business') NOT NULL DEFAULT 'free',
ADD COLUMN credit_balance INT NOT NULL DEFAULT 40, -- 초기 가입시 무료 40크레딧 부여
ADD COLUMN max_project_limit INT NOT NULL DEFAULT 3, -- Free는 최대 3개 프로젝트
ADD COLUMN billing_cycle_anchor TIMESTAMP NULL; -- 크레딧 리셋 주기 확인용 결제일
3. AI 크레딧 차감 및 사용량 제어 비즈니스 로직
사용자가 AI 생성을 시도할 때 백엔드 API(api/ai_generate_template.php)에서 쿼터를 검증하고 차감하는 실무 시퀀스입니다.
A. AI 생성 API 내 쿼터 확인 및 차감 의사코드 (Pseudo-code)
php
// api/ai_generate_template.php
$user = requireLogin();
$db = getDB();
// 1. 요청 분석 및 예상 소모 크레딧 계산
$requestedSlides = (int)($_POST['slide_count'] ?? 10);
$aiModel = $_POST['ai_model'] === 'pro' ? 'pro' : 'flash';
$neededCredits = ($aiModel === 'pro') ? ($requestedSlides * 5) : ($requestedSlides * 1);
// 2. 트랜잭션 시작 (Race Condition 방지)
$db->begin_transaction();
try {
// 사용자의 현재 크레딧 잔액 확인 (배타적 잠금 적용)
$stmt = $db->prepare("SELECT credit_balance, subscription_tier FROM users WHERE id = ? FOR UPDATE");
$stmt->bind_param("s", $user['id']);
$stmt->execute();
$userData = $stmt->get_result()->fetch_assoc();
$stmt->close();
if (!$userData) {
throw new Exception("사용자를 찾을 수 없습니다.");
}
// 크레딧 부족 시 에러 반환
if ($userData['credit_balance'] < $neededCredits) {
respondError("크레딧이 부족합니다. 현재 잔액: " . $userData['credit_balance'] . " (요청 필요: " . $neededCredits . ")", 402);
}
// 3. Gemini AI 생성 호출 및 결과 처리 (가정)
$generatedSlidesJson = callGeminiAPI($aiModel, $requestedSlides, $sourceText);
// 4. 호출 성공 시 크레딧 원자적 차감
$updateStmt = $db->prepare("UPDATE users SET credit_balance = credit_balance - ? WHERE id = ?");
$updateStmt->bind_param("is", $neededCredits, $user['id']);
$updateStmt->execute();
$updateStmt->close();
// 5. 신규 프로젝트 인서트 처리
$projectId = insertNewProjectFromAi($user['id'], $generatedSlidesJson);
$db->commit();
respond(['ok' => true, 'projectId' => $projectId]);
} catch (Exception $e) {
$db->rollback();
respondError("생성 실패: " . $e->getMessage(), 500);
}
4. 결제 플랫폼(Lemon Squeezy / Stripe) 웹훅 연동
사용자가 구독료를 결제하거나 해지할 때, 쿼터 정보를 실시간 갱신하기 위해 결제사 웹훅(Webhook)을 연동해야 합니다.
- 최초 구독 결제 성공 시 (
subscription_created):- 결제사 웹훅 신호를 받아 결제 정보에 기재된 유저 이메일/ID를 매핑합니다.
subscription_tier를'pro'로 승격하고,credit_balance에 300 크레딧을 즉시 가산하며,max_project_limit을 무제한(예:9999)으로 늘려 줍니다.
- 구독 주기 갱신 시 (
subscription_payment_success):- 다음 달 구독 결제가 성공하면, 기존 크레딧 잔액을 300 크레딧으로 리필(또는 기존 크레딧에 누적 합산)해 줍니다.
- 구독 취소/만료 시 (
subscription_expired/subscription_cancelled):- 유저의 요금제를 다시
'free'로 변경합니다. max_project_limit을 다시3개로 하향 조정합니다. (기존 프로젝트를 강제 삭제하지는 않고, 3개 초과 시 새 프로젝트 생성만 차단하도록 유도하는 것이 정석입니다.)
- 유저의 요금제를 다시
댓글 0