개인 서비스에 광고를 붙이기로 했다. 문제는 "어디에 몇 개를 붙일까"였다.
감으로 정하면 두 가지가 다 망가진다. 수익이 안 나거나, UX가 깎이거나. 우리 사이트는 유저가 아이템을 찾아 여러 페이지를 돌아다니는 룩업 서비스라 흐름이 끊기면 손해가 더 크다. 그래서 측정부터 하기로 했다.
1. 경쟁사가 실제로 어디에 붙였는지 실측
감이 아니라 사례가 필요했다. Playwright로 국내 대형 커뮤니티·게임 사이트를 자동 순회하면서, 페이지에 실제로 박혀 있는 광고 슬롯을 긁는 스크립트를 만들었다. 대상은 디시인사이드, 나무위키, 아카라이브, 에펨코리아, 그리고 비교군으로 우리 사이트였다.
슬롯마다 이런 걸 수집한다.
- 위치와 크기 (
x,y,w,h) — 화면 어디에, 몇 픽셀짜리인가
sticky여부 — 스크롤을 따라오는가
- 본문과의 거리 — 첫 콘텐츠보다 위인가 아래인가
- 애드테크 스택 — GAM / AdSense / AdFit / Prebid / Amazon TAM / Taboola / CMP 탐지
결과는 사이트별 JSON으로 떨어진다. 디시인사이드는 이렇게 나왔다 — 헤더에 300×100 하나, 애드테크는 카카오 AdFit 단독.
{ "site": "...", "viewport": "1920x1080", "docH": 2722, "slots": [ { "region": "header", "size": "mobile-3to1", "w": 300, "h": 100, "x": 460, "y": 5, "sticky": false, "annoy": "안거슬림(자연갭)" } ], "adtech": { "gam": false, "adsense": false, "adfit": true, "prebid": false } }
여기서 방법론 하나를 배웠다. DOM 관측만으로 판단하면 안 된다. 광고는 세션·지역·유저마다 다르게 뜨기 때문에, 한 번 열어본 화면은 표본 1개일 뿐이다.
누가 광고를 태워주는지 확인하는 권위 있는 소스는ads.txt다. 특히MANAGERDOMAIN한 줄이 그 사이트의 광고 관리 주체를 그대로 말해준다.
그래서 DOM 실측과
ads.txt 파싱을 같이 돌렸다. 33개 사이트를 훑으니 국내 시장 구도가 선명해졌다. AnyManager(디시인사이드·인벤), rev.iq(OP.GG·루리웹), Venatus(DAK.GG·로아와·디스이즈게임) — 세 관리 플랫폼이 갈라 먹고 있었고, 화면만 봐서는 절대 안 보이는 정보였다.2. 우리 레이아웃이 광고를 받을 수 있는지 측정
남의 배치를 그대로 베낄 수는 없다. 우리 레이아웃이 그 슬롯을 물리적으로 담을 수 있어야 한다. 그래서 이번엔 우리 페이지를 뷰포트별로 측정했다.
{ "아이템상세": { "1920": { "colW": 1632, "gutter": 144, "fits160": false, "fits300": false } }, "도감목록": { "1440": { "colW": 860, "gutter": 290, "fits160": true, "fits300": false } } }
fits160 / fits300은 "그 폭의 사이드 광고가 들어갈 자리가 있는가"다. 숫자로 보니 판단이 바로 나왔다.- 아이템 상세는 1920에서도 콘텐츠가 1632px을 먹어서 거터가 144px밖에 안 남는다 → 사이드 레일 불가
- 도감 목록은 860px 고정 컬럼이라 1440에서 거터가 290px → 160×600은 되지만 300×250은 안 됨
- 몹 상세는 거터가 81px까지 좁아진다 → 외곽 레일 포기. 대신 좌측 설명글 아래에 625px 빈 공간이 있어서 여기에 300×600을 넣을 수 있다
- 맵 상세는 거터 309px + 문서 높이 2225px → 스크롤이 길어서 sticky 레일이 유효
"빈 공간이 있어 보이니까 여기 넣자"가 아니라, 측정값이 배치를 결정하게 만든 게 이 단계의 핵심이다.
3. 블루프린트로 합의
측정 결과를 페이지별 배치도로 그렸다. 현행 슬롯은 회색 점선, 신규 제안은 색상 점선으로 구분하고, 근거가 된 치수를 그대로 적어뒀다.

이 그림 한 장이 있으니 "왜 여기에?"라는 질문에 매번 숫자로 답할 수 있게 됐다. 그리고 반응형 규칙도 여기서 확정했다 — 모바일에서는 좌측·거터 레일을 자동 제외하고 본문 끝 배너만 남기고, 태블릿은 페이지에 따라 다르게.
4. 목업으로 눈으로 확인
배치도는 도형이라 실제 느낌을 못 준다. 그래서 실제 페이지 스크린샷 위에 슬롯을 오버레이하는 스크립트를 만들어, 6개 페이지 × PC·태블릿·모바일 = 18장을 한 번에 뽑았다.

여기서 광고를 실제로 붙이기 전에 잡은 게 있다. 배너가 히어로 이미지 바로 위에 오니까 첫 화면이 광고로 시작하는 느낌이 났다. 코드 한 줄 안 건드리고 위치를 내렸다.
모바일은 특히 목업이 유용했다. 좁은 폭에서는 레일이 전부 빠지고 본문 아래 배너 하나만 남는데, 그게 실제로 어떻게 보이는지 미리 확인할 수 있었다.

5. 붙이고 나서 다시 실측
여기서 끝냈으면 버그를 못 잡았을 것이다. 실제 적용 후 다시 캡처해서 보니, 모바일에서 728px짜리 배너가 화면 밖으로 잘려나가고 있었다.
원인은 데스크톱/모바일 판별을 JS 훅으로 하고 있었던 것. 서버 렌더 시점에는 화면 크기를 알 수 없어서 훅이 일단 "데스크톱"으로 스냅샷을 찍고, 그 상태로 728 배너가 박힌 뒤 모바일에서 잘렸다.
레퍼런스를 다시 뒤졌더니 답이 있었다. 메이플랜드지지나 나무위키처럼 규모 있는 사이트는 GAM의 size mapping을 쓰고, AdFit처럼 그게 없는 경우엔 CSS 브레이크포인트로 슬롯 자체를 토글하고 있었다. 후자가 우리 상황에 맞아서 훅을 걷어내고 CSS로 바꿨다.
화면 크기에 따라 달라지는 것을 JS 상태로 관리하면, 서버 렌더 시점에 반드시 한 번은 틀린다.
배운 것
- 감으로 정하지 말고 측정한다. 거터 144px이라는 숫자 하나가 "여기는 레일 불가"를 즉시 결정해줬다
- DOM 관측은 표본 1개다. 광고처럼 매번 달라지는 대상은
ads.txt같은 선언적 소스를 같이 봐야 한다
- 목업은 코드보다 싸다. 18장 뽑는 데 스크립트 한 번인데, 붙이고 나서 고치는 것보다 훨씬 저렴했다
- 붙인 뒤에도 실측한다. 728 잘림은 적용 후 캡처에서만 보였다
결국 이 작업에서 제일 오래 걸린 건 광고 코드를 넣는 일이 아니라, 어디에 넣을지 정하는 근거를 만드는 일이었다. 그리고 그게 맞았다고 생각한다.
