사이드바 위젯 → 코드·플랜·실 DB(alpha·beta) 전수분석 · 2026 최적패턴 기준 해결안
billing_plans 행 + subscriptions.current_period_end에서 동적 조회 (하드코딩 없음). alpha·beta 값·구조 완전 동일.has_dedicated_manager 1종만 전시용(dead).leads 실시간 COUNT 0.12ms · usage_logs 파생집계)로 전환 → 집행방식 2종 표준화.| 플랜 | 검색 월/일/시 | 바이어(월) | 발송 월/일 | 체험일 | 금액(원) |
|---|---|---|---|---|---|
| 14일 무료체험 ← 현재 화면 | 10 / 3 / 2 | 500 | 5,000 / 200 | 14 | 0 |
| Pro 월정기 | 100 / 10 / 3 | 1,000 | 5,000 / 200 | 0 | 198,000 |
| Team 월정기 | 300 / 20 / 5 | 3,000 | 50,000 / 2,000 | 0 | 495,000 |
| Enterprise 맞춤 | 무제한 | 무제한 | 무제한 | 0 | 견적 |
| Internal Infinite (내부) | 무제한 | 무제한 | 무제한 | 0 | 0 |
NULL=무제한. tier 저장 2단 분리 — 숫자 한도는 billing_plans(subscriptions.plan_id 연결), 기능 접근권은 iam_tier_boundaries(tier→policy 매핑).
| 컬럼 | trial | 차단 지점 | 집행 방식 | 상태 |
|---|---|---|---|---|
buyer_search_{h/d/m}_limit | 2/3/10 | rate-limit.service:191/201/211 | 파생집계 (jobs COUNT) | 활성 |
leads_monthly_limit | 500 | usage.service:159 | 저장카운터 | 우회 3경로 |
products_limit | 3 | plan-limits.service:263 | 실시간 행수 | 활성 |
emails_daily_limit | 200 | send-email:476 (Redis+DB) | 파생집계+Redis | 활성 |
emails_monthly_limit | 5,000 | send-email:483 | 저장카운터 | 활성 |
email_accounts_limit | 1 | email-account.service:446 | 실시간 행수 | 활성 |
sending_domains_limit | null | sending-domains.routes:109 | 실시간 행수 | 활성 |
workspaces_limit | 1 | workspace-auth.service:265 | 실시간 행수 | 활성 |
workspace_members_limit | null | plan-limits.service:350 (grandfather) | 실시간 행수 | 활성 |
csv_download_enabled | false | plan-limits:395 + FE | 불리언 게이트 | 활성 |
ai_monthly_budget_usd_cents | $10 | ai-budget.service:174 | 예산 누적합 | 활성 |
trial_days | 14 | recurring-billing:130 → period_end | 시점 계산 | 활성(최다) |
has_dedicated_manager | — | 없음 | — | 전시용(dead) |
중앙 SSOT = getPlanLimits() (5분 인메모리 캐시 + Redis pub/sub 무효화). 검색 차단은 assertBuyerSearchAllowed() 단일 함수.
| 기능 | 표시 | 실제 차단 게이트 | 판정 |
|---|---|---|---|
| 검색 횟수 | 10/월 | 월·일·시 3단 비교 후 throw | 일치 |
| 바이어 저장 | 500/월 | recordUsage(lead_create) — 3경로 미호출 | 불일치 |
| 캠페인 발송 | 5,000·200 | reserveSendQuota daily=defer / monthly=실패 | 일치 |
| 무료체험 만료 | D-14 | 매시간 크론 trialing→expired → 전기능 deny | 일치 |
| 테이블 | 역할 | alpha | beta |
|---|---|---|---|
billing_plans | 플랜별 제한 SSOT | 5 | 5 |
subscriptions | WS→plan·체험만료 | 100 | ~12 |
workspace_usage | 카운터 (leads_used·emails_sent) | 76 | 437 |
usage_logs | append 이벤트로그 | 2,671 | 1,085,696 |
leads | 실제 리드 | 234,885 | 1,157,869 |
buyer_search_jobs | 검색 파생집계 소스 | 356 | 0 |
인덱스 leads_ws_created_id_idx (workspace_id, created_at DESC, id)가 alpha·beta 양쪽에 이미 존재 → 실시간 파생 카운트가 저렴.
leads 1,157,869건 vs usage_logs.lead_create 1,152건(0.1%). workspace_usage.leads_used는 사실상 무의미 → 바이어저장 500 제한이 규모상 이미 무력화. 수동·대량·방문자 적재 우회의 실측 증거.
usage_logs(append 로그)와 workspace_usage(카운터) 두 곳에 기록 → drift. 2026 원칙은 "이벤트 로그 1개 = SSOT, 나머지는 파생". 현재 둘 다 불완전.
metadata.emailsLimit(6000/1500) vs 컬럼(5000) 불일치·코드 미참조 dead JSONB. + beta billing_plans PG stats stale.
핵심: 더 정확한 소스가 이미 DB에 존재(leads 테이블·usage_logs)하는데 부정확한 카운터를 병행 유지 중. 카운터를 삭제하고 정확 소스로 파생.
| 제한 유형 | 방식 | 대상 |
|---|---|---|
| 리소스 개수 | 리소스 테이블 실시간 COUNT(*) | leads · products · domains · accounts · workspaces · members |
| 행위 빈도 | usage_logs 파생 집계 | email_send · search |
저장 카운터(workspace_usage) 방식은 폐기 — 신규 제한 추가 시 금지 원칙.
leads_used 폐기, leads 테이블 실시간 COUNT (0.12ms, 인덱스 존재). 신규 경로 자동 반영 = 우회버그 원천 소멸.usage_logs(workspace_id, created_at) WHERE usage_type='email_send' 추가 후 파생 집계. beta 1.08M 로그가 이미 SSOT.ANALYZE.alpha·beta 값·구조·drift가 완전히 동일 → 동일 마이그레이션으로 양쪽 처리 (alpha-beta-sync 규칙 적용).