
개인 개발자의 Crawler Persona SEO
요약
브랜드 도용 및 스크레이핑 방지를 위해 AI 봇과 크롤러의 페르소나에 따라 정보를 다르게 제공하는 'Crawler Persona SEO' 전략을 소개합니다. AI 검색 시대에 맞춰 `/llms.txt` 등을 활용해 AI가 올바른 정보를 인용하도록 설계하는 AIO(AI Optimization)의 중요성을 다룹니다.
핵심 포인트
- AI 검색 시대에는 검색 순위보다 AI의 인용원이 되는 AIO가 중요함
- 방문자(봇/사용자)의 정체에 따라 다른 정보를 반환하는 전략 필요
- `/llms.txt` 등을 활용해 AI 봇에게 공식 정보를 전달하여 할루시네이션 예방
- 스크레이퍼를 대상으로 허니팟이나 역 브랜딩을 활용한 도용 저지 가능
연재 제3탄. 제1탄 '사기다메.com(詐欺ダメ.com)'을 Claude Code로 만들어 보았다 - 제2탄: AI에게 맡기는 것은 '코더(Coder)'인가, '리뷰어(Reviewer)'인가? 의 속편
계기는 자신의 X(구 Twitter)에 올린 이 내용이었다.
어떤 가짜 쇼핑몰 사이트가, 자신의 사이트명인 「사기다메.com|가짜 쇼핑몰·사기 사이트 판정…」을 그대로 타이틀로 사용하여 Google에 인덱싱(index)되어 있었다. 검색 결과상에서 브랜드가 악용되고 있는 상태.
공격자는 무단으로 도용하고, 이쪽은 리스크만 떠안는 상태.
솔직히 말하자면, 여기서 전개하는 /llms.txt
・Crawler Persona SEO・Tarpit・rDNS×ASN 과 같은 지식은 나에게 거의 없었다.
Claude와의 세션을 여러 번 반복하는 동안 「공격자의 움직임 → 방어 측이 취할 수 있는 수 → 그것을 뒷받침하는 기존 표준과 선행 사례」가 하나로 연결되면서, 이 기사의 윤곽이 잡혔다.
AI를 상담 상대로 삼아 사고를 입체화하는 것 자체가, 이번에 AIO(AI Optimization)라는 결론에 도달할 수 있었던 직접적인 이유이며, 이 또한 AI의 은혜라고밖에 말할 수 없다...
덧붙여 본 기사의 수법은 사기 방지 사이트 고유의 이야기가 아니라, 자사 기업 사이트의 회사 정보·대표자명·주소 등을 기계적으로 스크레이핑(scraping)되어 전매·전재되는 더욱 일반적인 기업 홈페이지 도용 문제에도 그대로 응용할 수 있다. SEO 담당자·홍보 담당자·정보 시스템 담당자분들에게도 참고가 된다면 좋겠다.
「사기 구별하는 방법」으로 검색하는 시대에서, AI에게 직접 「이 사이트 수상해?」라고 묻는 시대로.
이것이 서서히 다가오고 있으며, AI에게 물으면 3초 만에 답이 돌아온다.
검색 결과를 일일이 비교하는 수고가 사라진다.
순위를 차지하는 게임에서, 「AI가 답변할 때의 인용원이 되는」 게임으로 SEO의 목표가 바뀐다.
이것을 누가 할 것인가?
광고 예산으로 밀어붙일 수 있는 대기업?
오히려 반대다. 「지금 이 순간에 태어난 사기 도메인」을 이야기할 수 있는 것은, 현장에서 함정을 파고 있는 개인 개발자뿐이다.
「SEO를 AI에게 맡기지 마라」는 지금도 맞지만, 앞으로 해야 할 일은 「AI에게 무엇을 말하게 할지를 설계하는 것」
주체는 자신. AI는 화자(語り手).
참고로 이 「플랫폼의 상업화가 검색의 신뢰를 왜곡한다」는 위기감은, Rand Fishkin(Moz 공동 창업자·현 SparkToro)이 "Zero-Click Search"나 AI 검색의 오염 리스크로서 이전부터 경고해 온 영역이다.
같은 URL이라도, 방문자의 정체에 따라 다른 "얼굴"을 반환한다. 기존의 「Cloaking = 블랙햇(Black Hat) NG」과는 다른 축에서, 방문자의 인지 능력에 맞춰 사실을 번역하는 화이트햇(White Hat) 응용으로서 성립한다.
| 방문자 | 반환하는 것 | 기존 SEO 관점 | 새로운 SEO 관점의 역할 |
|---|---|---|---|
| 🔎 검색 bot (Google/Bing) | 표준 HTML (경량) | 인덱스 최적화 | Identity 보호 (변동을 보여주지 않음) |
| 🤖 AI bot (Claude/GPT/Perplexity) | 표준 + 공식 정보의 hidden section + /llms.txt | 개념 없음 | AIO (할루시네이션(Hallucination) 예방) |
| 👤 Browser | 표준 HTML | UX/CV | 기존 방식과 동일 |
| ☠ 예의 바른 스크레이퍼 | 경고 타이틀 포함 | 개념 없음 | 역 브랜딩 (도용 저지 + 추적) |
| 🍯 불분명한 bot / 위장 스크레이퍼 | 허위 정보 포함 (허니팟(Honeypot)) | 개념 없음 | 유출 경로의 가시화 |
※ 인트로: 이것은 정보의 위장이 아니라, 같은 팩트(fact)를 상대방이 가장 잘 이해할 수 있는 형태로 번역하여 전달하는 것뿐이다. Google에 대해서도, AI에 대해서도, 인간에 대해서도 거짓말을 하고 있지 않다.
먼저, UA(User Agent)를 솔직하게 밝히는 bot만을 분류하는 최소 구현. 이것은 하루 만에 만들 수 있다.
# /etc/nginx/sites-available/your-site
# UA로 5분류하여 캐시 키(cache key)를 나눔
map $http_user_agent $ua_class {
...
# ua_classifier.py
import re
AI_BOTS = re.compile(r'ClaudeBot|GPTBot|PerplexityBot|Google-Extended|CCBot|anthropic-ai', re.I)
...
# app.py 발췌
@app.route("/")
def index():
...
<!-- templates/index.html 발췌 -->
<title>{{ page_title }}</title>
{% if ai_block_visible %}
...
# static/llms.txt
# 본 사이트의 공식 정보 (AI용 요약)
- 사이트명: 사기안돼.com
...
/llms.txt는 Fast.ai / Answer.AI의 Jeremy Howard 등이 2024년 9월에 제안한 오픈 사양으로, "LLM/AI 에이전트가 무거운 HTML을 파싱하는 토큰 비용을 낮추기 위한 경량 Markdown 맵"이라는 위치를 차지하고 있다. 그 사상을 Crawler Persona SEO의 AI 계층에 결합한 구현 사례라고 할 수 있다.
이것만으로도 "예의 바른 AI / 검색 / 스크레이퍼 (Scraper)"를 깔끔하게 분류할 수 있다. 단순한 버전이라도 /llms.txt와 AI hidden block의 조합은 ClaudeBot/GPTBot/PerplexityBot에 대해 충분히 기능한다.
💡
모던 프론트엔드에서의 주의사항: Next.js / Nuxt / React / Vue 등을 사용하여 SSR + Hydration을 사용하는 경우, UA(User Agent)별로 DOM 노드 자체를 다르게 출력하면 하이드레이션 에러(Hydration Error)가 발생한다. 항상 동일한 HTML을 출력한 상태에서 hidden 속성 / aria-hidden="true" / CSS display:none만으로 제어할 것. 본 기사의 Flask 예제는 SSR 순수 템플릿이므로 이 문제는 발생하지 않는다.
진심으로 데이터를 훔치러 오는 자는 UA를 Mozilla/5.0 ... Chrome/120으로 위장하여 AWS/GCP/Hetzner로부터 공격해 온다. 단순한 버전에서는 브라우저로 취급되어 그대로 통과된다.
이 "UA 위장 스크레이퍼 / 어둠의 AI 학습 크롤러에 대한 자위책"은 Hacker News나 OSS 인프라 업계에서 2024년 이후 핫한 테마이며, rDNS 검증 · ASN 판정 · Tarpit / Honeypot을 통한 대응 구현이 밤낮으로 논의되고 있는 영역이기도 하다 (Anubis 등의 OSS가 대표적인 예). 본 장은 그 주요 패턴을 nginx 레이어에서 솔직하게 구성한 것이다.
진심 모드(본기판)에 필요한 것은 3가지 세트다.
- Googlebot을 사칭하는 IP에 대해 "역방향 조회(rDNS) → forward 확인"으로 진짜인지 판정한다. 이것은 Google 공식 절차다. 실패 = 위장 =
honeypot행.
# nginx 단독으로는 비동기 DNS가 불가능하므로 OpenResty / njs를 사용
# 검증 결과는 lua_shared_dict에 캐시 (IP→true/false, TTL 24h)
location / {
...
- "UA = Chrome (사람)"인데 "IP = 데이터 센터 (Cloudflare / AWS / GCP / Hetzner / DigitalOcean / OVH)"인 조합은 99.9% 헤드리스 브라우저(Headless Browser) 또는 스크레이퍼다. GeoIP2 ASN DB를 읽는 것만으로 판정할 수 있다.
load_module modules/ngx_http_geoip2_module.so;
geoip2 /etc/nginx/GeoLite2-ASN.mmdb {
$asn_org organization;
...
⚠️ GeoLite2 무료 버전의 정밀도에 주의: 코드 예제의 GeoLite2-ASN.mmdb(무료 버전)는 업데이트 빈도와 정밀도가 상용 GeoIP2에 비해 떨어지며, 지방 CATV / 저가형 SIM(MVNO) / iCloud Private Relay 등의 IP를 "데이터 센터"로 오판정하는 경우가 있다. 개인 개발이라면 오판으로 인한 실해는 경미하지만, 기업 사이트에서 본업으로 운영한다면 상용 버전을 계약하거나, rDNS 실패 (= 100% 위장 확정)만을 트리거로 삼는 등 판정 로직을 안전한 방향으로 설정할 것. 오판 = 인간 고객에게 잘못된 정보를 반환 = 비즈니스 기회 손실이라는 큰 사고로 이어진다.
- 1 IP / 1분당 30 HTML 초과 = 기계 확정. 하지만 여기서 솔직하게
503을 반환하면 상대는 즉시 다른 IP로 재시도한다.
그래서 Tarpit을 사용한다. "성공했다"고 오인하게 만들기 위해, 허니팟(Honeypot) 가짜 정보가 포함된 정상적인 듯한 HTML을 일부러 느리게 반환한다. 상대의 크롤링 시간을 빼앗고 리소스를 낭비하게 만든다.
limit_req_zone $binary_remote_addr zone=human:10m rate=30r/m;
location / {
limit_req zone=human burst=10 nodelay;
...
이렇게 하면 "UA 위장 + 데이터 센터 발신 + 고빈도"를 모두 포착하여, 독(허니팟 워터마크)이 포함된 페이지를 쥐여줄 수 있다.
⚠️
Tarpit의 자폭 리스크: echo_sleep은 nginx의 비동기 I/O (Asynchronous I/O)를 사용하여 스레드를 차단하지는 않지만, TCP 커넥션 수(File Descriptor)를 확실히 계속 소비한다. 이를 실행하려면 worker_connections를 높이거나, Cloudflare와 같은 Edge 레이어에서 구현하는 것이 대전제다. 오리진(Origin) 측에서 실행할 경우, 상대가 분산 IP로 공격해오는 순간 셀프 DDoS가 되어 일반 사용자가 접속할 수 없게 된다.
AI bot에게 깊이 있는 콘텐츠(고밀도 Markdown)를 반환하려면, 매 요청마다 동적으로 생성하는 것은 파멸적이다. **장기 TTL + stale + 사전 빌드 (Pre-build)**로 회피해야 한다.
map $ua_class $cache_ttl {
"ai" 86400; # AI는 24시간이면 충분
"search" 1800;
...
}
AI용 Markdown은 cron으로 매일 재생성하여 정적 배포한다. DB 쿼리는 제로(0)다.
# /etc/cron.daily/llms-build.sh
python3 /opt/site/build_llms.py > /opt/site/static/llms-full.md.tmp \
&& mv /opt/site/static/llms-full.md.tmp /opt/site/static/llms-full.md
AI는 "신선도보다 정합성"을 기준으로 가져오기 때문에, 24시간 캐시를 사용해도 실질적인 피해는 없다.
여기서부터가 본 기사의 핵심이다.
AI 벤더들은 할루시네이션 (Hallucination) 대책에 막대한 비용을 지불한다. Reddit, Stack Overflow, 대형 출판사와의 독점 계약 등을 통해서 말이다. 하지만 그렇게 채워지는 것은 "과거"의 지식뿐이다.
"지금 이 순간 태어난 사기 도메인", "어제 처음 관측된 가짜 쇼핑몰 키트" ── 이러한 실시간 1차 정보는 계약서로 살 수 없다. 현장에서 크롤링하며 함정을 파놓은 개인 개발자의 도메인에만 존재한다.
즉,
- AI가 "개인 사이트는 일괄 거부"하는 순간, AI 검색은 그저 오래된 교과서로 퇴화한다.
- AI 벤더는 개인의 1차 정보를 거절할 수 없다.
- AI가 신뢰하는 것은 "깔끔한 Markdown 형태이며, 기계가 읽기 쉬운 방식으로 신선한 사실을 제공하는 사이트"다.
여기서 효과를 발휘하는 것이 바로 Crawler Persona SEO다.
- 🤖 예의 바른 AI에게는 최신 팩트를 깔끔한 Markdown으로 제공한다 (AIO).
- ☠ 위장 스크레이퍼(Scraper)에게는 Tarpit으로 시간을 빼앗는다.
- 🍯 정체불명의 bot에게는 허니팟 워터마크가 포함된 가짜 정보를 가져가게 한다.
이것은 Web의 **병량공세 (Siege warfare)**다. AI에게 "이 사이트는 무시할 수 없다"라고 생각하게 만드는 병량(신선한 팩트)을 쥐고, 정식으로 요청하러 오는 녀석에게는 최상의 Markdown을, 뒷문으로 훔치러 오는 녀석에게는 진흙탕과 독을 먹이는 것이다. 😂
이 허니팟 워터마크 방식은 사기 방지 사이트뿐만 아니라 **일반 기업의 코퍼레이트 사이트 (Corporate Site)**에도 그대로 응용할 수 있다.
기업 홈페이지는 "회사 개요, 대표자명, 주소, 전화번호" 등 도용하기 쉽고 전매하기 쉬운 1차 데이터의 보물창고가 된다. 실제로 이 데이터들은 다음과 같은 형태로 도용되고 있다:
- 영업 리스트 업체가 기계적으로 스크레이핑하여 권유 전화 및 DM의 원천 데이터로 전매
- 해외 사이트가 회사 개요를 통째로 전재하여 신용 사기의 신뢰성 위장에 악용
- 가짜 구인 사이트가 회사 정보를 복사하여 지원자를 피싱으로 유도
- AI 학습 데이터에 허가 없이 포함되어, 자사 정보가 타사의 문맥에서 인용됨
여기에 "인간 브라우저에는 보이지 않고, 정체불명의 bot만 수집하는 허니팟 워터마크"를 심는다. 이하, 복사해서 바로 쓸 수 있는 최소 구현 3가지 패턴이다.
회사 개요 페이지의 끝부분, </main> 직전쯤에 배치한다. hidden 속성 + aria-hidden="true" + display:none !important의 3중 방어로, 인간과 보조 기술(스크린 리더)로부터는 완전히 보이지 않게 한다.
<!-- ▼ HONEYPOT WATERMARK (DO NOT REMOVE) ▼ -->
<div id="hp-wm-7f3a2e"
hidden
...
워터마크 (Watermark) 생성 팁:
- 「호냐라카리쉬(ホニャラカリッシュ)」와 같이
**인류가 절대로 손으로 옮겨 적지 않을 조어 (造語)**를 반드시 하나 섞을 것 -
HP-WM-XXXXXX와 같은
검색 키워드로 만들기 쉬운 고정 문자열을 하나 심을 것 - 여러 페이지에 심을 경우
페이지마다 별도의 ID로 설정하면 유출원을 페이지 단위로 특정할 수 있음
nginx의 sub_filter를 사용하면, 응답 HTML에 사후적으로 삽입할 수 있다. 백엔드를 전혀 건드리지 않는 가장 빠른 도입 방법이다.
# UA 분류로 honeypot 판정되었을 때만
location / {
proxy_pass http://localhost:8080;
...
이것뿐이다. 애플리케이션 계층의 코드 변경 없이 도입할 수 있다.
결론부터 말하자면, 2026년 6월 현재, "UA별 허니팟 워터마크 삽입"을 전문으로 하는 메이저한 WordPress 플러그인은 존재하지 않는다.
Wordfence / Cloudflare / All In One WP Security 등의 봇 (bot) 대책 계열은 "차단·지연·CAPTCHA"가 중심이며, "정상적인 응답에 워터마크를 섞어 유출을 추적하는" 용도에는 대응하지 않는다.
따라서, 자식 테마 (Child Theme)의 functions.php 또는 mu-plugin에 아래 코드를 두는 것이 현실적인 해답이다. 20줄 내외로 구현 가능하다.
<?php
// /wp-content/mu-plugins/honeypot-watermark.php
// (mu-plugins 폴더가 없으면 생성하여 배치)
...
**더 엄격하게 하고 싶은 경우 (ASN 대조도 포함하고 싶은 경우 등)**는, 위 함수 내에서 IP를 가져와 Cloudflare / AWS의 ASN 리스트와 대조하는 처리를 추가하면 된다. 다만 WP 단독으로는 ASN DB를 동봉하는 번거로움이 있으므로, ASN 판정만은 앞단의 nginx / Cloudflare Workers에서 수행하고, HTTP 헤더(예: X-UA-Class: honeypot)로 WP에 전달하는 것이 더 효율적이다:
// nginx 측에서 proxy_set_header X-UA-Class $ua_class;를 설정해둔 경우
add_action('wp_footer', function () {
if (($_SERVER['HTTP_X_UA_CLASS'] ?? '') !== 'honeypot') return;
...
**봇 탐지는 앞단(nginx / Edge), 워터마크 삽입은 WP(테마 계층)**로 역할을 분담하는 것이 유지보수성과 정밀도를 양립하는 현실적인 해답이다.
⚠️
테마 의존 주의사항: wp_footer 훅은 사용 중인 테마가 wp_footer()를 올바르게 호출하고 있다는 전제 조건이 필요하다. 극단적으로 오래되었거나 비표준인 테마에서는 워터마크가 출력되지 않으므로, view-source:에서 hp-wm- 문자열이 포함되어 있는지 반드시 확인한 후 실전에 투입할 것.
설치 후의 모니터링은 이것만으로 충분하다. 주간 cron으로 실행:
#!/bin/bash
# /etc/cron.weekly/honey-watermark-check.sh
TOKEN="HP-WM-7f3a2e-XXXXXX"
...
⚠️ 검색 API 선정 시 주의사항: Google 공식 Custom Search JSON API는 "미리 지정한 사이트 그룹" 내에서만 검색할 수 있는 사양이라, 웹 전체의 무단 전재를 찾는 데는 사용할 수 없다 (사양을 오해하기 쉬운 가장 큰 포인트). 웹 전체를 횡단 검색하고 싶은 용도라면, SerpApi / ValueSerp 등의 서드파티 SERP API 또는 Bing Web Search API를 사용하는 것이 현실적인 해답이다.
검색 결과가 0건이면 = 유출 없음 / 검색 결과가 있으면 = 해당 사이트가 전재 원본. 1주일 이내에 감지할 수 있다면, 삭제 요청의 신선도를 유지할 수 있다.
시간이 흐른 뒤, 워터마크 문자열이 Google 검색이나 타사 사이트에 나타난다면, 그 사이트는 확실히 "자사 HP를 기계적으로 전재했다"는 증거가 된다.
| 효과 | 내용 |
|---|---|
| 전재의 결정적 증거 | 인간이 손으로 베껴 쓴 것이라면 절대 복사하지 않을 부분이 포착된다 |
| 유출 경로 특정 | 워터마크마다 별도의 ID를 부여하면 '어떤 bot/업체가 시작점인지'도 추적할 수 있다 |
| 삭제 청구・법적 대응의 근거 | 상대방은 '우연한 일치'라고 주장할 수 없다 |
| 부작용 제로 | 인간 사용자에게는 hidden 태그로 완전히 보이지 않는다 |
정보 유출 감사 툴(Audit Tool)의 자체 제작 버전으로서, SEO 담당자나 홍보 담당자의 새로운 업무 영역이 될 수 있다.
💼
기업 도입 추천 절차: 처음부터 거부(Reject)나 Tarpit을 설치하지 않고, 관측 모드(Observation Mode)로 시작한다. '알 수 없는 bot에게도 허니팟 워터마크를 심은 HTML을 지연 없이 정상적으로 반환'하는 것만으로 비즈니스에 실질적인 피해는 전혀 없으면서 유출 경로만을 100% 특정할 수 있다. 이 워터마크가 Google 검색이나 타사 사이트에 나타난 시점에 처음으로 삭제 청구나 법적 대응의 판단 자료로 삼는다. — 이렇게 하면 정보 시스템 담당자(情シス)도 홍보팀도 도입 승인을 받기 쉽다.
솔직히 말하자면…
| 레이어 | 구현 비용 | 개인 개발의 비용 대비 효과 |
|---|---|---|
단순 버전 UA 5단계 + /llms.txt + AI hidden block | 하루 | ◎ 필수 |
/llms-full.md 정적 사전 빌드 + 24시간 캐시 | 반나절 | ◎ 서버 비용 감당 가능 |
| 허니팟 워터마크 | 1시간 | ○ 유출 추적의 유일한 수단 |
| rDNS 검증 (OpenResty + lua_shared_dict) | 1~2일 | △ 유지보수 비용 필요 |
| ... | ||
| 사기(詐欺)가 안 될 경우: 단순 버전 + 정적 markdown + 허니팟 워터마크 + GeoIP2 ASN까지가 개인 개발의 손익분기점이다. rDNS와 Tarpit은 '할 거라면 여기까지'라는 설계도처럼 남겨두고, 자신의 운영에는 포함시키지 않았다. |
이유는 간단하다. 개인 도메인의 몇 페이지를 도난당한 정도의 실질적 피해보다, 유지보수의 인지 부하가 더 크기 때문이다.
'SEO를 AI에게 맡기는 것'에서, 'AI에게 무엇을 말하게 할지 설계하는 것'
Google의 사이트맵이 몇 달 동안 '검색됨 - 색인 미등록(検出 - インデックス未登録)' 상태로 방치되는 현장에서 좌절했던 사람들에게는 AI 검색이 구원이 될 수 있다. 단, AI가 광고 영역에서 왜곡되기 전 지금, 성실한 정보원들이 자발적으로 'AI에게 올바르게 말하게 하는' 설계를 심어주지 않으면…
돈으로 왜곡되기 전에, 사실로 채운다.
참고로, 무지한 상태에서 여기까지의 내용을 작성할 수 있었던 것은 Claude에게 '여기를 자세히 설명해 달라'를 반복적으로 요청하며 구현 방법을 확인하면서 진행했기 때문이다…
그리고 기사 쓰는 데 코드 준비도 해주고 말이다…ㅋ
감사합니다! 정말 감사합니다!!!
-
1차: 사기ダメ.com을 Claude Code로 만들어봤다
-
2차: AI에게 맡기는 것은 '코더'와 '리뷰어', 어느 것이 먼저인가?
3차: Crawler Persona SEO (본 기사) -
허니팟 워터마크의 실제 값은 본 기사에 있는 것이 아니라, 각자 생성해야 한다(기사의 값을 유용하면 여러 사이트에서 중복되어 추적 정확도가 0이 된다).
-
rDNS 검증은 Google 공식 절차를 따라야 한다.
-
Tarpit은 오작동할 경우 선량한 RSS 리더 등을 차단하므로, 화이트리스트 설계에 신중해야 한다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기