"team 검색 무제한" 결정을 위한 코드 + DB 구조 전수분석 (buyer-search-v2)
ai_monthly_budget)·usage_logs 어디에도 안 걸림 — 검색비용은 별도 무한 경로.query.service.ts:69 검색 시작 전 호출하는 게이트는 assertBuyerSearchAllowed 단 하나.
AI 미션이 가진 ai_monthly_budget_usd_cents($500/team) 예산 게이트를 검색은 거치지 않으며, usage_logs·workspace_usage에도 기록하지 않는다.
→ rate limit을 풀면 검색비용을 상한하는 다른 메커니즘이 존재하지 않는다.
| 단계 | 저장소 | 집계 대상 | 목적 | trial/pro/team |
|---|---|---|---|---|
| 시간당 | Redis sorted set (sliding window 3600s) | 모든 검색 시작 (결과 무관) | burst 어뷰징 핵심 방어 — 0건 무한반복 차단 | 2 / 3 / 5 |
| 일간 | DB COUNT (buyer_search_jobs) | billable만 | 일일 남용 상한 | 3 / 10 / 20 |
| 월간 | DB COUNT (buyer_search_jobs) | billable만 | 비용 쿼터 (초과 시 nudge 메일 발송) | 10 / 100 / 300 |
status='completed' AND total_companies > 0인 검색만 산입. 결과 0건·실패·취소·진행중은 제외(페이월 신뢰 유지).
단 시간당(Redis)은 결과와 무관하게 시작 시점에 기록 — 0건 검색 무한반복 abuse를 막아야 하므로.
assertBuyerSearchAllowed가 3단 전부 스킵(rate-limit.service.ts:169-187).
query.service.ts:69assertBuyerSearchAllowed→
admin bypass?→
시간→일→월 순차 비교→
초과 시 TooManyRequestsError
// rate-limit.service.ts:191-220 — null(무제한)이면 비교 자체를 건너뜀 if (usage.hourly.limit !== null && usage.hourly.used >= usage.hourly.limit) throw TooManyRequestsError("hourly_limit_exceeded") if (usage.daily.limit !== null && usage.daily.used >= usage.daily.limit) throw TooManyRequestsError("daily_limit_exceeded") if (usage.monthly.limit !== null && usage.monthly.used >= usage.monthly.limit) { fireLimitNudge(userId, workspaceId) // 리액티브 넛지 메일 throw TooManyRequestsError("monthly_limit_exceeded") }
한도값이 null이면 해당 if 조건이 통째로 skip → 그 축은 완전 무제한. 즉 team h/d/m를 모두 null로 두면 세 방어선이 전부 무효화됨.
| 컬럼 | 용도 |
|---|---|
workspace_id, created_at | 일·월 COUNT 필터 키 |
status / total_companies | billable 판정 (completed & >0) |
cost_breakdown (jsonb) | 실비용: totalCostUsd, llm/hasdataSerp/millionVerifier 분해 |
mode, pro_metadata, criteria_sources | 검색 모드·메타 |
| 플랜 | 시간 | 일 | 월 |
|---|---|---|---|
| trial | 2 | 3 | 10 |
| pro | 3 | 10 | 100 |
| team (현행) | 5 | 20 | 300 |
| Internal Infinite | null | null | null |
시간당은 DB가 아닌 Redis(buyer_search:rate:{workspaceId} zset)에서 집계. 일·월만 buyer_search_jobs COUNT.
| 비용 항목 (대형 검색 1회) | $ | 내역 |
|---|---|---|
| LLM | 0.92 | 320콜 / 입력 227만·출력 23만 토큰 |
| HasData SERP | 0.06 | 116콜 |
| MillionVerifier | 0.07 | 145건 이메일 검증 |
| 합계 | 1.05 | — |
| 한도 | 역할 | null로 풀면 |
|---|---|---|
| 시간당(Redis) | 폭주/버그 방어 — 스크립트·재시도 루프 | 버그 1개로 시간당 $수십~수백 |
| 일간 | 일일 남용 상한 | 하루 무제한 |
| 월간 | 비용 쿼터 (team 최악 ≈ 300×$0.34 ≈ $100/월) | 무한 — 월 수천건×$1 손실 가능 |