애드센스 심사를 올리기 전에 내 사이트에서 찾아낸 것들
2026.08.03
애드센스 심사를 올리려고 사이트를 처음부터 끝까지 훑었다. 글 수도 충분하고 정책 페이지도 있으니 그냥 신청하면 되겠다고 생각했는데, 훑어보니 네 가지가 걸렸다. 그중 하나는 몇 달째 배포돼 있던 버그였다.
1. 동작하지 않는 광고 태그가 글 45편에 실려 있었다
라이브 사이트의 블로그 글 HTML을 그대로 받아서 광고 태그를 찾아봤다.
curl -s https://joowonkoh.com/blog/dev/2026040204 | grep -o 'data-ad-client="[^"]*"'돌아온 값이 이랬다.
data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"
XXXXXXXXXXXXXXXX. 컴포넌트를 만들 때 자리만 잡아두려고 넣은 값인데, 그대로 배포돼서 색인된 글 45편 전부에 실려 나가고 있었다. 슬롯 ID도 마찬가지로 1234567890이었다.
동작하지 않는 광고 태그는 광고를 안 띄우는 것보다 나쁘다. 빈 <ins> 껍데기가 글마다 남고, 심사자가 그 자리를 보면 깨진 광고 영역으로 읽힌다. 애드센스가 "사이트가 준비되지 않았다"고 판단할 근거를 스스로 만들어 준 셈이다.
원인은 단순했다. 컴포넌트에 기본값을 박아 두고, 나중에 채운다고 생각하고 배포한 것이다. 문제는 "나중에"가 오지 않아도 아무 에러가 나지 않는다는 점이다. 빌드도 통과하고 페이지도 잘 뜨니까 잊어버린다.
2. 광고 단위 ID는 승인 후에만 만들 수 있다
고치려고 대시보드에 들어가서 알게 된 게 이거다. 애드센스에서 다루는 ID가 두 종류인데 성격이 완전히 다르다.
즉 승인 전에는 슬롯 ID를 채워 넣을 방법이 아예 없다. 광고 단위를 만드는 메뉴 자체가 승인될 때까지 잠겨 있다. 심사에 필요한 건 <head>에 들어가는 스크립트 한 줄뿐이다.
그래서 결론은 "심사 기간에는 광고 자리를 아예 그리지 않는다"였다. 값이 없으면 안 그리게 만드는 쪽으로 갔다.
// src/lib/adsense.ts
export const ADSENSE_CLIENT = "ca-pub-…"; // ads.txt와 같은 값
export const ADSENSE_SLOT = process.env.NEXT_PUBLIC_ADSENSE_SLOT || null;export default function AdSense() {
// 슬롯이 없으면 아무것도 그리지 않는다
if (!ADSENSE_SLOT) return null;
return <ins className="adsbygoogle" data-ad-client={ADSENSE_CLIENT} data-ad-slot={ADSENSE_SLOT} … />;
}이렇게 두면 승인 뒤에 할 일이 환경변수 한 줄 추가로 끝난다. 무엇보다 값을 잊어도 가짜 태그가 나가지 않는다. 자리를 잡아두는 기본값이 왜 위험한지 이번에 배웠다. 기본값은 "없음"이어야 하고, 없으면 조용히 실패하는 게 아니라 아무것도 하지 않아야 한다.
3. 캔버스 페이지의 본문이 350자였다
내 사이트에는 플레이그라운드라는 코너가 있고, 웹 미니게임이 아홉 개 들어 있다. 문제는 이 페이지들이 캔버스 하나로만 되어 있다는 것이다. 페이지에 실제로 들어 있는 텍스트를 세 봤다.
curl -s https://joowonkoh.com/playground/ddong-mallang \
| python3 -c "
import sys, re
h = sys.stdin.read()
h = re.sub(r'<script.*?</script>', '', h, flags=re.S)
print(len(re.sub(r'\s+', '', re.sub(r'<[^>]+>', ' ', h))))"355자. 헤더와 푸터를 빼면 사실상 제목뿐이다. 사람이 봐도 이게 뭘 하는 건지, 어떻게 노는 건지 알 수 없다. 검색엔진 입장에서는 더 그렇다.
애드센스 반려 사유에서 자주 보이는 "가치 없는 콘텐츠"가 이런 페이지를 말한다. 아홉 개면 사이트맵 70개 중 9개라 비율도 무시할 수 없었다.
4. 도움말을 붙이는 방법이 세 가지인데, 두 개는 틀렸다
각 게임에 "무엇인지 / 어떻게 노는지 / 어떻게 만들었는지"를 적은 도움말을 붙이기로 했다. 여기서 구현 방식이 갈렸다.
1번은 흔한 선택인데 목적을 못 이룬다. 리액트 상태로 조건부 렌더링하면 닫혀 있는 동안 그 텍스트는 문서에 존재하지 않는다. 도움말을 붙였는데 페이지는 여전히 350자다.
2번은 더 나쁘다. 사용자에게 안 보이는 텍스트를 크롤러에게만 먹이는 형태가 되어, 검색엔진 쪽에서 숨긴 텍스트로 볼 여지가 있다. 얻으려던 것보다 잃을 게 크다.
그래서 <details>를 썼다.
<details className="absolute top-3 right-3">
<summary>도움말</summary>
<div>…무엇인지, 어떻게 노는지, 어떻게 만들었는지…</div>
</details>접힘 영역은 오래된 표준 패턴이고, 검색엔진도 접힌 내용을 정상적으로 색인한다. 자바스크립트가 없어도 열리고, 키보드와 스크린리더 지원이 공짜로 따라온다. 리액트 상태를 하나도 안 늘리고 끝났다.
결과로 페이지당 본문이 350자에서 480~790자가 됐다. 여전히 글이 많은 페이지는 아니지만, 이제 처음 들어온 사람이 뭘 하는 건지 읽을 수 있다. 그게 원래 목적이었고 색인은 부수 효과다.
5. 내비에서 감춘 페이지가 검색에는 열려 있었다
마지막으로 하나 더 찾았다. 아직 다듬는 중이라 내비게이션 목록에서 빼둔 페이지가 여럿 있었는데, 그 페이지들이 사이트맵에는 그대로 들어 있었다.
목록에 없으면서 색인은 되는 페이지는 고아 페이지가 된다. 검색으로 들어온 사람은 그 페이지만 보고, 사이트 안에서 다시 찾아갈 길이 없다. 링크 하나 없이 떠 있는 페이지가 여섯 개였다.
감추기로 마음먹었다면 세 곳을 다 막아야 한다는 걸 이번에 정리했다.
목록에서만 빼고 나머지를 안 막으면, 감췄다고 생각하는 페이지를 스스로 사이트맵에 적어서 제출하고 있는 셈이다.
정리하며
네 가지를 고치고 나서 다시 확인했다. 빌드 결과물에서 data-ad-client가 전부 사라졌고, 게임 페이지에는 읽을 게 생겼고, 감춘 페이지는 사이트맵에서 빠졌다.
돌아보면 세 개는 같은 실수였다. "나중에 채운다"고 두고 배포한 값, "일단 목록에서만 빼자"고 미룬 처리, "도움말은 나중에 쓰자"고 넘긴 설명. 전부 에러를 내지 않아서 잊혔다. 빌드가 통과하는 미완성은 완성으로 착각하기 쉽다.
심사에 통과하든 아니든 사이트는 이 편이 낫다. 광고 태그를 정리한 건 광고 때문이 아니고, 도움말을 붙인 것도 색인 때문이 아니다. 라이브에 나가 있는 걸 실제로 열어서 확인해 본 게 이번에 얻은 것 중 제일 크다.
같이 읽으면 좋은 글
개발 블로그 한 달 운영 회고: 트래픽, 유입 키워드, 안 먹힌 글
블로그를 시작한 지 한 달 만에 글 14편을 썼다. 실제 트래픽, 검색 유입 키워드, 의외로 안 먹힌 글까지 — 숫자로 본 한 달 회고를 솔직하게 정리한다.
Blog회고2026.04.25개발자 사이드 프로젝트 수익화: 애드센스부터 유료 SaaS까지 단계별 정리
사이드 프로젝트로 돈을 버는 5가지 길을 단계별로 정리한다. 애드센스, 유료 멤버십, 스폰서십, 디지털 제품, 유료 SaaS. 각 단계의 기대 수익과 진입 장벽을 솔직하게 비교한다.
수익화사이드프로젝트애드센스2026.04.24