개발 slecs

이메일 스팸 수집 차단과 크롤러 접근 범위 정비로 SEO 기반 강화

목차

SEO 강화 작업의 일환으로 이메일 노출 방식과 크롤러 접근 정책을 동시에 손봤다.

왜 이 두 가지를 같이 처리했나

사실 처음엔 따로따로 티켓이 올라와 있었다. 하나는 "contact 페이지에 이메일이 그냥 텍스트로 박혀 있어서 스팸봇에 수집될 수 있다"는 이슈, 다른 하나는 "robots.txt가 사실상 비어 있어서 크롤러가 어디든 들어가고 있다"는 이슈였다. 작업하다 보니 두 건 모두 '크롤러·봇으로부터 어떤 리소스를 어떻게 보호할 것인가'라는 같은 맥락이었고, 한 번에 정리하는 게 맞겠다 싶어서 묶었다.

팀 리드 입장에서 이런 판단이 중요한 이유가 있다. 작은 SEO 관련 작업들은 낱개로 PR이 올라오면 컨텍스트가 분산되고, 리뷰어도 "이게 전체 SEO 전략에서 어느 위치인지" 파악하기 어렵다. 같은 도메인 문제는 묶어서 처리하고 "이 PR 이후로 봇 관련 정책은 이렇게 된다"라는 기준점을 하나 남기는 게 훨씬 낫다.

email obfuscation — 왜 단순 텍스트 노출이 문제인가

contact.astro, about.astro, privacy.astro 세 페이지에 이메일이 흩어져 있었다. HTML 소스를 그대로 읽는 스팸 하베스터 봇 입장에서 [email protected] 같은 패턴은 즉시 수집 대상이다. 스팸 필터가 아무리 좋아도 수집 자체를 막는 편이 운영 피로를 줄인다.

obfuscation 방식은 크게 세 가지가 자주 쓰인다.

방식 구현 난이도 봇 회피 효과 사용자 UX
HTML entity encoding 낮음 중간 (고급 봇엔 통함) 투명함
CSS direction trick 중간 중간 주의 필요
JS 런타임 조합 중간 높음 JS 비활성화 시 노출 안 됨

Astro 환경이라 빌드 타임에 처리 가능한 범위 내에서, HTML entity 방식 또는 속성 분리 방식을 섞어서 적용했다. 예를 들면 이런 패턴이다.

---
// 빌드 타임에 조합, 봇이 읽는 raw HTML에는 분리된 형태로 남음
const user = 'contact';
const domain = 'example.com';
---

<a href={`mailto:${user}@${domain}`}>
  {user}&#64;{domain}
</a>

&#64;@의 HTML entity다. 단순하지만 대부분의 하베스터 봇은 entity 디코딩을 하지 않거나 패턴 매칭으로만 움직이기 때문에 실질적인 차단 효과가 있다. 완벽한 방어는 아니지만, 수집 비용을 높이는 것만으로도 충분히 의미 있다.

robots.txt — disallow 범위 설계

public/robots.txt는 정적 파일이라 빌드 산출물과 무관하게 서버 루트에 그대로 노출된다. 기존 파일이 거의 비어 있거나 Allow: / 한 줄짜리였다면, 검색엔진이 /about, /contact, /privacy 같은 비콘텐츠 경로까지 인덱싱 우선순위에 넣게 된다.

"콘텐츠 경로가 아닌 곳"을 Disallow하는 기준을 잡는 게 핵심이었다. 정리한 기준은 이렇다.

  • 인덱싱 의미 없는 유틸리티 페이지: /privacy, /contact — 검색 유입이 목적이 아님
  • 중복 콘텐츠 가능성이 있는 경로: 태그/카테고리 파생 경로 등
  • 인덱싱 대상으로 유지할 경로: 블로그 포스트, 주요 랜딩 등
User-agent: *
Disallow: /contact
Disallow: /privacy

User-agent: *
Allow: /

about은 팀이나 서비스 소개 맥락에 따라 인덱싱이 유리할 수도 있어서 Disallow 여부를 한 번 더 검토했다. 결국 포함 방향으로 결론 냈지만, 이런 판단은 사이트마다 다르다. "이 페이지가 검색 유입을 원하는가"라는 질문 하나로 정리된다.

회고

솔직히 이런 작업은 기능 개발에 비해 우선순위가 밀리기 쉽다. "당장 서비스가 터지는 것도 아닌데"라는 생각이 팀 전체에 암묵적으로 깔려 있으면 몇 달째 백로그에 묻혀 있다가 "이메일로 스팸이 엄청 온다"는 민원이 터진 뒤에야 처리하게 된다. 그 전에 정리해두는 게 훨씬 싸다.

코드리뷰할 때도 이런 보안·SEO 류의 작은 픽스는 "이거 왜 지금 하는지"를 커밋 메시지나 PR description에 짧게라도 써두는 습관이 필요하다. 그게 없으면 리뷰어가 변경 의도를 파악하는 데 시간을 쓰거나, 최악의 경우 "별 이유 없어 보이는 변경"으로 묻혀서 꼼꼼한 리뷰를 못 받는다. 오늘 작업은 나 스스로도 그 기준을 지키려고 커밋 메시지에 obfuscatedisallow non-content paths를 명시적으로 구분해서 썼다.

다음 단계는 sitemap.xml 생성 자동화와 연동해서 Disallow 경로가 sitemap에서도 빠지도록 일관성을 맞추는 것이다. robots와 sitemap이 따로 놀면 크롤러 신호가 혼선되기 때문에 세트로 관리하는 게 맞다.


끝.


🛒 이 글과 어울리는 추천 상품

*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

댓글 0

첫 댓글 달아줘.