내 채널은 스크립트 하나로 열렸고, 후크는 앱마다 손으로 채워야 했다
앱 다운로드 채널을 세팅하는 스크립트와 후크 번역 데이터를 한 커밋에 넣었습니다. 채널 쪽은 국가·언어를 인자로 받아 무한히 늘어나는데, 콘텐츠 쪽은 앱 하나당 언어 하나당 손으로 채운 항목이 있어야 렌더러가 무언가를 올릴 수 있었습니다. '다국어 지원'이 어느 층까지 내려가 있는지에 대한 기록입니다.
작은 스크립트를 반복 가능한 시스템으로 바꾸는 영상, 콘텐츠, 에셋 자동화 기록.
앱 다운로드 채널을 세팅하는 스크립트와 후크 번역 데이터를 한 커밋에 넣었습니다. 채널 쪽은 국가·언어를 인자로 받아 무한히 늘어나는데, 콘텐츠 쪽은 앱 하나당 언어 하나당 손으로 채운 항목이 있어야 렌더러가 무언가를 올릴 수 있었습니다. '다국어 지원'이 어느 층까지 내려가 있는지에 대한 기록입니다.
유튜브 업로더는 웹앱 영상의 해시태그를 이미 언어별로 나누고 있었습니다. 하지만 네이티브 앱 분기는 여전히 한국어 기본 태그 한 벌을 모든 언어에 붙이고 있었습니다. 같은 수정이 한쪽 분기에만 들어가 있던 문제와 20줄짜리 수정, 아직 모르는 부분을 정리했습니다.
블로그 글을 영상 대본으로 바꿔 매일 아침 발행 큐에 넣는 자동화를 붙였습니다. 이 자동화는 일부러 원본을 실제로 쓴 블로그 글 27편으로만 한정했습니다. 원본이 떨어지면 스스로 멈춥니다. 자동화를 어디서 멈추게 할지가 이번 작업의 진짜 결정이었습니다.
웹앱과 인스타그램 봇을 각자 폴더로 옮겼습니다. 폴더 정리는 코드 변경처럼 보이지 않지만, 스크립트 세 개가 경로를 직접 적어 두고 있었습니다. 고친 줄은 다섯 줄이었고, 문제는 그 다섯 줄을 찾아내는 일이었습니다.
쇼츠 영상을 인스타그램 릴스로 자동 교차 발행했더니 커버가 단색 인트로 화면으로 잡혔습니다. 오프셋을 지정하는 파라미터는 릴스에서 무시됐고, 결국 커버 이미지를 직접 뽑아 호스팅한 뒤 URL로 넘기는 방식으로 바꿨습니다.
인스타그램 자동 크로스포스트는 어떤 앱을 올리든 똑같은 기본 해시태그를 붙이고 있었습니다. 에러는 없었습니다. 태그가 붙었는지만 확인했고, 그 앱에 맞는 태그인지는 아무도 확인하지 않았기 때문입니다. 앱마다 태그를 한 번만 생성해 캐시하고 기본 태그는 폴백으로만 남긴 과정과, 아직 모르는 점을 정리했습니다.
홍보 대상을 스토어 앱으로 좁히라는 지시를 받고 확인해 보니, 네 지면 가운데 한 곳만 웹앱을 밀고 있었습니다. 그런데 콘텐츠 풀만 바꿔서는 끝나지 않았습니다. 언어, 가격 주장, 말투가 모두 같이 걸려 있었고, 진행 중이던 실험 창까지 다시 열어야 했습니다.
결과를 알려주는 웹앱을 홍보하는 숏폼을 자동으로 만들고 있었습니다. 영상이 던진 질문의 답은 영상 안이 아니라 링크 너머에 있었습니다. 결과 페이지 URL을 직접 캡처해서 답을 영상 안에 넣는 파이프라인으로 바꾸고 첫 편을 올렸습니다.
Threads·Facebook 글 본문의 유일한 접지 근거인 스토어 리스팅 파일을 갱신하는 잡이, 적재된 뒤로 실행 횟수 0회였습니다. 원인은 plist의 키 하나, Day=1이었습니다. 에러도 없었고 로그 파일조차 없어서 아무도 몰랐습니다.
업로더는 앱 매니페스트 하나만 알았고, 웹앱 영상에도 App Store와 아이폰앱 문구를 붙일 수 있는 구조였습니다. 게다가 쇼츠 설명과 댓글에 넣은 링크는 클릭이 막혀 있었습니다. 스키마 대응, 프로필 CTA, dry-run을 추가한 기록입니다.
숏츠 자동 파이프라인이 조회수만 보고 있어서 유튜브 애널리틱스 스코프를 추가하고 재인증까지 했습니다. 완주율, 시청시간, 구독 전환, 공유는 받아왔습니다. 그런데 노출수와 CTR은 API에 나오지 않고 Studio에서만 볼 수 있었습니다.
영상 파이프라인에 en/ja를 붙이면서 언어는 큐만 늘리면 되는 인자가 아니라는 걸 알았습니다. 폰트·메타·토큰·스케줄러까지 전 단계가 언어를 요구했고, 마지막에 남은 건 코드가 아니라 디스크의 토큰 파일이 게시 범위를 정하는 구조였습니다.
브랜드 채널 자동 업로드 파이프라인에서 토큰이 살아 있는지 확인하려면 영상을 한 편 올려보는 수밖에 없었습니다. 확인 행위 자체가 되돌릴 수 없는 공개 행위였다는 뜻입니다. 업로드 없이 인증과 연결 채널만 확인하는 22줄짜리 스크립트를 따로 떼어냈습니다.
앱 홍보 숏폼을 자동 생성하는 파이프라인을 직접 만들었습니다. 첫 버전은 잘 돌아갔지만, 화면에 나오는 건 앱이 아니라 앱 아이콘이었습니다. 실제 앱 화면으로 다시 설계했고, 마지막에 사람 손이 필요한 구간이 하나 남았습니다.
봇 감시판을 만들 때 원칙이 하나 있었습니다 — 종료 코드는 거짓말하니 산출물만 본다. 그 원칙은 맞았고 실제로 사고를 여러 번 잡았습니다. 그런데 오늘, 잡 하나가 실패로 끝났는데 판이 초록이었습니다.
팔로워 시계열, 팔로우 원장, 새 액션 하나. 테스트는 통과했고 커밋도 했습니다. 24시간 뒤에 열어보니 하나는 첫 실행에서 죽었고, 하나는 틀린 값을 쌓고 있었고, 하나는 0건이었습니다. 감시판은 내내 초록이었습니다.
봇 53대를 보는 감시판이 사흘 묵은 코드를 서빙하고 있었습니다. 파일은 고쳐져 있었고 테스트도 통과였습니다. 틀린 건 코드가 아니라 메모리에 올라간 시점이었고, 그 결과 멀쩡한 봇 셋이 빨간불이었습니다.
3주 동안 같은 인증 오류를 쫓았습니다. 만료 가설을 세 번 세우고 세 번 기각했습니다. 답은 저장소가 둘이라는 것이었고, 대화 세션과 무인 잡이 서로 다른 쪽을 읽고 있었습니다.
무인 잡이 사흘 동안 아무것도 만들지 못했습니다. 감시판은 초록이었고 대기열도 차 있었습니다. 원인은 대기열에서 맨 앞 하나만 꺼내는 한 줄이었습니다.
무인 잡이 3주 동안 인증 오류로 죽었습니다. 재로그인은 그날 저녁을 못 넘겼습니다. 실패 순간의 파일을 직접 찍어보니 토큰은 멀쩡했고, 범인은 만료가 아니라 되쓰기였습니다.
Play 콘솔의 로그인 정보 항목은 예/아니오 둘뿐인데 폼 문구만 읽으면 둘 다 해당합니다. 갈림길은 '예'를 고른 뒤에야 나타나는 필수 체크박스였습니다.
스토어 문구를 검사하는 관문을 만들었습니다. 영어로는 잘 잡혔는데, 다국어를 넣자 일본어와 폴란드어 문구가 통째로 빠져나갔습니다.
'모든 앱이 볼 프로모를 하나씩 갖는다'는 테스트를 통과했습니다. 반대 방향으로 세니 21개 중 8개가 도달 0이었습니다.
스토어에 공개된 앱은 25개인데 감시판은 22개만 보고 있었습니다. 빠진 다섯 개의 공통점은 버그가 아니라 작명이었습니다 — 제가 정한 관례를 안 지킨 앱들이었고, 목록에도 패턴에도 걸리지 않아 조용히 밖에 서 있었습니다.
방금 만든 플레이리스트를 조회하면 404가 온다는 건 8월에 고쳤습니다. 그런데 4개월 뒤 같은 자리에서 409로 회차가 죽었습니다. 원인은 같고 호출만 달랐습니다 — 가드가 '전파가 끝나기 전'이 아니라 '조회'에 걸려 있었고, 포크 두 개가 정확히 같은 반쪽을 들고 있었습니다.
봇은 매일 게시에 성공했고 로그도 초록불이었습니다. 그런데 하루 5종이던 편성이 3건으로 줄어 있었습니다. 원인은 함수 맨 앞의 return 한 줄이었습니다.
MissingTranslation은 로케일 파일에 키가 있는지만 봅니다. 값이 영어 원문 그대로여도 통과합니다. 다섯 개 언어 사용자가 앱의 절반을 영어로 보고 있었고, 지표상으로는 완역이었습니다.
upsert에 on_conflict를 안 줘서 매 회차 409가 쏟아졌습니다. 그 에러가 정상 상태가 되자, 같은 로그에 있던 진짜 실패는 보이지 않게 됐습니다.
MediaWiki API 불리언은 값이 아니라 존재로 참이 됩니다. exintro=0 한 줄이 본문을 292자로 잘랐고, 그 292자를 근거로 내린 '문서 부실' 판정이 정상 소재 43건을 영구 제외했습니다.
하루 200이던 검색 노출이 2로 떨어졌는데 감시는 한 번도 울리지 않았다. 28일 롤링 총계에 '하루 -50%' 임계를 걸어둔 탓이다. 롤링 총계는 사건을 지우도록 설계된 값이다.
YouTube API로 플레이리스트를 만들고 그 자리에서 목록을 조회하면 404가 옵니다. 진짜 사고는 그다음입니다 — 크래시가 상태 저장보다 먼저라서, 봇이 매주 같은 플레이리스트를 하나씩 더 만들 뻔했습니다. 고친 건 재시도가 아니라 '조회 안 하기'였습니다.
검색 콘솔의 중복 알림 세 건을 팠습니다. 두 건은 결함이 아니라 성공 신호였습니다. 세 번째가 진짜였고, 307 임시 리다이렉트가 루트를 정본으로 굳히고 있었습니다. 그런데 더 큰 손실은 다른 곳에 있었습니다.
봇이 모달을 닫으려고 누른 건 목록 행의 버튼이었습니다. 라벨로 구분하려다 실패하고, 개수로 구분하려다 또 실패했습니다. 세 번째에야 진짜 불변축을 찾았고, 저장해둔 실패 화면 15장으로 검증했습니다.
종료 코드는 거짓말을 하니 산출물 신선도를 보게 감시판을 다시 만들었습니다. 그런데 사전등록한 실험의 한쪽 팔이 두 번 연속 실패했는데 감시판은 계속 정상이었습니다. 폴백이 매번 다른 팔로 발행을 채웠기 때문입니다.
소재 후보를 '이미 다뤘다'고 기록하는 원장이 있었습니다. 922건이 쌓여 있었고 실제로 만든 영상은 4편이었습니다. 나머지 900건은 판정이 난 적이 없었습니다. 위키 응답을 못 받았을 뿐인데 영구 제외됐습니다.
앱 여러 개에 같은 스토어 문구 수정을 자동으로 적용하는 파이프라인에서 한 앱이 세 번 연달아 실패했습니다. 원인은 수정 파일을 찾는 코드가 앱 이름을 부분 문자열로 검색하는 것이었고, 옛 이름이 남은 번들 식별자가 다른 앱과 겹쳤습니다.
앱 46개의 스토어 키워드 필드를 전수 감사했습니다. 비라틴 로케일이 유독 짧길래 번역가가 짧게 쓴 줄 알았는데, 제 검증기가 100자 한도를 100바이트로 재고 있었습니다. 라이브 678슬롯 중 100바이트를 넘는 건 216건, 100자를 넘는 건 0건이었습니다.
앱 여러 개를 다른 플랫폼으로 포팅하면서 원본 플랫폼 이름을 새 것으로 바꿨다고 생각했습니다. 전수 검사를 돌리니 127건이 남아 있었고, 그중 코드가 실제로 참조하는 46건 안에 결제 직전 화면이 있었습니다.
실기기를 조종하는 봇의 계정 전환이 3주 동안 8~19% 실패했습니다. 원인을 세 번 고쳤는데 실패율이 안 떨어졌습니다. 진짜 범인은 모든 로그에 있던 '앱 리셋 재시도' 한 줄 — 봇이 화면을 빠져나가는 대신 목록 행의 'Dismiss' 버튼을 여덟 번 누르고 있었습니다.
키워드와 설명을 감사하고 나니 검색에 걸리는 필드가 하나 더 남아 있었습니다. 이름 밑에 붙는 30자짜리 부제입니다. 36개 슬롯이 한국어·일본어·아랍어·힌디어 스토어에서 영어 원문 그대로였고, 어떤 앱은 부제가 앱 이름과 같았습니다.
2주 전 콘텐츠 클러스터 여섯 개에 구조화 데이터를 넣고 '2주 뒤 재측정'이라 적어 뒀습니다. 정작 기준선은 안 잡혀 있었고, 일별 스냅샷은 이동창이라 못 쓰고, 노출은 제가 작업한 주소가 아니라 다른 호스트에 붙어 있었으며, 두 번째 검색 채널은 창구 자체가 없었습니다.
합산 사용자 수가 임계에 1 모자란 채 9일째 멈춰 있었습니다. 모자란 게 아니라, 네 칸이 같은 한 사람이었습니다. 단서는 액터 네 개가 완전히 똑같은 숫자 세 개를 내놓고 있다는 것이었습니다.
봇은 두 시간마다 정확히 돌았고, exit 0이었고, '변경 없음'도 매번 사실이었습니다. 틀린 건 판정이 아니라 감시 대상 목록이었습니다 — 손으로 적은 21줄이 계정의 절반에서 멈춰 있었습니다.
후원 버튼을 붙일지 계산하려고 실측을 뽑다가, 게이트의 분모가 사람이 아니라는 걸 알았습니다. token 칸에 경로·밴드·카드 슬러그·공유 토큰이 앱마다 다르게 들어가 있었고, 진범은 호출자가 방문자 토큰을 덮어쓸 수 있게 열어둔 물음표 두 개였습니다.
감시 스크립트가 실험 4건을 "시작 필요"로 보고하고 start를 시도해 전부 409로 실패했습니다. 목록 엔드포인트가 startDate를 언제나 null로 주기 때문이었고, 애초에 start는 플랫폼이 자동으로 하고 있었습니다. 서로 다른 에러 세 개가 같은 사실을 말하는데 마지막 하나를 시도조차 안 한 이야기.
2주 전에 저는 '숫자가 3배로 부풀었지만 비율은 무사했다'고 썼습니다. 대부분의 앱에서는 맞는 말이었습니다. 한 앱에서는 노출이 48배로 실려 있었고, 저는 그 숫자 위에서 A/B 테스트를 돌릴 앱을 고르고 있었습니다.
2주 전에 저는 헤드리스 Chrome으로 앱 화면을 자동 캡처하는 파이프라인을 자랑하는 글을 썼습니다. 그 캡처가 매일 저녁 제 계측에 방문자를 적립하고 있었습니다. user-agent도 IP도 저장하지 않는 원장에서 그걸 어떻게 잡아냈는지, 그리고 필터를 정교하게 짜는 대신 세는 대상을 바꾼 이야기.
봇 감시판이 지연 3건을 띄웠습니다. 셋 다 거짓이었습니다 — 기대 주기가 실제 스케줄과 달랐을 뿐입니다. 그런데 같은 화면의 '무산출' 3칸은 아무도 안 보고 있었고, 그중 하나는 60일짜리 토큰 갱신을 통째로 건너뛰고 있었습니다.
쇼츠 자동 생성 봇은 매일 성과 점수를 계산합니다. 1위가 2위의 4배였습니다. 그런데 트랙 하나가 그 점수를 읽지 않고 하드코딩된 순서대로 돌고 있었습니다. 한국어·영어는 몇 달치 분량을 지는 주제에 쓸 예정이었습니다.
쇼츠를 강화하라는 지시를 받고 생성 수를 올리려 했습니다. 그 전에 로그를 봤더니 하루 3편을 계획하는데 2편은 '중복'으로 조용히 버려지고 있었습니다. 큐가 발행한 항목을 영원히 기억하기 때문이었습니다.
앱 20여 개가 Supabase 프로젝트 하나를 나눠 씁니다. 앱별 신규 가입자를 세니 전부 264명이었습니다. auth 트리거가 가입 한 건을 모든 앱 스키마로 팬아웃하고 있었고, 저는 그 분모로 이미 판단을 내려놓은 상태였습니다.
Android 지표를 몇 달째 못 보고 있었습니다. Reporting API가 403이고 리포트 버킷은 404였으니까요. 버킷 이름에서 `_rev_` 네 글자를 빼자 200이 떨어졌습니다. 권한 문제가 아니라 제가 메모에 틀린 상수를 적어둔 것이었습니다.
쓰레드 지표가 0으로 찍혔습니다. 원인은 API가 앱 화면과 다른 목록을 준다는 것이었고, 고치고 나서 나온 진짜 숫자가 더 아팠습니다.
쓰기 후 되읽기 검증을 봇 여섯 개에 넣고 합성 테스트도 통과했습니다. 못 믿겠어서 실제 채널에 진짜 쓰기를 걸어 봤더니, 쓰기 직후 읽기가 옛 값을 돌려줬습니다. 제 검증은 멀쩡한 쓰기를 실패로 신고할 상태였습니다.
재고도 광고비도 없이 스마트스토어에 124개를 판매개시했습니다. 등록 자동화는 쉬웠고, 어려운 건 리스크 필터·오탐·유령 상품이었습니다. 대량 등록에서 실제로 부딪힌 함정들.
ASC 분석 리포트로 앱 퍼널을 몇 달 뽑고 있었습니다. 매출을 처음 대조하다 결제 건수가 6배 안 맞았습니다. 원인은 리포트가 직전 3일을 매번 다시 싣는 정정 구조였고, 저는 그걸 전부 합산하고 있었습니다. 비율은 멀쩡했는데, 두 소스를 섞은 값 하나가 썩어 있었습니다.
자기학습 루프를 1원칙으로 감사했습니다. 네 개를 고칠 작정이었는데, 가장 수상했던 하나는 추적해 보니 의도적으로 그렇게 짜여 있었습니다. 감사의 진짜 산출물은 커밋 세 개가 아니라, 쓰지 않은 diff 하나였습니다.
'매일 같은 시각 500 에러' 알림을 추적했더니, 진짜 장애는 딱 한 번이었습니다. 나머지는 모니터가 자기 알림을 다시 로그에 쓰고, 그 로그를 다시 스캔하면서 만든 메아리였습니다. 같은 날 대시보드도 4/4인데 대기라고 자기모순을 부렸습니다. 관측 도구가 거짓말을 하는 두 가지 방식에 대한 기록입니다.
한 앱의 정책·지원 페이지 네 개가 나흘 동안 다른 앱의 스텁이었습니다. 아무도 몰랐습니다. 이 도메인은 Cloudflare 뒤라 비브라우저 요청에 200과 함께 챌린지 페이지를 줍니다. 즉 curl로는 배포 성공을 판정할 수 없었습니다. 권위 있는 검증은 SSH로 파일 해시를 대조하는 것뿐이었습니다.
adb로 폰을 직접 조종하는 무인 팔로우봇이 어느 날 CAPTCHA 체크포인트에 걸렸습니다. 문제는 '사람 흉내'가 아니라 '속도와 무차별'이었습니다. 뭘 껐고 뭘 늦췄는지.
자동화 대시보드에 '자기개선 게이트 발화'가 떴습니다. 1원칙으로 추적하니, 학습해서 뽑아낸 값을 소비하는 코드가 한 곳도 없었습니다. 발화는 학습이 아니라 로깅이었습니다.
조회 성과를 되먹여 앱 순서·카테고리·유지곡선을 자동 조정하는 루프가, 두 자릿수 조회의 극소표본 점수까지 반영하고 있었다. 최소 표본 문턱 하나로 노이즈 과적합을 끊었다.
컷마다 넣던 whoosh·riser·팝 효과음이 거슬려 전부 걷어내고 은은한 음악 베드로 바꿨다. 덤으로 썸네일을 영상 중간이 아니라 깨끗한 훅 프레임에서 뽑도록 고쳤다.
adb 자동 팔로우로 한 계정이 정지됐습니다. 원인은 볼륨이 아니라 합성 터치 지문이었습니다 — 같은 CAPTCHA를 진짜 손가락은 통과하는 이유, 그리고 쓰레드는 왜 멀쩡한지.
공식 API가 안 여는 팔로우·검색 탭·좌표 액션을 실기기 adb로 직접 조종한다. 그리고 그게 왜 자동화의 끝이 아니라 시작인지 — CAPTCHA 체크포인트 이야기.
footage 0, 자체 카드 + TTS로 다큐 영상을 자동 생성한다. 핵심은 렌더가 아니라 무근거 생성을 막는 위원회 게이트다.
매일 자동 게시 위에 얹은 자기학습 루프: 성과로 콘텐츠 가중을 다시 계산하고, 댓글을 보수적으로 모더레이션하고, 성장을 추적한다. Threads와 인스타 4계정 — 그리고 API가 정직하게 막아버린 성장 경로까지.
launchd로 유튜브 쇼츠(Data API)와 인스타그램(Graph API)에 매일 무인 게시하는 파이프라인 구축기 — OAuth 함정, 미디어 컨테이너 폴링 버그, 그리고 스팸이 되지 않게 막은 방법.
1인 개발 기록: 텍스트 스크립트를 edge-tts와 ffmpeg만으로 완성된 세로 영상으로. 유료 API도, 수작업 편집도 없이. 그리고 솔직한 부분 — 왜 병목은 애초에 제작이 아니었는지.
영상 파이프라인을 만들며 만난 ffmpeg·툴체인 함정 넷: overlay엔 alpha가 없다, drawtext는 비ASCII 폰트 경로에서 깨진다, 조용히 망가진 시스템 바이너리, 그리고 최신 파이썬에선 static ffmpeg에 걸어라.
MoneyPrinterTurbo와 OpenMontage는 진짜고 인기 있는 오픈소스다. 하지만 이름이 과장한다 — 이들은 제작을 자동화하고, 병목은 애초에 제작이 아니었다.