AI한테 광고 예산을 맡겨도 될까? - 난테 스튜디오 아키텍처 5편
광고 예산은 매시간 도는 Worker가 움직입니다. 한 번에 69%를 깎았다가 캠페인이 멈춘 뒤로, 제안은 AI가 하고 한도는 코드가 거는 구조로 바꿨습니다.
난테 스튜디오는 앱을 내면 Google 앱 캠페인으로 사용자를 데려옵니다. 앱이 많다 보니 캠페인도 많고, 어느 캠페인의 예산을 올리고 내릴지 사람이 매일 판단하기는 어렵습니다. 그래서 예산은 매시간 도는 Worker가 움직입니다.
그런데 8월에 두 번, 예산을 한 번에 크게 줄였다가 캠페인이 거의 멈췄습니다. 8월 9일에는 이 Worker가 34%를, 8월 19일에는 일회성 CLI 명령이 69%를 한 번에 깎았는데요. 두 번째 캠페인은 줄어든 예산조차 2%밖에 쓰지 못했습니다. 예산이 모자라서가 아니라, 급격한 변화가 입찰을 흔들어 놓았기 때문이에요.
지금 원칙은 한 줄입니다. AI는 제안하고, 코드가 강제합니다. 에이전트든 크론이든 무엇을 제안하든, 예산을 실제로 바꾸는 한 곳에서 코드가 한도를 겁니다. 이번 글에서는 이 Worker가 매시간 하는 일과 예산을 줄이고 올리는 한도, 그리고 광고를 켜는 결정을 누가 하는지 공유하려고 합니다. 금액은 빼고 비율로만 적었습니다.
매시간 무슨 일이 일어날까
예산 Worker는 매시간 한 번 깨어나서 정해진 순서대로 14단계를 실행합니다. 순서는 코드에 목록으로 박혀 있고, 단계마다 결과가 기록됩니다.
그림 1. 매시간 실행되는 14단계입니다. 단계마다 측정, 판단, 실행 중 무엇을 하는지 표시했습니다.
14단계는 하는 일에 따라 세 종류로 나뉩니다.
- 측정: 앱 목록을 맞추고, 캠페인별 설치와 광고 수익을 모읍니다.
- 판단: 앱이 지금 어느 단계에 있는지, 캠페인마다 예산을 얼마로 가져갈지 정합니다.
- 실행: 정해진 한도 안에서 예산과 입찰을 바꾸고, 바꾼 내용을 기록합니다.
측정 단계가 판단 단계보다 앞에 있는 데는 이유가 있습니다. 판단은 측정한 숫자로만 합니다. 광고 수익을 아직 못 읽은 앱은 "수익 0"이 아니라 "모름"으로 처리하고, 모르는 상태에서는 예산을 올리지 않습니다.
예산은 얼마나 줄일 수 있을까
Google은 앱 캠페인 가이드에서 "Try not to make drastic changes to your campaign"이라고 말합니다. 급격한 변화의 예로 예산을 20% 넘게 바꾸는 경우를 들고요. 예산 같은 큰 변화는 학습 단계를 다시 시작하게 하고, 안정되기까지 최소 7일에서 14일을 기다리라고도 적혀 있습니다.
그래서 예산을 줄일 때는 한 번에 20%를 넘지 않습니다. 더 줄여야 하면 다음 날 또 한 단계 줄입니다. 그리고 어떤 경우에도 캠페인이 멈추지 않을 최소 예산 아래로는 내리지 않습니다.
8월 9일 사고 뒤에 처음 고친 곳은 Worker의 예산 계획 코드였습니다. 그런데 열흘 뒤 두 번째 사고는 계획 코드가 아니라 CLI 명령에서 났어요. 예산을 바꾸는 길이 여러 개였는데, 한도는 그중 하나에만 걸려 있었던 거죠.
그림 2. 예산을 바꾸려는 쪽은 여럿이지만, 실제로 Google Ads에 쓰는 함수는 하나입니다. 한도는 그 함수에 겁니다.
지금은 한도를 예산을 실제로 쓰는 함수 하나에 겁니다. Worker의 계획 코드든, CLI든, 나중에 누가 새로 만든 코드든 이 함수를 지나야 예산이 바뀝니다. CLI 쪽에도 같은 규칙이 Rust로 들어 있고요. 새 코드가 생겨도 한도를 따로 챙길 필요가 없습니다.
예외도 하나 있습니다. 만든 지 얼마 안 된 캠페인이나 꺼져 있던 캠페인은 어차피 학습 중이라, 지켜야 할 학습이 없습니다. 이런 캠페인은 20% 단계를 거치지 않고 목표 예산으로 바로 바꿉니다. 단계를 나누면 쓸데없는 지출만 길어지니까요.
예산은 얼마나 올릴 수 있을까
줄이는 쪽과 달리 올리는 쪽에는 고정된 상한이 없습니다. 원래는 있었는데요. 9월에 상한 숫자 하나가 가장 잘되던 앱을 8일 동안 붙잡아 둔 걸 보고 지웠습니다. 그 앱은 그동안 광고비보다 많이 벌고 있었습니다. 시장이 아니라 저희가 정한 숫자가 성장을 막은 거예요.
대신 올리는 쪽은 근거가 있어야 움직입니다.
- 올릴 때도 캠페인당 하루에 한 번, 20% 한 단계씩만 올립니다.
- 올린 금액은 스튜디오 전체 월 손실 한도에서 먼저 빼 둡니다.
- 지금 예산도 다 못 쓰는 캠페인은 올리지 않습니다.
- 광고로 들어온 사용자가 버는 광고 수익이 기준보다 낮으면 올리지 않습니다.
- 앱이 손익분기 아래로 내려가는 날, 목표 예산이 멈추고 줄이는 규칙이 이어받습니다.
오르는 걸 멈추고 싶을 때 상한을 다시 넣지 않는 것도 규칙입니다. 멈춰야 한다면 측정이 틀렸거나 판단 기준이 틀린 것이니, 그쪽을 고칩니다.
광고는 누가 켤까
새 앱의 첫 캠페인은 세션이 만들어 두되 꺼 둔 상태로 둡니다. 광고 문구는 사용자가 실제로 쓴 표현에서 가져오고, 출처를 함께 기록해야 만들 수 있어요. 그런데 한때 첫 캠페인을 켜는 일이 대화창의 "켜도 돼요"를 기다렸습니다.
그러다 한 앱이 스토어에 공개된 지 사흘이 지나도록 광고가 꺼진 채로 있었습니다. 사용자는 0명이었고요. 아무도 그 질문을 받은 적이 없었기 때문입니다. 병호 님의 결정은 간단했습니다. 앱이 출시됐는지는 크론이 확인하고, 광고도 크론이 켠다.
그림 3. 첫 캠페인을 켜기 전에 확인하는 조건입니다. 하나라도 "아니요"거나 "모름"이면 켜지 않습니다.
지금은 매시간 Worker가 조건을 하나씩 확인합니다. 스토어 페이지 공개, 광고 소재 승인, 설치 전환 연결, 광고 수익 준비, 시험 운영 예산 한도를 봅니다. 모든 답이 "예"일 때만 켭니다. "모름"은 "예"가 아닙니다. 한 번에 앱당 캠페인 하나만 켜고, 켤 수 없는 앱은 막힌 조건과 함께 아침 보고서에 올라갑니다.
숫자와 기록은 어디에 둘까
한도를 정하는 숫자는 JSON 파일 하나에 모아 둡니다. 지금 90개가 있어요. Worker의 TypeScript 코드와 CLI의 Rust 코드가 같은 파일을 읽습니다. 같은 숫자가 다른 곳에 한 번 더 적히면 린트가 푸시를 막고요. 예산 규칙 파일마다 그 규칙을 실행하는 단계가 있는지도 린트가 확인합니다. 아무 단계에도 속하지 않은 규칙은 있어도 실행되지 않으니까요.
예산을 바꿀 때마다 여섯 가지를 기록합니다. 누가, 무엇을, 왜, 언제, 어디에, 어떻게 바꿨는지예요. 에이전트가 바꿨다면 어느 세션인지까지 남깁니다. 대화 기록은 기록으로 치지 않습니다. 대화는 세션이 끝나면 찾기 어렵기 때문입니다. CLI에서 캠페인 하나의 기록을 날짜순으로 다시 볼 수 있습니다.
자동화한 시스템이 돈을 쓰게 한다면
먼저, 돈이 나가는 길을 전부 세어 보세요. 한도를 계획 코드에만 걸면, 그 계획을 거치지 않는 길에서 사고가 납니다. 실제로 쓰는 함수 하나에 걸어야 합니다.
다음으로, 줄이는 한도와 올리는 한도를 따로 생각해 보면 좋습니다. 저희는 줄이는 쪽은 숫자로 막고, 올리는 쪽은 근거로 막습니다. 둘을 같은 방식으로 막으면 잘되는 캠페인까지 붙잡게 됩니다.
마지막으로, "모름"을 "예"로 읽는 곳이 없는지 확인하세요. 측정이 빠진 상태에서 자동화가 움직이면, 그 결정은 근거 없이 내려진 것입니다.
마치며
돌아보면 사고는 판단이 틀려서보다 판단을 실행하는 길이 여러 개라서 났습니다. 같은 규칙을 모든 길에 붙이는 대신, 모든 길이 지나가는 한 점을 찾는 데 시간이 가장 많이 들었습니다.
AI한테 예산을 맡겨도 되느냐는 질문에는 지금 이렇게 답할 수 있습니다. 판단은 맡겨도 됩니다. 다만 한도는 AI가 읽는 문서가 아니라, 돈이 나가는 코드에 있어야 합니다.
다음 글에서는 사람이 모든 코드를 리뷰할 수 없을 때 품질을 어떻게 지키는지 다룹니다. 자동화로 광고를 운영하고 있다면 조금이나마 도움이 되었으면 합니다.
난테 스튜디오 아키텍처 시리즈의 5편입니다. 전체 목록은 1편에 있습니다.