바이어 검색 Rate Limit · 비용 거버넌스 분석

"team 검색 무제한" 결정을 위한 코드 + DB 구조 전수분석 (buyer-search-v2)

2026-07-02 env: alpha + beta buyer-search-v2 PG 18 + Redis
TL;DR

1. 핵심 — 검색엔 다른 비용 게이트가 없다

rate limit = 검색의 유일한 비용 governor

query.service.ts:69 검색 시작 전 호출하는 게이트는 assertBuyerSearchAllowed 단 하나. AI 미션이 가진 ai_monthly_budget_usd_cents($500/team) 예산 게이트를 검색은 거치지 않으며, usage_logs·workspace_usage에도 기록하지 않는다. → rate limit을 풀면 검색비용을 상한하는 다른 메커니즘이 존재하지 않는다.

2. 3단 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

billable 정의 — "가치를 본 검색만 차감"

일·월 카운트는 status='completed' AND total_companies > 0인 검색만 산입. 결과 0건·실패·취소·진행중은 제외(페이월 신뢰 유지). 단 시간당(Redis)은 결과와 무관하게 시작 시점에 기록 — 0건 검색 무한반복 abuse를 막아야 하므로.

관리자 우회

user role admin 또는 workspace member admin이면 assertBuyerSearchAllowed가 3단 전부 스킵(rate-limit.service.ts:169-187).

3. 코드 흐름

검색 요청 query.service.ts:69
startBuyerSearch
assertBuyerSearchAllowed 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로 두면 세 방어선이 전부 무효화됨.

4. DB 구조

buyer_search_jobs (일·월 집계 소스)

컬럼용도
workspace_id, created_at일·월 COUNT 필터 키
status / total_companiesbillable 판정 (completed & >0)
cost_breakdown (jsonb)실비용: totalCostUsd, llm/hasdataSerp/millionVerifier 분해
mode, pro_metadata, criteria_sources검색 모드·메타

billing_plans (한도 SSOT) — 검색 관련

플랜시간
trial2310
pro310100
team (현행)520300
Internal Infinitenullnullnull

시간당은 DB가 아닌 Redis(buyer_search:rate:{workspaceId} zset)에서 집계. 일·월만 buyer_search_jobs COUNT.

5. 비용·활동 실측

$0.34
검색 1회 평균 비용 (alpha 실측)
$1.05
검색 1회 최대 (LLM 320콜)
333
alpha completed 검색 (누적)
85
beta v2 검색 (통계 stale로 0 표기됐던 값)
비용 항목 (대형 검색 1회)$내역
LLM0.92320콜 / 입력 227만·출력 23만 토큰
HasData SERP0.06116콜
MillionVerifier0.07145건 이메일 검증
합계1.05

6. "team 검색 무제한" 리스크 & 권장

한도역할null로 풀면
시간당(Redis)폭주/버그 방어 — 스크립트·재시도 루프버그 1개로 시간당 $수십~수백
일간일일 남용 상한하루 무제한
월간비용 쿼터 (team 최악 ≈ 300×$0.34 ≈ $100/월)무한 — 월 수천건×$1 손실 가능

왜 위험한가

Internal Infinite가 h/d/m 전부 null인 건 신뢰된 내부 사용자라 가능. 고객 노출 유료 team 플랜에 그대로 적용하면, 검색엔 다른 방어선이 0개이므로 버그·악의적 스크립트 한 번에 검색비용($1+/건)이 무제한으로 샌다.

권장 — 월만 개방, 시간·일은 안전레일 유지

  • monthly = null (체감 무제한 = 상품 논리)
  • hourly·daily는 유지/상향 (예: 20 / 100) — 폭주 비용 폭탄 방어
  • 완전 null을 원하면, 별도 워크스페이스 월 검색비용 상한(ai_budget처럼 $ 기반 cost cap)을 먼저 도입 후 개방하는 것이 2026 최적패턴