비즈니스 현실10 분 읽기

봇 10개가 다 돌았는데, 앱 방문을 만드는지 아무도 몰랐다

SNS·유튜브 봇을 1원칙으로 점검했더니 대시보드는 조회수로 가득했지만 정작 '앱 방문 하나라도 만드는가'는 아무도 재고 있지 않았습니다. 수신기는 이미 있었고, 빠진 건 계측선 하나였습니다.

#reality-check#analytics#first-principles#social-automation
개념 도식: 왼쪽은 대시보드가 보여주는 28,706 조회·팔로워·초록불, 오른쪽은 아무도 재지 않은 '앱 방문 = ?'
조회수는 쟀다. 클릭은 안 쟀다. 수신기는 이미 있었고, 선 하나가 빠져 있었다.

봇이 열 개 돌고 있었습니다. 유튜브 쇼츠 채널들, 인스타 네 계정, 쓰레드, 무인 팔로우봇. 대시보드를 열면 전부 초록불이었습니다. 조회수 28,706, 팔로워 3,207, 게시 정상. 숫자가 매일 올라갔습니다.

그런데 한 문장을 스스로에게 걸었더니 전부 멈췄습니다. 저는 대시보드를 볼 때마다 "1원칙으로 분석해"를 강제로 겁니다 — 진짜 의미 있는 숫자 하나가 뭐냐, 나머지는 허영 아니냐. 그 질문을 봇들에 던졌습니다.

이 봇들이 존재하는 이유가 뭐지? 앱으로 사람을 보내서 광고 수익을 만드는 것. 그러면 진짜 숫자는 조회수가 아니라 앱 방문입니다. 조회수는 그 앞 단계일 뿐.

그래서 물었습니다. 28,706 조회 중에 앱 방문 하나라도 만든 게 있나?

몰랐습니다. 아무도 재고 있지 않았습니다.

대시보드는 활동을 잰다, 결과가 아니라

당신의 자동화에 물어보세요. 그게 재는 게 활동입니까, 결과입니까?

조회수·팔로워·게시 성공 — 전부 상류 지표입니다. "봇이 뭔가 했다"는 증거지, "그게 목표에 닿았다"는 증거가 아닙니다. 매출 사슬은 이렇게 생겼습니다:

관심(views)  ->  클릭(앱 방문)  ->  전환(수익)

제 대시보드는 맨 왼쪽 칸만 채우고 있었습니다. 가운데 칸 — 관심이 실제로 클릭으로 넘어가는가 — 은 완전히 캄캄했습니다. 조회수 5만짜리 쓰레드 글도 앱으로 한 명도 못 보낼 수 있습니다. 그걸 모르면 조회수는 그냥 예쁜 숫자입니다.

당신이라면?

여기서 손이 근질거립니다. 조회수는 오르니까, 콘텐츠를 더 찍고 채널을 더 열고 싶어집니다. 상류를 더 밀면 되겠지.

멈추세요. 상류는 이미 넘칩니다 — 월 2만 조회는 병목이 아닙니다. 병목은 그 관심이 어디서 새는지 모른다는 것입니다. 봇을 하나 더 만들겠습니까, 아니면 이미 있는 트래픽이 어디로 빠지는지 먼저 보겠습니까?

저는 후자를 골랐고, 계측선을 찾으러 갔습니다. 그리고 두 번 놀랐습니다.

반전 1 — 수신기는 이미 켜져 있었다

UTM 파라미터(utm_source, utm_campaign 같은)를 링크에 붙이면 GA4가 "이 방문은 쓰레드에서 왔다"를 자동으로 분류합니다. 그러려면 앱 쪽에 GA4 스니펫이 있어야 합니다.

앱 코드를 grep 했습니다. GA 측정 ID가 안 나왔습니다. "역시 수신기가 없구나" 했는데 — 틀렸습니다.

측정 ID가 환경변수 뒤에 숨어 있었습니다. 코드는 process.env.NEXT_PUBLIC_GA_ID가 있을 때만 스니펫을 렌더합니다. grep은 하드코딩된 ID를 찾으니 당연히 빈손이었죠. 실제 배포된 페이지를 curl 하니 G-XXXX... gtag가 멀쩡히 렌더되고 있었습니다. 운세 앱 여섯 개가 이미 몇 주째 GA4로 방문을 잡고 있었습니다.

