광고 문의 폭주 방어하기
목차
광고 문의 기능이 연달아 도배되는 문제가 터졌다. 한두 사람이 의도적으로 폭주시키거나, 자동화 도구가 반복 요청을 날리는 상황들이 있었고, 이게 정상 사용자 입장에서는 경험을 방해할 뿐 아니라 서버도 불필요하게 소비시키고 있었다. 단순한 스팸 차단이 아니라 시스템 안정성과 사용 편의성 모두를 고려해서 방어 체계를 짜기로 했다.
도배 문제의 실제 임팩트
처음엔 "몇 건 정도야?" 하고 넘어가려다가, 실제 로그를 보니 생각보다 심각했다. 한 IP에서 1시간에 20건이 넘는 요청이 들어오는 경우도 있었고, 24시간 단위로 보면 50건을 초과하는 케이스까지 있었다. 이게 남는 일은 아니다:
- DB 쓰기 부하: 매 요청마다 새 레코드가 쌓이는데, 이런 도배 요청까지 모두 저장하면 테이블이 빠르게 팽창
- 운영 팀 피로: 스팸과 정상 요청을 분류하고 정리하는 데 시간 소모
- 정상 사용자 의심: 연달아 요청을 보낼 이유가 없으니 도배한 사람 입장에서는 시스템이 먹히는 줄 알고 계속 누르게 됨
계층형 방어 전략으로 설계
이번에 선택한 방식은 프론트엔드와 백엔드 두 단계에서 모두 제어하는 것이었다.
클라이언트 단계(Astro 컴포넌트)
- 버튼을 누른 후 5분간 재요청 불가 상태로 전환
- 남은 시간을 화면에 표시해서 사용자가 "아, 방금 요청했구나" 하고 이해하게 함
- 로컬 스토리지나 메모리에 마지막 요청 시간 기록
// src/components/AdInquiryWidget.astro 개념
// 5분 쿨다운 처리
const [lastSubmitTime, setLastSubmitTime] = useState(null);
const isCooldown = lastSubmitTime &&
Date.now() - lastSubmitTime < 5 * 60 * 1000;
const handleSubmit = () => {
if (isCooldown) {
showToast("5분 후 다시 요청해주세요");
return;
}
submitInquiry()
.then(() => setLastSubmitTime(Date.now()))
}
서버 단계(API 엔드포인트)
- IP를 기반으로 rate limit 적용
- 1시간 내 3건, 24시간 내 10건이 최대치
- 초과하면 429 Too Many Requests 응답
// src/pages/api/ad-inquiry.ts 로직
const checkRateLimit = async (clientIP) => {
const hour1Count = await redis.get(`rate:1h:${clientIP}`);
const day24Count = await redis.get(`rate:24h:${clientIP}`);
if (hour1Count >= 3 || day24Count >= 10) {
return false; // 초과
}
return true;
}
왜 두 단계를 모두 필요했나? 프론트엔드 제어만으로는 브라우저 개발자 도구로 우회되고, 서버 제어만으로는 사용자가 불쾌한 에러만 만나게 되기 때문이다. 함께 써야 한다.
1시간 3건, 24시간 10건으로 정한 이유
숫자를 정할 때 팀과 여러 번 토론했다.
- 너무 엄격하면? (예: 1시간 1건): 정상적인 재검토, 추가 질문이 필요한 경우를 막는다. 사용자는 "첫 번째 요청이 제대로 들어갔나?" 하고 한두 번 더 보내고 싶을 수 있다.
- 너무 느슨하면? (예: 1시간 20건): 도배 방어 의미가 없다. 스팸 봇은 여전히 대량 요청을 날린다.
현재 설정값의 의도:
- 1시간 3건: 정상 사용 패턴 고려. 요청 후 회신 기다리다가 "혹시 들어갔나?" 재확인, 추가 사항 기술 이렇게 3회 정도면 충분
- 24시간 10건: 같은 사람이 여러 제품/서비스를 문의하거나, 날을 나눠서 문의할 여유를 둠. 근데 24시간 10건이면 하루에 평균 1.6건 정도라 문의만 하는 사람 아니면 무리
5분 쿨다운은 "지금 막 요청했으니 조급해하지 말고 잠시 기다려" 하는 심리적 버퍼다. 기술적으로 요청을 한 번 받았으니 즉시 따라붙는 재요청은 거를 수 있다.
일반론으로 배운 점
이런 유형의 작업을 하면서 깨달은 몇 가지:
계층형 방어 원칙
- 클라이언트 / 서버 / 데이터베이스 각 단계에서 자체 제어를 가진다
- 한 단계가 뚫려도 다음이 버틴다
모니터링이 전제
- rate limit을 지정한 후로는 반드시 모니터링해야 한다
- 혹시 정상 사용자까지 막진 않나? 도배는 정말 줄었나? 주기적으로 확인
UX와 보안의 밸런스
- "요청 불가" 상태를 명확하게 전달하는 게 중요
- 단순히 버튼을 비활성화만 하는 게 아니라 "왜 비활성화했는지" 메시지를 보여준다
IP 기반 제어의 한계
- VPN 사용자나 회사 네트워크에서는 같은 IP로 여러 사람이 나간다
- 나중에 필요하면 사용자 ID 기반 rate limit 추가 고려
이번 작업으로 단순 스팸 차단을 넘어 사용자 경험과 시스템 안정성을 함께 고려하는 게 얼마나 중요한지 다시 느꼈다.
🛒 이 글과 어울리는 추천 상품
*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.
댓글 0
첫 댓글 달아줘.