path
Jin's Lab
코드로 실험하고 기록하는 개발 일지 · J.Lab

Jin's Lab

코드로 실험하고 기록하는 개발 일지. 커리어와 삶의 방향을 탐색하며 기록하는 공간입니다

automation

GAS 트리거 간격 설계로 하루 발행량을 4배 늘린 비결

J.Lab ·
목차
    최근 블로그 이사 작업을 진행하면서 수백 개의 글을 자동으로 마이그레이션하는 작업을 진행하고 있었다. 내가 작성한 스크립트인 Code_ITStory_Migration.gs 파일에는 setMigrationTriggers라는 함수가 있었는데, 기존에는 하루에 딱 세 번, 즉 1시, 13시, 15시에 트리거가 작동하도록 설정해 두었다. 하지만 이 속도로는 전체 데이터를 마이그레이션하는 데 무려 57일이나 걸린다는 계산이 나왔다. 너무 느리다는 생각에 마음이 조급해졌다. 그래서 하루 발행량을 획기적으로 올리기 위해, 0시부터 22시까지 2시간 간격으로 하루 총 12번 실행되도록 트리거 주기를 변경하기로 결심했다. 계산대로라면 완료 예상 기간이 15일로 대폭 단축되는 마법 같은 계획이었다.

    첫 번째 삽질: 1시간 간격 설정과 원인 불명의 스킵 현상

    처음에는 더 욕심을 내서 1시간 간격으로 트리거를 촘촘하게 배치해 보았다. 그러나 실행 결과를 확인해 보니 이상하게도 발행된 글의 개수가 기대했던 것의 절반밖에 되지 않았다. 무엇이 문제인지 로그를 뜯어보니, 내가 runMigration 함수 내부에 걸어둔 안전장치가 발목을 잡고 있었다. 마지막 글 발행 이후 1시간이 지나지 않았다면 중복 발행이나 스팸 방지를 위해 실행을 건너뛰도록 만든 '마지막 발행 후 1시간 미만이면 스킵' 가드 로직이 문제였다.

    분명히 정각 단위로 1시간 간격 트리거를 설정해 두었는데 왜 가드 로직에 걸려 스킵이 발생한 것일까? 원인은 구글 앱스 스크립트(GAS)의 시간기반 트리거 작동 방식에 있었다. GAS에서 제공하는 시간기반 트리거 API는 특정 시간(atHour)만 지정할 수 있을 뿐, 구체적인 분(minute) 단위 제어가 불가능하다. 즉, 실행 분은 구글 서버의 부하 분산을 위해 시스템이 무작위로 결정하는 구조다.

    자동화와 스케줄링을 상징하는 기어와 디지털 시계가 맞물려 돌아가는 아이소메트릭 일러스트

    이로 인해 2시 트리거는 2시 58분에 실행되고, 바로 다음인 3시 트리거는 3시 2분에 실행되는 기현상이 발생할 수 있었다. 두 실행 사이의 실제 물리적 시간 간격이 고작 4분밖에 되지 않았던 것이다. 결국 내 스크립트에 들어 있던 1시간 가드 조건에 걸려 3시 트리거는 아무 작업도 하지 않고 스킵 처리되는 상황이 반복되었다. 시간기반 트리거의 이 무작위 분 실행 특성을 간과한 결과였다.

    해결책: 최소 간격 가드를 고려한 GAS 트리거 간격 설계

    문제를 해결하기 위해서는 내부의 최소 간격 가드 로직과 외부의 GAS 트리거 간격 설계를 입체적으로 조율해야 했다. 무작위 분 제어가 안 된다면, 트리거 간격을 충분히 벌려서 어떤 경우에도 가드 시간인 60분을 침범하지 않도록 안전 마진을 확보하는 방법이 최선이었다. 그래서 트리거를 무리하게 1시간 간격으로 배치하는 대신, 2시간 간격으로 다시 조정했다.

    트리거 간격을 2시간으로 설계하면 수학적으로 완벽한 안전 영역이 확보된다. 예를 들어 이전 트리거가 가장 늦은 시간인 0시 59분에 실행되고, 다음 트리거가 가장 빠른 시간인 2시 00분에 실행되더라도 최소 실행 간격은 61분이 보장된다. 이는 내 가드 조건인 60분을 아슬아슬하게 넘어서는 완벽한 안전거리였다.

    2시간 간격의 타임라인 마커와 정밀한 스케줄 관리를 시각화한 3D 아이소메트릭 그래픽

    이렇게 GAS 트리거 간격 설계를 바꾼 후, 스크립트는 단 한 번의 가드 충돌도 없이 안정적으로 돌아가기 시작했다. 하루에 설정한 12건의 트리거가 누수 없이 100% 제 역할을 다하며 글을 성공적으로 발행했다. 덕분에 마이그레이션 완료 기간도 계획했던 대로 15일로 크게 줄일 수 있었으며, 자동화 스크립트의 실행 신뢰성은 최고 수준으로 올라갔다.

    추가적인 수확: 이미지 생성 쿼터 분리와 진짜 병목 찾기

    이 과정에서 마이그레이션 효율을 극대화하기 위해 몇 가지 추가 조치를 함께 취했다. 먼저 마이그레이션에 필요한 이미지 생성에 무료 API인 Pollinations를 적극 활용했다. 기존의 Gemini나 구글 자체 이미지 쿼터와 완벽하게 분리해 둔 덕분에, 발행량을 대폭 늘려도 내가 운영 중인 다른 자동화 블로그의 AI 할당량을 잠식하지 않는 독립적인 구조를 완성할 수 있었다.

    또한 작업을 이어가며 깨달은 진짜 병목은 따로 있었다. 바로 구글 계정 단위로 공유되는 GAS 트리거의 하루 최대 90분 런타임 제약이었다. 간혹 마이그레이션 도중 429 에러(Too Many Requests) 같은 일시적 제한을 만나면 스크립트가 대기(sleep)하며 재시도를 수행하는데, 이 대기 시간마저도 90분 런타임 한도에 그대로 누적되어 소모된다.

    클라우드 서비스와 API 통합을 통한 데이터 흐름을 시각화한 미니멀리스트 벡터 일러스트

    한 번 실행할 때 적용되는 6분 실행제한뿐만 아니라, 하루에 쓸 수 있는 전체 누적 시간 자체가 매우 귀하다는 사실을 뼈저리게 실감했다. 결국 단순한 실행 빈도 증가보다 대기 시간을 최소화하는 효율적인 네트워크 요청 처리가 안정적인 자동화의 핵심이라는 인사이트를 얻었다. 이번 삽질을 통해 분 단위 제어가 안 되는 구글 트리거의 한계를 극복하고 나만의 견고한 마이그레이션 시스템을 구축하게 되어 매우 뿌듯하다.

    automation blog-migration gas-optimization gas-trigger google-apps-script web-scraping
    ← 이전 글
    최근 게시물
    다음 글 →
    이전 게시물
    ≡ 글 목록으로
    소개
    path
    J.Lab

    커리어와 삶의 방향을 탐색하며
    기록하는 공간입니다

    카테고리
    403 Forbidden Error 1 AdSense 1 AdSense Bot Detection 1 AI Automation 1 AI Image Generation 1 ai-automation 2 AI-Prompting 1 AIBlogAutomation 1 AICodeLog 1 AIContentGeneration 1 AIImageGeneration 1 AIWritingErrors 1 API Automation 2 API Retry Design 1 api-automation 1 API-Deprecation 1 api-error 1 api-fallback 1 api-retry-strategy 1 api-troubleshooting 1 APIDebugging 1 APIError 1 APIErrorHandling 1 APIIntegration 1 APITokenRenewal 1 AppsScript 1 appsScriptAutomation 1 AutomatedBlogging 1 automation 1 Automation 2 AutomationTroubleshooting 1 Backoff Algorithm 1 Base64Encoding 1 BatchProcessing 1 blog-automation 1 blog-customization 1 blog-migration 3 Blog-Sorting 1 blog-strategy 1 blog-theme-customization 1 blog-workflow 1 blogautomation 1 BlogAutomation 2 Blogger 4 Blogger API 2 Blogger Automation 1 Blogger SEO 1 blogger-api 3 blogger-automation 1 blogger-error-400 1 blogger-labels 1 blogger-seo 1 blogger-tips 1 Blogger-Tips 1 BloggerAPI 3 BloggerBypath 1 BloggerCustomization 2 BloggerFavicon 1 BloggerTemplate 1 BloggerTemplateEditing 1 BloggerTheme 1 BloggerThemeCustomization 1 BloggerXMLStructure 1 Blogging-Automation 1 BloggingTools 1 BlogQuality 1 BlogSpot 1 BWidgetBIncludable 1 CardClickHandler 1 CDATA 1 clamping-text 1 Code Integration 1 codelog 1 CodeLog 1 codeRefactoring 1 contentsequencing 1 cost-reduction 1 CSSOverlay 1 data-parsing 1 DatabaseDesign 1 DataRecovery 1 DateInjection 1 Debugging 2 dev-journey 1 DeveloperJourney 2 DevJourney 1 display-none-seo 1 dom-manipulation 1 dynamic-menu 1 Error-Handling 1 ErrorHandling 1 Execution Timeout 1 ExecutionLimit 1 exponential-backoff 1 FactChecking 1 FaviconEmbedding 1 Feed-API 1 filesystem 1 Flat Illustration 1 FluxModel 1 FreeAIImage 1 FrontEndDev 1 FrontendTips 1 GAS Blogger API 1 gas-automation 1 gas-optimization 1 gas-trigger 1 GASTimeout 1 gasTriggerManagement 1 GCP Project Linkage 1 Gemini 1 Gemini API 1 gemini-api 1 GeminiAPI 1 GeminiPrompt 1 GeminiPromptEngineering 1 GmailRestrictions 1 Google Apps Script 7 Google Discover 1 google-apps-script 3 Google-Apps-Script 1 google-blogger 1 googleAppsScript 1 GoogleAppsScript 6 googlesapps-script 1 hero-image 1 HTMLWidgetBlogger 1 http-429 1 HuggingFace 1 HuggingFaceAPI 1 ico-generator 1 Image Optimization 1 image-automation 1 image-optimization 1 In-App Browser 1 invalid-argument 1 javascript 3 JavaScript 2 javascript-automation 1 javascript-toc 1 jlab-codelog 1 JLabDevLog 1 json-schema 1 label-length-limit 1 linux 1 meta-description 1 Negative Prompt 1 notion-api 1 notion-database 1 notion-gas-integration 1 NotionAPI 1 NotionAutomation 1 notionintegration 1 NotionIntegration 1 OAuth Scope 1 pagination 1 payload-validation 1 permalink-management 1 PersonaDesign 1 pil-image-processing 1 Pinterest 1 Pollinations 1 Pollinations Flux 1 pollinations-api 1 PollinationsAI 1 PositionAbsolute 1 Prompt Design 1 Prompt Engineering 1 prompt-engineering 1 Prompt-Engineering 1 PromptEngineering 1 publishorder 1 python-favicon 1 queue-stuck 1 RandomDelay 1 rate-limiting 1 Refactoring 1 rfc3339 2 Robots Meta Tag 1 RSS Feed 1 scriptNotFound 1 Search Engine Optimization 2 Search-Engine-Optimization 1 SEO Tips 1 seo-optimization 4 SEO-Optimization 1 seriesmanagement 1 SidebarLinks 1 SNSMarketing 1 SVGIcon 1 table-of-contents 1 TechJournal 1 Template 1 ThreadsAPI 1 tistory-migration 1 transparent-favicon 1 triggerCleanup 1 Troubleshooting 1 unzip 1 URL Slug 1 vanilla-js 1 Web Development 1 Web Publishing 2 Web Security 1 web-development 1 Web-Development 1 web-development-tips 1 web-performance 1 web-scraping 1 WebAccessibility 1 WebDevelopment 3 WebDevelopmentTips 1 WebDevJourney 1 windows 1 WorkflowAutomation 1 XMLParser 1 XMLSpecialCharacters 1 XMLTheme 1 zip 1
    인기 글
    GAS에서 Blogger API 403 오류 완전 정복 — appsscript.json 스코프와 GCP 프로젝트 연결
    GAS에서 Blogger API 403 오류 완전 정복 — appsscript.json 스코프와 GCP 프로젝트 연결
    Google Apps Script 실행 시간 6분 제한 — Gmail 계정 함정과 랜덤 딜레이 설계
    Google Apps Script 실행 시간 6분 제한 — Gmail 계정 함정과 랜덤 딜레이 설계
    Blogger 테마 XML 직접 뜯어봤더니 알게 된 것들
    Blogger 테마 XML 직접 뜯어봤더니 알게 된 것들
    다른 블로그
    IssueLog
    IssueLog
    issuelog.jinbytes.com
    KidStory
    KidStory
    kidstory.jinbytes.com
    IT Story
    IT Story
    jinbytes.com
    © Jin's Lab · J.Lab · All Rights Reserved
    jinbytes.com · Powered by Blogger