즉 유튜브 쇼츠가 붙여 보내던 UTM은 허공에 뿌려진 게 아니라 이미 집계되고 있었습니다. 저는 그것도 모르고 "측정이 안 된다"고 진단했던 겁니다. 소스 코드의 부재는 기능의 부재가 아닙니다 — 환경변수 뒤를 봐야 합니다.

반전 2 — 진짜 구멍은 딱 하나였다

그럼 뭐가 문제였나. 쇼츠는 완전한 UTM을 보냅니다. 그런데 쓰레드 봇은 링크에 ?src=threads만 붙이고 있었습니다.

GA4는 utm_* 접두사만 캠페인으로 인식합니다. src=같은 커스텀 파라미터는 그냥 무시합니다. 쓰레드에서 온 방문은 전부 "출처 불명"으로 뭉개지고 있었습니다. 조회 8만짜리 채널의 유입이 통째로 안 보였던 겁니다.

고친 코드는 세 줄이었습니다. 링크를 만드는 함수 하나 — 봇의 모든 앱 링크가 이 함수 하나를 거칩니다(단일 초크포인트) — 를 바꿨습니다:

# 전: GA4가 무시함
return f"{url}?src=threads"
 
# 후: host 첫 라벨을 캠페인으로 자동 추출 (unse.ootssu.com -> unse)
host = url.split("://", 1)[-1].split("/", 1)[0]
camp = host.split(".", 1)[0]
return f"{url}?utm_source=threads&utm_medium=social&utm_campaign={camp}"

호출부 여섯 곳을 안 건드렸습니다. 함수 하나가 병목이었으니 함수 하나만 고치면 모든 링크가 따라옵니다. 뿌리에서 한 번 고치면 모든 호출자가 따라오는 그 구조입니다.

자가진단 — 당신의 깔때기는 보이나요?

지금 당신의 파이프라인에 이 셋을 던져보세요.

  1. 목표에서 역산한 진짜 숫자가 대시보드에 있나? 조회수 말고, 그게 만들려는 결과(방문·가입·매출)가 숫자로 찍히나요? 없으면 나머지는 전부 허영 지표입니다.
  2. "수신기가 없다"를 코드 부재로 판단했나? 환경변수·피처플래그 뒤에 이미 켜져 있는지 라이브를 직접 찔러보세요. 소스 grep은 거짓 음성을 냅니다.
  3. 유입 링크가 목적지가 읽을 수 있는 언어로 태깅됐나? src=, ref= 같은 자작 파라미터는 대부분의 분석 도구가 무시합니다. 표준(UTM)을 쓰세요.

솔직한 부분

계측선을 이었다고 매출이 생긴 건 아닙니다. 지금 데이터는 0입니다. GA4 리포트는 실제 클릭이 몇 개 들어와야 채워지고, 그건 앞으로 3~7일을 기다려야 나옵니다.

이 글은 "문제를 풀었다"가 아닙니다. 몇 주 동안 못 풀고 있던 이유가, 풀 대상을 볼 수조차 없었기 때문이었다는 이야기입니다. 측정이 답을 준 게 아니라, 이제서야 질문을 던질 수 있게 됐을 뿐입니다 — 어느 봇이 진짜로 사람을 앱에 보내나. 표본이 쌓이기 전에 결론 내지 않는 것이 다음 규율이고요.

그래서 지금 제가 미는 건 콘텐츠도 채널도 아닙니다. 아무것도 안 만들고 며칠 기다려 그 숫자를 읽는 것. 그게 지금 가장 값진 다음 행동입니다. 봇을 정직하게 감사하면 대부분은 죽여야 할 것으로 드러나는데, 죽일지 살릴지도 저 숫자가 정합니다.

당신의 자동화는 활동을 재고 있나요, 결과를 재고 있나요? 오늘 딱 하나, 가장 바쁜 봇의 링크가 목적지에서 제대로 태깅돼 도착하는지 curl 한 번 찔러보세요. 아마 저처럼 놀랄 겁니다.

관련 글