대부분의 SaaS 제품에서 가짜 무료 계정 하나는 데이터베이스에 한 줄(row)을 비용으로 발생시킵니다. 하지만 AI 앱에서는 토큰(tokens)이 비용입니다. 무료 크레딧을 소진하는 새로운 계정은 곧 청구서가 되며, 무료 티어를 악용하는 사람들은 이를 알고 있습니다. 즉, 새 이메일 주소, 새 브라우저 프로필, 때로는 VPN을 사용하며 어제와 동일한 작업량을 수행합니다.
일반적인 해결책들은 모두 허점이 있습니다. 일회성 받은 편지함(disposable inbox)을 가진 사람들을 막는 것은 이메일 인증으로는 불가능합니다. 무료 티어에 신용카드를 요구하면 남용과 대부분의 가입자를 막을 수 있습니다. IP 비율 제한(IP rate limits)은 하나의 NAT 뒤에 있는 전체 사무실을 차단해 버립니다.
작동하는 방법은 '레이어링(layers)'입니다. 각 레이어는 자체적으로 저렴하며, 가장 먼저 배치되어 사용자가 누구인지 파악하기도 전에 청구서가 발생하는 것을 막아줍니다. 이것이 우리가 구축할 순서대로의 설정입니다.
1. 우선 폭발 반경을 제한하라 (Cap the blast radius first)
무언가를 감지하려고 시도하기 전에, 최악의 경우를 사용자가 정한 숫자로 제한해야 합니다.
- 모든 무료 사용에 대한 글로벌 일일 예산. 이 예산을 소진하면, 무료 요청은 대기열에 들어가거나 다음 날까지 기다립니다. 하루가 잘못되어 갑작스러운 청구서로 이어지는 것을 막을 수 있습니다.
- 필요하다고 생각하는 것보다 낮은 수준의 계정별 일일 제한(per-account daily cap).
- 모델 호출 전에, 서버에서 둘 다 확인해야 합니다. (나중에 확인할 것이 아닙니다.)
// 무료 티어 사용자를 위해 모델을 호출하기 전
async function canSpendFree(userId: string, estTokens: number) {
const today = new Date().toISOString().slice(0, 10);
...
}
이것만으로는 남용을 막지 못합니다. 하지만 남용을 지루하고 제한된 활동으로 만들 수 있으며, 이것이 가장 큰 승리입니다.
2. 각 계정의 가치를 낮춰라 (Make each account worth less)
남용은 경제학 문제입니다. 계정당 지급액(payout)을 낮추면 대부분의 악용자는 멈춥니다.
- 한 번에 큰 금액 대신 조금씩 크레딧을 공급하라. '일주일 동안 매일 50개'는 '지금 당장 350개'를 남용하는 것보다 가치가 훨씬 낮습니다. 정직한 사용자는 이를 거의 알아채지 못합니다.
- 새 계정은 더 저렴한 모델로 시작하게 하라. 좋은 모델은 이메일 인증과 어느 정도의 정상적인 사용 후에 잠금 해제되도록 합니다. 악용자가 계정당 지출하는 비용은 올라가고, 당신이 부담하는 비용은 내려갑니다.
- 신뢰를 통해 더 많은 기능을 잠금 해제하라: 계정 연령, 정상 사용량, 인증된 이메일 또는 신용카드를 요구할 수 있지만, 카드는 해당 계정이 위험해 보일 때만 요청하세요 (5단계).
3. 이메일 레이어를 정리하라 (Clean up the email layer)
결심한 사람들을 막기 위함이 아니라, 오후의 작업 시간으로 만들고 가장 게으른 '파밍(farming)' 활동을 제거하기 위함입니다.
const DISPOSABLE = new Set(disposableDomains); // 예: 오픈 소스 "disposable-email-domains" 목록
export function normalizeEmail(raw: string) {
...
}
정규화된 이메일을 고유 인덱스와 함께 저장하고, 계정이 삭제될 때 해당 해시값을 유지하여 '삭제 후 재가입'을 통해 무료 티어가 초기화되는 것을 방지합니다.
4. 개별 계정을 판단하기보다 그룹으로 분류하기
단 하나의 신호만으로는 아무것도 증명할 수 없습니다. VPN 사용은 프라이버시를 중시하는 사용자에게 흔하며, 공유 IP는 모든 사무실과 이동통신사에서 볼 수 있는 일반적인 현상입니다. '파머(farmer)'들을 드러내는 것은 그들의 계정들이 서로 비슷하게 보인다는 점입니다:
- IP 주소뿐만 아니라 네트워크 유형. 사무실의 NAT나 이동통신사의 IP는 가정용 또는 모바일 ISP에 해당합니다. VPN 출구 지점이나 클라우드 서버에서 한 시간 동안 생성된 열 개의 계정은 그렇지 않습니다. 이러한 구분이 오탐(false positives)을 줄여줍니다.
- 단일 세션 내 네트워크 변경: 가입 도중에 IP, ASN 또는 국가가 바뀌는 것은 로테이팅 프록시(rotating proxy)의 강력한 신호입니다.
- 타이밍: 이전 계정이 크레딧을 소진한 직후 몇 분 간격으로 새로운 계정을 생성하는 경우.
- 작업 부하: 오래된 계정과 동일한 첫 번째 프롬프트, 파일 또는 프로젝트 형태를 보이는 경우.
첫날부터 모든 가입에 대해 이러한 신호들을 기록하십시오. 기록하지 않은 역사는 군집화(cluster)할 수 없습니다.
5. 신호가 쌓였을 때만 마찰력을 추가하기
이제 이 신호들을 부드럽게 사용합니다:
| 신호 | 대응 방식 |
|---|
| 특이 사항 없음 | 정상적인 무료 티어 이용 허용 |
| ... | |
정직한 대다수의 사용자들은 이러한 과정을 전혀 경험하지 못합니다. 하지만 파머는 모든 계정에서 이를 경험하게 됩니다.
FindIP Shield가 적용되는 곳
공개 정보: 저희는 FindIP Shield라는 무료 도구를 만들었는데, 이 도구는 4단계와 5단계의 네트워크 부분을 담당합니다. 이는 각 방문과 양식 제출을 0점에서 100점 사이로 점수화하며 (VPN, 프록시, Tor, 호스팅 또는 데이터센터 네트워크, 알려진 악성 IP, 세션 중 IP 변경 등) 그 이유를 함께 제공합니다. 또한 위험도가 높은 제출에 대해서만 짧은 대기 시간 추가, Turnstile 확인, 또는 중지 메시지를 추가할 수 있으며, 이 기능은 사용자가 활성화할 때까지 비활성화되어 있습니다. 이는 기기를 점수화하는 것이 아니라 네트워크를 점수화하므로 (장치 지문 인식(device fingerprinting)이 없으므로), 자체적으로는 새로운 프로필에서 같은 노트북을 인식하지 못합니다. 또한 양식 값을 절대 읽지 않습니다.
브라우저 측은 하나의 스크립트 태그로 구성됩니다:
<script
src="https://cdn.findip.net/shield/v1.js"
data-site-key="pub_xxxxxxxxx"
...
가입 요청 시 Shield 세션 ID를 함께 전송하고 (FindIP.getSession().sessionId), 서버에서 결정을 내리도록 하십시오. 왜냐하면 브라우저에서 결정된 것은 우회될 수 있기 때문입니다:
async function shieldVerdict(sessionId: string) {
const res = await fetch('https://shield.findip.net/v1/shield/sessions/verify', {
method: 'POST',
...
이 판정 결과를 3단계와 4단계에서 얻은 자체 신호들과 결합하여 5단계의 표에 입력하십시오. Shield는 사이트당 하루 최대 10,000 이벤트까지 무료이며, 가입 페이지가 직접 작성된 것이 아니라면 WordPress 플러그인, Shopify 앱, GTM 템플릿, 그리고 코드가 필요 없는 Cloudflare 통합 기능도 제공됩니다.
간단 요약
- 서버에서 전역 및 계정별 무료 사용량을 제한합니다.
- 크레딧을 점진적으로 지급하고 신규 계정을 더 저렴한 모델로 시작하게 합니다.
- 이메일을 정규화하고, 일회용 도메인을 차단하며, 삭제된 계정을 기억합니다.
- 네트워크 유형, 타이밍 및 작업 부하별로 계정을 클러스터링합니다.
- 신호가 쌓인 곳에만 마찰을 추가하고, VPN 사용자를 절대적으로 차단하지 않습니다.
당신은 모든 의지가 강한 사람들을 막을 수는 없습니다. 그럴 필요도 없습니다. 당신이 필요한 것은 '파밍(farming)' 비용이 절약되는 비용보다 더 들게 만드는 것이며, 정직한 사용자들은 여전히 한 번의 클릭만으로 진입할 수 있게 하는 것입니다.