Installs into .claude/skills of the current project.
Sponsors
SSkills DirectorySkills Directory
Is that skill safe to install?
SSkills DirectorySkills Directory
Know which skills are safe — weekly.
Are you the author of Adrevenue?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/snwlee-adrevenue)
---
name: adrevenue
description: 광고 수익 레버 조언 — 퍼널(요청→채움→노출→수익) 병목 판정, 믹스 역전, 레버 우선순위. 게이트가 아니라 조언이다. 트리거 "eCPM", "RPM", "매치율", "show rate", "광고 수익", "수익이 안 나", "퍼널".
---
# adrevenue
nereus:common 규칙을 따른다. 담당 에이전트: ads-engineer.
**조언자다. 게이트가 아니다.** `violations` 를 내지 않고 `levers` 와 `unanswerable` 을 낸다.
어디에 힘을 쓸지는 **사업 판단**이고, 하네스가 강제하면 하네스가 사업을 결정하게 된다.
(`nereus-game:track` 과 같은 규율이다.)
## 0. 퍼널을 뭉치지 않는다
```
requests ──matchRate──▶ matched ──showRate──▶ impressions ──▶ revenue
```
`Ruling: 퍼널 단계를 하나의 효율 지표로 뭉치지 않는다 — 고칠 곳이 뒤집힌다.`
- **matchRate 가 낮다** → 재고·미디에이션·타게팅 신호 문제다. 지면을 늘려도 보여줄 것이 없다.
- **showRate 가 낮다** → 지면·동기·원격 설정 문제다. 채움은 멀쩡한데 보여주질 않는 것이다.
도너 실측에서 리워드는 **채움은 높고 노출은 낮았다**. 하나의 "효율" 지표로 뭉쳤다면
채움 쪽을 고쳤을 것이고, 아무것도 안 바뀌었을 것이다. 실제 수치는 여기 적지 않는다 —
계정·시점 종속이라 빨리 낡고, 낡은 수치가 틀린 확신을 만든다.
## 1. 입력
```json
{
"formats": [
{ "format": "rewarded", "requests": 0, "matched": 0, "impressions": 0, "revenue": 0, "clicks": 0 },
{ "format": "fixedBanner", "requests": 0, "matched": 0, "impressions": 0, "revenue": 0 }
],
"targets": { "matchRate": 0.8, "showRate": 0.3, "maxCtr": 0.1 }
}
```
`targets` 는 **우리가 정하는 기준**이다. 없으면 **병목을 판정하지 않는다** —
기본값을 지어내면 그럴듯하게 틀린다. 판정을 안 했다는 사실은 `unanswerable` 에 남는다.
```bash
echo '{"formats":[…],"targets":{…}}' | node "${CLAUDE_PLUGIN_ROOT}/lib/ad-funnel.mjs"
```
출력: `{ rates, bottleneck, levers, unanswerable }`
## 2. 레버 순서 — 위에서부터 본다
순서가 곧 방법론이다. 아래를 먼저 손대면 위가 그대로라 효과가 상쇄된다.
### A. show rate
**가장 먼저다.** 채워 놓고 안 보여준 노출은 이미 값을 치른 재고다.
지면을 늘리거나(=`adplacement` 의 축) 리워드 동기를 명확히 하거나
원격 설정으로 꺼둔 지면이 없는지 본다.
`bottleneck[format] === "show"` 로 나온다.
### B. 믹스 역전
노출 점유 > 수익 점유인 포맷. **기준 없이도 판정된다** — 점유율끼리의 비교라
외부 기준이 필요 없다. 포맷이 하나면 점유율이 1 이라 의미가 없어 판정하지 않는다.
`levers` 에 `mix-inversion` 으로 나온다. 처방은 **재배분이지 제거가 아니다** —
같은 노출 1건의 가치가 포맷마다 다르다는 신호다.
### C. 고RPM 편중
같은 노출을 단가가 높은 쪽으로 옮긴다. 지역·포맷·미디에이션 순위가 축이다.
**여기서 실측 RPM 을 하네스에 적지 않는다.** 프로젝트의 보고서에서 그때 읽는다.
### D. 집중도
소수 지면·소수 사용자에 노출이 몰려 있으면 그 지면이 죽을 때 전부 같이 죽는다.
분산은 수익 증가가 아니라 **분산 자체가 가치**인 레버다.
### E. 트래픽 방어
**A~D 의 모든 증분은 현재 matched 를 가정한다.** 트래픽이 빠지면 개선분이 그대로 상쇄된다.
그래서 `analyzeFunnel` 은 targets 유무와 무관하게 "트래픽 자체가 유지되는가"를
**항상** `unanswerable` 에 싣는다.
## 3. 답할 수 없는 것
`Ruling: 답할 수 없는 것을 결과에 싣는다 — 검사기가 다 본다고 믿게 만드는 것이 더 나쁘다.`
`unanswerable[]` 은 `{ question, needs }` 다. **질문 자체에 무엇이 없어서 판정하지 않았는지를 적는다** —
`needs` 에만 적으면 읽는 사람이 질문만 보고는 무엇이 빠졌는지 모른다.
| 질문 | 왜 광고 리포트로는 못 푸는가 |
|---|---|
| show rate 가 왜 낮은가 | 지면 노출 빈도·리워드 동기·원격 설정 상태는 앱 내 이벤트 로그에만 있다 |
| 높은 CTR 이 실제 오클릭인가 | AdMob **정책 리포트**와 무효 트래픽 공제 데이터가 따로 필요하다 |
| 트래픽 자체가 유지되는가 | 기간별 matched 추이와 스토어 콘솔 데이터 |
| 기준이 없어 비율을 판정하지 않았다 | `targets` 가 비어 있다 |
## 4. 하지 말 것
- **실측 eCPM·RPM·show rate 실제값과 퍼블리셔 ID 를 하네스에 적지 않는다.**
- **기준 없이 병목을 단정하지 않는다.** 지어낸 기본값은 그럴듯하게 틀린다.
- **레버를 `violations` 로 내지 않는다.** 정지 위험과 수익 기회는 무게가 다르다.
- **요청이 0 일 때 비율을 0 으로 내지 않는다.** `null` 이다 — 나눗셈을 하지 않은 것과
나눠서 0 이 나온 것은 다르다.
- **CTR 이 높다고 오클릭이라 판정하지 않는다.** 우리가 가진 데이터로는 구분되지 않는다.