React 애드블록 감지 A to Z 사용자 경험과 수익을 모두 잡는 방법
웹사이트를 운영하다 보면 광고 수익이 정말 중요한 역할을 하거든요.
그런데 요즘 사용자분들이 애드블록을 많이 사용하시면서 이게 큰 고민거리가 되었습니다.
그래서 오늘은 React 환경에서 어떻게 애드블록을 감지하고, 또 어떻게 지혜롭게 대처할 수 있는지 그 A to Z를 전부 파헤쳐 보려고 하는데요.
이건 단순히 기술적인 싸움이 아니라, 우리 사이트의 생존과 사용자 경험 사이의 줄타기라고 할 수 있습니다.
애드블록, 도대체 어떻게 광고를 막는 걸까
먼저 적을 알아야 싸움에서 이길 수 있겠죠.
애드블록이 어떤 원리로 동작하는지 알면, 우리가 어떻게 감지해야 할지 실마리를 잡을 수 있습니다.
보통 애드블록은 크게 네 가지 방식으로 동작하는데요.
각각의 방식을 이해하면 왜 우리가 여러 감지 기법을 함께 써야 하는지 명확해집니다.
첫 번째는 'DNS 필터링' 방식이거든요.
이건 특정 광고 서버의 주소 자체를 차단 목록에 넣어두고, 해당 주소로 가는 모든 요청을 막아버리는 겁니다.
마치 특정 가게가 있는 골목 자체를 막아버려서 아무도 접근 못 하게 하는 것과 같습니다.
두 번째는 광고 스크립트나 리소스의 URL을 기준으로 차단하는 'URL 필터링'인데요.
adsbygoogle.js
처럼 이름만 들어도 광고와 관련된 파일들의 다운로드를 원천적으로 막는 방식입니다.
이건 골목은 열어두되, '광고' 간판을 단 가게에는 못 들어가게 막는 경비원 같은 역할입니다.
세 번째는 '콘텐츠 필터링' 또는 'CSS 인젝션'이라는 방식이거든요.
이건 일단 페이지에 광고 요소가 그려지기는 하는데, 애드블록이 강제로 CSS 스타일을 주입해서 display: none;
같은 속성으로 우리 눈에 안 보이게 숨겨버리는 겁니다.
가게는 열려있고 사람도 들어갔는데, 갑자기 투명 망토를 씌워버리는 것과 비슷합니다.
마지막으로 'JS 인젝션'은 광고를 실행하는 자바스크립트 코드의 작동 자체를 멈추게 하는 방식인데요.
가게 문은 열려있지만, 가게 안의 직원들이 아무 일도 못 하도록 막는 셈입니다.
그래서 우리는 바로 이 '요청 실패'와 '요소 숨김'이라는 두 가지 현상을 이용해서 애드블록을 감지해낼 수 있습니다.
React에서 애드블록 감지하기 실전편
이제 본격적으로 React 환경에서 써먹을 수 있는 실용적인 감지 코드들을 알아볼 텐데요.
하나의 방법만으로는 모든 애드블록을 잡아낼 수 없기 때문에, 여러 방법을 섞어서 사용하는 것이 핵심입니다.
1. 광고 스크립트 요청 실패를 이용한 탐지
가장 직관적이고 간단한 방법은 애드블록이 차단할 만한 유명 광고 스크립트에 일부러 요청을 보내보는 거거든요.
만약 요청이 실패해서 에러가 발생한다면, 그건 중간에 누군가 요청을 가로챘다는 뜻이 됩니다.
import { useState, useEffect } from 'react';
function useAdBlockDetectorByRequest() {
const [isBlocked, setIsBlocked] = useState(false);
useEffect(() => {
fetch(
"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js",
{
method: "HEAD",
mode: "no-cors",
cache: "no-store",
},
)
.then(response => {
// 성공적으로 응답이 오면 애드블록이 없다고 판단할 수 있습니다.
// 하지만 일부 애드블록은 빈 응답을 보내기도 해서 100%는 아닙니다.
})
.catch(() => {
// 요청 자체가 실패하면 거의 확실하게 애드블록이 동작 중인 겁니다.
console.log("애드블록 감지: 요청이 차단되었습니다.");
setIsBlocked(true);
});
}, []);
return isBlocked;
}
여기서 method: "HEAD"
는 실제 데이터를 다 받아오지 않고 헤더 정보만 요청해서 네트워크 자원을 아끼는 작은 팁이고요.
mode: "no-cors"
는 CORS 정책 위반 에러를 피하기 위한 설정입니다.
이 방법은 DNS나 URL 필터링 방식의 애드블록을 잡아내는 데 아주 효과적입니다.
2. '허수아비' 요소를 이용한 탐지
하지만 어떤 애드블록은 요청은 그대로 두되, 페이지에 렌더링된 광고 요소만 교묘하게 숨겨버리거든요.
이런 '콘텐츠 필터링' 방식은 요청 실패만으로는 잡아낼 수 없습니다.
그래서 우리는 일부러 광고인 척하는 '허수아비' 요소를 심어두고, 이 요소가 제대로 보이는지 감시해야 하는데요.
이게 바로 CSS 인젝션 방식의 애드블록을 잡아내는 핵심 전략입니다.
import { useState, useEffect, useRef } from 'react';
function useAdBlockDetectorByElement() {
const [isBlocked, setIsBlocked] = useState(false);
const baitRef = useRef(null);
useEffect(() => {
const checkElement = () => {
const baitElement = baitRef.current;
if (baitElement) {
// 요소의 높이가 0이거나, 숨김 처리되거나, 투명 처리되면 차단된 것으로 간주합니다.
if (
baitElement.offsetHeight === 0 ||
baitElement.style.display === 'none' ||
baitElement.style.visibility === 'hidden' ||
window.getComputedStyle(baitElement).getPropertyValue('display') === 'none'
) {
console.log("애드블록 감지: 허수아비 요소가 숨겨졌습니다.");
setIsBlocked(true);
}
}
};
// DOM이 변경될 때마다 체크하기 위해 MutationObserver를 사용하면 더 정확합니다.
// 하지만 간단하게 구현하기 위해 setTimeout을 사용해 주기적으로 체크합니다.
const intervalId = setInterval(checkElement, 500);
return () => clearInterval(intervalId);
}, []);
// 실제 렌더링될 허수아비 요소입니다.
// 애드블록이 흔히 차단하는 클래스 이름을 사용합니다.
const BaitComponent = () => (
<div
ref={baitRef}
className="adsbygoogle"
style={{ height: '1px', width: '1px', position: 'absolute', left: '-10000px', top: '-10000px' }}
aria-hidden="true"
/>
);
return { isBlocked, BaitComponent };
}
이 코드의 핵심은 애드블록이 타겟으로 삼을 만한 클래스 이름(adsbygoogle
)을 가진 아주 작은 요소를 만드는 겁니다.
그리고 이 요소의 실제 높이(offsetHeight
)가 0이 되거나 display
속성이 none
으로 바뀌면 애드블록이 작동했다고 판단하는 원리입니다.
사용자 눈에는 보이지 않도록 화면 밖으로 멀리 던져두는 게 포인트입니다.
3. 두 가지 방법을 합쳐서 정확도 높이기
앞서 말했듯이, 하나의 방법만으로는 부족하거든요.
그래서 이 두 가지 훅을 합쳐서 종합적으로 판단하는 상위 훅을 만드는 게 좋습니다.
function useAdBlockDetector() {
const isBlockedByRequest = useAdBlockDetectorByRequest();
const { isBlocked: isBlockedByElement, BaitComponent } = useAdBlockDetectorByElement();
// 둘 중 하나라도 true이면 애드블록이 켜진 것으로 판단합니다.
const isBlocked = isBlockedByRequest || isBlockedByElement;
return { isBlocked, BaitComponent };
}
// 실제 앱에서 사용하는 방법
function App() {
const { isBlocked, BaitComponent } = useAdBlockDetector();
return (
<div>
{/* 허수아비 컴포넌트는 항상 렌더링해둬야 합니다. */}
<BaitComponent />
{isBlocked && (
<div>애드블록이 감지되었습니다! 사이트 운영을 위해 해제를 부탁드립니다.</div>
)}
{/* ... 나머지 앱 콘텐츠 ... */}
</div>
);
}
이렇게 하면 요청 차단 방식과 요소 숨김 방식의 애드블록을 모두 효과적으로 잡아낼 수 있습니다.
물론 adblock-detect-react
같은 라이브러리를 쓰는 방법도 있지만, 이런 라이브러리는 애드블록에게 역으로 탐지당하기 쉽다는 단점이 있습니다.
그래서 직접 구현한 로직과 병행해서 사용하는 것이 가장 안전한 방법입니다.
애드블록 감지 후, 어떻게 대처해야 할까
애드블록을 감지하는 기술보다 어쩌면 더 중요한 것이 바로 '어떻게 대응하느냐' 이거든요.
사용자를 불쾌하게 만들면 바로 이탈로 이어지기 때문에, 아주 신중하게 접근해야 합니다.
최악의 선택, 콘텐츠 전체 막기
가장 먼저 피해야 할 방법은 화면 전체를 덮는 모달 창을 띄워서 '애드블록 끄기 전까지는 아무것도 못 봐!'라고 으름장을 놓는 건데요.
유튜브 프리미엄처럼 대체 불가능한 서비스가 아니라면, 대부분의 사용자는 뒤로 가기 버튼을 누를 겁니다.
이건 정말 최후의 수단으로 남겨둬야 합니다.
현명한 선택, 부드럽게 요청하기
가장 좋은 방법은 페이지 상단이나 하단에 작은 배너를 띄우는 겁니다.
사용자의 콘텐츠 소비를 방해하지 않으면서, 우리 상황을 정중하게 알리는 거죠.
{isBlocked && (
<div className="fixed bottom-0 w-full bg-yellow-200 p-3 text-center shadow-md">
🚫 저희 사이트는 광고 수익으로 운영되고 있어요. 괜찮으시다면 애드블록 해제를 고려해주시겠어요?
</div>
)}
여기서 중요한 건 메시지의 '톤앤매너'인데요.
'광고를 봐야 한다'고 강요하는 대신, '사이트 운영을 위해 도와달라'고 협조를 구하는 편이 훨씬 효과적입니다.
투명하게 상황을 공유하면 사용자들도 더 쉽게 마음을 열어줄 겁니다.
그리고 한 번 메시지를 본 사용자에게 계속 보여주면 피로감을 줄 수 있으니, 로컬 스토리지를 이용해서 '오늘 하루 보지 않기' 같은 기능을 넣어주는 것도 좋은 사용자 경험 설계입니다.
로컬 환경에서 광고 테스트하기
그런데 막상 개발할 때 구글 애드센스 같은 광고는 로컬 환경(localhost
)에서 제대로 나오지 않아서 테스트하기가 참 까다롭거든요.
이럴 때 쓸 수 있는 꿀팁이 있습니다.
먼저 내 컴퓨터의 hosts
파일을 수정해서 로컬 환경을 실제 도메인처럼 속여주는 건데요.
맥이나 리눅스에서는 /etc/hosts
파일에, 윈도우에서는 C:\Windows\System32\drivers\etc\hosts
파일에 아래 내용을 추가합니다.
127.0.0.1 dev.내도메인.com
이렇게 한 줄만 추가해주면 됩니다.
그리고 애드센스 스크립트 태그에 data-adtest="on"
속성을 추가하는 건데요.
이걸 붙이면 실제 수익에는 집계되지 않는 테스트용 광고가 송출되기 시작합니다.
이제 로컬에서도 실제 광고가 붙는 상황을 완벽하게 시뮬레이션하며 애드블록 감지 로직을 테스트할 수 있습니다.
마무리하며
애드블록 감지는 단순히 광고 차단기를 막는 기술적인 행위가 아니거든요.
이건 우리 서비스의 지속 가능성과 사용자 경험이라는 두 마리 토끼를 모두 잡기 위한 섬세한 커뮤니케이션 과정입니다.
오늘 소개해드린 방법들을 조합해서 사용하면 대부분의 애드블록은 감지할 수 있을 겁니다.
하지만 기술보다 중요한 것은 감지 이후에 사용자와 어떻게 소통하느냐에 달려있다는 점을 꼭 기억해주세요.
강압적인 차단보다는 부드러운 설득으로, 우리 사이트의 든든한 후원자로 만들어나가는 지혜가 필요합니다.