기능별 화면 라우트 · 한도 UI/모달 · 체크 값 · BE 게이트 · 초과 UX 총정리
useWorkspaceUsage/useSearchUsage → 백엔드 getPlanLimits.| 기능 | 화면(링크) | 한도 UI (파일) | 체크 값 | BE 게이트 (파일) | 성격 | 초과 UX |
|---|---|---|---|---|---|---|
| 바이어 검색 실행 | /lead-discovery | 사이드바 막대 SidebarUpgradeCta:174 |
buyerSearch{H/D/M}Limit |
rate-limit.service:191-220 |
FG | throw 429 (현재 전용모달 없음) |
| 검색결과 → 그룹 저장 | /lead-discovery/:sessionId | 모달 UsageConfirmDialog(v2):59 |
leadsUsed / leadsMonthlyLimit |
usage.service:159 |
FG | 모달: 부분진행 / 업그레이드 · throw |
| 바이어 목록 → 그룹 추가 | /leads | 토스트 CustomerTable:533-599 |
USAGE_MONTHLY / PLAN_PRODUCT |
usage.service:159 |
FG | 토스트 8s + "업그레이드"(하드 네비) |
| 캠페인 활성화 | /sequences | 없음(현재) | emailsMonthlyLimit |
sequences.routes:445 (신규 #9387) |
FG | throw · 전용모달 없음(개선 대상) |
| 테스트 메일 발송 | /sequences/proposal | 모달 ProposalTestSendDialog:299 |
일 테스트 used / limit |
(테스트 전용 카운터) | FG | 모달 내 버튼 비활성 + 안내 |
| 캠페인 실제 발송 | —(백그라운드 워커) | 없음(유저 부재) | emails{Daily/Monthly}Limit |
send-email:532 · usage.service:451 |
BG | defer(24h)/fail · 모달 불가 → 알림 |
| 이메일 계정 연동 | /settings | 없음(에러 토스트) | emailAccountsLimit |
email-account.service:446 |
FG | throw PLAN_EMAIL_ACCOUNT… |
| 플랜 사용량 확인 | /settings/billing | PlanUsageSection:162-178 |
leads·emails used/limit | —(표시 전용) | — | 80%↑ 업셀 callout |
| 플랜 비교/업그레이드 | /upgrade | 모달 UpgradePlanDialog:38 |
전 플랜 limits 비교 | — | — | 플랜 카드 → /upgrade/:tier |
FG=포그라운드(모달 가능) · BG=백그라운드(모달 불가). 값 소스: useWorkspaceUsage·useSearchUsage(hooks/usage.ts) → getPlanLimits.
| 컴포넌트 | 파일:줄 | 구조 | 체크/표시 값 | 여는 방식 |
|---|---|---|---|---|
| 모달 UpgradePlanDialog | components/UpgradePlanDialog.tsx:38-229 |
Pro/Team/Ent 3열 비교표 + 가격 | 전 limits(leads·emails·search·계정·도메인·상품·WS·멤버·CSV·매니저) | 부모 state open/onOpenChange |
| 모달 UsageConfirmDialog v2 | pages/lead-discovery-mastra/UsageConfirmDialog.tsx:59-454 |
기존/새 그룹 2탭 + 그룹명 + 한도경고 + CTA | leadsUsed / leadsMonthlyLimit, remainingQuota, isOverLimit |
검색완료 후 ChatRoom에서 open |
| 모달 UsageConfirmDialog v1 | pages/lead-discovery/UsageConfirmDialog.tsx:51-362 |
그룹명 입력 + 한도경고 + 진행/업그레이드 | leadsUsed / leadsMonthlyLimit, remainingQuota |
ChatRoom onOpenChange |
| 모달 ProposalTestSendDialog | components/sequences/proposal/ProposalTestSendDialog.tsx:46-330 |
수신주소 + 단계선택 + 남은횟수 | 일 테스트 used / limit, quotaExhausted |
제안서 페이지 "테스트" 버튼 |
| SidebarUpgradeCta | components/app-sidebar/SidebarUpgradeCta.tsx:92-193 |
검색/바이어/발송 3막대 + 상태헤더 | search.monthly, leadsUsed, emailsSent vs 각 limit | 사이드바 상시(조건부 표시) |
| PlanUsageSection | pages/settings/components/PlanUsageSection.tsx:41-225 |
플랜정보 + 바이어/발송 progress + 업셀 | leads·emails used/limit/percentage | /settings/billing 상시 |
| 토스트 CustomerTable quota | pages/lead-discovery/CustomerTable.tsx:533-599 |
사전 checkLimit + 에러 토스트 + 액션버튼 | USAGE_MONTHLY_LIMIT_EXCEEDED·PLAN_PRODUCT_LIMIT_REACHED |
그룹추가 액션 시 자동 |
| 버튼 SidebarUpgradeButton | components/app-sidebar/SidebarUpgradeButton.tsx:20-52 |
"플랜 업그레이드" CTA → /upgrade | subscriptionDisplay(상태) | 비관리자+트라이얼/만료/미납 |
| 공통 code→문구 | lib/api/utils/extract-error.ts:82 |
ApiError.code → errors.<CODE> i18n + details 보간 |
모든 PLAN_*/USAGE_* code | mutation onError에서 호출 |
| 게이트 | 파일:줄 | 체크 값(컬럼) | 차단 | 우회 |
|---|---|---|---|---|
| 검색 실행(시/일/월) | buyer-search-v2/rate-limit.service.ts:191-220 | buyer_search_{hourly/daily/monthly}_limit | throw 429 | admin·null(team월) |
| 그룹 저장(리드 적재) | customer-group.service.ts:312 → usage.service.ts:159 | leads_monthly_limit | throw(트랜잭션) | skipUsageCharge·null |
| 캠페인 활성화 | sequences.routes.ts:445 → usage.service.ts (assertMonthly…) | emails_monthly_limit(소진 시) | throw | null(무제한) |
| 발송 L1(일, Redis) | workspace-plan-limit.service.ts:147 | emails_daily_limit | defer 24h | null·circuit fail-open |
| 발송 L2(fallback) | usage.service.ts:451 | emails_daily/monthly | throw | null |
| 발송 L3(계정 reserve) | send-email.ts:532-570 | emails_daily/monthly | defer/fail | null |
| 이메일 계정 연동 | email-account.service.ts:446 | email_accounts_limit | throw | null |
| 상품 등록 | plan-limits.service.ts:263 | products_limit | throw(Forbidden) | null |
| 발송 도메인 | sending-domains.routes.ts:109 | sending_domains_limit | throw | null |
| 워크스페이스/멤버 | workspace-auth.service.ts:265 · plan-limits.service.ts:350 | workspaces_limit·workspace_members_limit(grandfather) | throw | null |
모든 게이트는 getPlanLimits()(5분 캐시+Redis pub/sub) 단일 SSOT로 한도 조회. code→FE 문구는 common.csv errors.<CODE>.
| 기능 | 경로(링크) | 페이지 |
|---|---|---|
| 바이어 검색 | /lead-discovery · /:sessionId | LeadDiscoveryMastraPage |
| 바이어 목록 | /leads · ?view=companies|people · /add | LeadsPage |
| 캠페인 | /sequences · /create · /proposal · /edit | SequencesPage |
| 요금제/사용량 | /settings/billing | BillingSettingsPage |
| 발송 도메인 | /settings/sending-domains/new · /:domainId | SendingDomain*Page |
| 업그레이드 | /upgrade · /upgrade/:tier | UpgradePlanPage / UpgradeCheckoutPage |
| 대시보드 | /dashboard | DashboardHome |
라우터 정의: router/index.tsx · router/dashboard-routes.tsx · router/dashboard-admin-routes.tsx
/lead-discovery → BE assertBuyerSearchAllowed(시/일/월) 통과해야 실행recordUsage(lead_create) leads_monthly_limit throw/sequences → BE assertMonthlyEmailQuotaNotExhausted(월 소진 시 throw)