문제가 내 코드가 아니라는 것을 깨닫기 전까지, 3주 동안 속도 제한(Rate Limits) 디버깅에 매달렸던 경험
요약
광고 플랫폼 API(Google Ads, Meta)를 활용한 데이터 파이프라인 구축 중 겪은 속도 제한(Rate Limits) 디버깅 경험을 공유합니다. 단순한 코드 오류가 아닌 플랫폼별 상이한 스로틀링 정책이 원인이었음을 밝히며, 안정적인 시스템을 위한 아키텍처 설계 방안을 제시합니다.
핵심 포인트
- 플랫폼마다 다른 API 속도 제한(Rate Limit) 정책을 반드시 이해해야 함
- 간헐적인 429 에러는 코드 오류가 아닌 플랫폼 제한일 가능성이 높음
- 단순 크론 잡 대신 재시도와 백오프가 내장된 작업 큐(Queue) 사용 권장
- OAuth 토큰 만료를 대비한 별도의 인증 모니터링 체계 구축 필요
며칠 동안 버그를 쫓다가, 알고 보니 그 "버그"가 사실 플랫폼이 설계된 대로 정확하게 작동하고 있었던 것이라는 사실을 발견한 적이 있나요? 클라이언트 리포팅 파이프라인(client reporting pipeline)을 구축할 때 저에게 그런 일이 일어났습니다. 그 교훈은 머릿속에 깊이 박혔습니다.
여러 광고 플랫폼에서 마케팅 데이터를 가져오는 것에 대해 아무도 말해주지 않는 사실이 있습니다. 어려운 부분은 대시보드가 아니었습니다. 그 밑에서 일어나는 모든 것이 문제였습니다.
서류상으로는 간단해 보였던 설정
요청 사항은 쉬워 보였습니다. Google Ads와 Meta에서 지출액(spend), 클릭(clicks), 전환(conversions) 데이터를 가져와 저장하고 차트로 보여주는 것이었습니다. 주니어 개발자라면 한 스프린트(sprint) 안에 끝낼 수 있을 것이라고 생각했습니다.
현실은 달랐습니다. Google Ads API는 개발자 토큰(developer token)당 작업 할당량(operation quotas)을 강제하며, 이 할당량은 계정 등급에 따라 다르게 확장됩니다. 한편, Meta의 Marketing API는 앱이 아닌 광고 계정 자체와 연결된 이동 평균 사용 점수(rolling usage score)를 기반으로 스로틀링(throttling)을 수행합니다.
두 개의 플랫폼. 완전히 다른 두 가지 스로틀링 철학. 두 플랫폼 모두 실제 운영 환경(production)에서 한계에 부딪히기 전까지는 실제 제한 사항을 명확히 알 수 있도록 문서화되어 있지 않았습니다.
실제로 문제가 발생한 지점
제 첫 번째 버전은 매시간 모든 클라이언트 계정을 폴링(poll)했습니다. 클라이언트 3명까지는 괜찮았습니다. 그러다 12번째 클라이언트를 온보딩하자, Meta가 간헐적으로 429 에러를 반환하기 시작했습니다. 지속적이지 않고 간헐적으로 발생했습니다. 이것이 가장 최악의 종류의 버그입니다.
처음에는 코드 문제라고 가정했습니다. 재시도 로직(Retry logic) 문제이거나, 제 작업 스케줄러(job scheduler)의 레이스 컨디션(race condition) 때문일 것이라고 생각했습니다. 저는 3주 동안 그 방향으로 파고들었습니다. 결국 진짜 원인을 찾아냈습니다. 모든 클라이언트 계정에 걸친 누적 API 호출량이 개별 계정 제한이 아닌, Meta의 앱 수준 속도 제한(app-level rate limit)을 건드리고 있었던 것입니다.
해결책은 더 많은 재시도가 아니었습니다. 지수 백오프(exponential backoff)를 적용한 요청 큐(request queue)와, 활성 대시보드가 유휴 대시보드보다 먼저 새로고침되도록 하는 우선순위 시스템을 도입하는 것이었습니다. 지나고 보니 간단했지만, 개발 시간 측면에서는 비용이 많이 들었습니다.
다중 플랫폼 리포팅 뒤에 숨겨진 진짜 아키텍처
만약 여러분이 직접 이것을 구축하고 있다면, 제가 겪은 실패를 바탕으로 실제 운영 환경 수준의 파이프라인에 무엇이 필요한지 알려드리겠습니다.
크론 잡(Cron Job)이 아닌 큐(Queue)
단순히 정해진 일정에 따라 API 호출을 날리고 결과가 잘 나오길 기도하지 마세요. 처음부터 재시도 정책(Retry Policies)과 백오프(Backoff) 기능이 내장된 BullMQ 또는 Sidekiq와 같은 적절한 작업 큐(Job Queue)를 사용하세요. 이것만 했어도 저의 그 3주는 아낄 수 있었을 것입니다.
일급 시민으로서의 토큰 갱신 (Token Refresh as a First-Class Concern)
OAuth 토큰은 조용히 만료됩니다. 일반적인 에러 로깅(Error Logging)과는 별도로, 인증 실패(Auth Failures)만을 위한 모니터링을 구축하세요. 그렇지 않으면, 고객이 왜 대시보드가 비어 보이냐고 물어올 때가 되어서야 고객의 Google Ads 토큰이 만료되었다는 사실을 알게 될 것입니다.
원시 데이터와 저장소 사이의 정규화 계층 (A Normalization Layer Between Raw Data and Storage)
Google Ads는 이를 "cost"라고 부릅니다. Meta는 "spend"라고 부릅니다. GA4는 유사한 지표들을 완전히 다른 차원(Dimension) 이름 아래에 묻어둡니다. 프론트엔드 코드에서 플랫폼별 필드 이름을 처리하게 두지 말고, 하나의 내부 스키마(Internal Schema)를 구축하여 모든 플랫폼을 그곳에 매핑하세요.
javascript // 단순화된 정규화 예시 function normalizeMetric(platform, rawData) { const mapping = { google_ads: { cost: rawData.cost_micros / 1e6, clicks: rawData.clicks }, meta: { cost: rawData.spend, clicks: rawData.clicks }, }; return mapping[platform] || rawData; }
이 함수는 사소해 보입니다. 하지만 5개 플랫폼에 걸쳐 통화 변환(Currency Conversions), 시간대 불일치(Timezone Mismatches), 기여 창(Attribution Window)의 차이를 동시에 처리해야 한다면 결코 사소하지 않습니다.
이것을 실제로 직접 구축해야 할까요?
이제 이런 종류의 시스템을 2년 동안 유지보수해 온 사람으로서 솔직한 답변을 드리겠습니다. 만약 리포팅 인프라(Reporting Infrastructure)가 제품의 핵심 차별화 요소가 아니라면, 직접 구축하는 것은 대개 잘못된 결정입니다.
가볍게 드리는 말씀이 아닙니다. 저도 무언가를 만드는 것을 좋아합니다. 하지만 Meta의 속도 제한(Rate Limit) 특이사항을 디버깅하며 보낸 모든 시간은, 우리 제품을 사용자에게 실제로 더 좋게 만들어 줄 기능을 개발하는 데 쓰지 못한 시간과 같습니다.
내 생각을 바꾼 것
결국 우리는 자체 파이프라인을 계속 유지하는 대신, 클라이언트 리포팅 워크플로우의 대부분을 전용 플랫폼인 RaiseReturn으로 옮겼습니다. 그 플랫폼은 이미 제가 직접 손으로 구축했던 멀티 플랫폼 정규화 (multi-platform normalization), 토큰 갱신 모니터링 (token refresh monitoring), 그리고 속도 제한 인지 큐잉 (rate-limit-aware queuing) 기능을 갖추고 있었습니다.
API 통합 (API integrations)은 이미 존재했으며, 이 문제를 전업으로 다루는 팀에 의해 각 플랫폼의 특이 사항(quirks)들에 대해 검증까지 마친 상태였습니다. 이는 사이드 프로젝트나 심지어 별도의 스프린트(sprint)를 통해서도 저렴한 비용으로 복제할 수 있는 것이 아닙니다.
커스텀 코드가 여전히 유효한 경우
분명히 말씀드리자면, 이것이 통합 기능을 절대 구축해서는 안 된다는 주장은 아닙니다. 만약 귀하의 리포팅 요구사항이 진정으로 독점적인 내부 시스템—커스텀 CRM이나 내부 데이터 웨어하우스(data warehouse) 등—에서 데이터를 가져와야 한다면, 어떤 기성 도구(off-the-shelf tool)도 이를 커버할 수 없습니다. 이 경우에는 어차피 커스텀 커넥터(custom connectors)를 구축해야 할 것입니다.
하지만 표준 광고 플랫폼(ad platforms)의 경우, 문제는 이미 해결되어 있습니다. 잘 테스트되고, 프로덕션 환경에서 검증되었으며(production-hardened), Meta가 새벽 2시에 API 변경 사항을 배포하여 장애를 일으켰을 때 호출(paged)을 받는 사람들에 의해 유지보수되고 있습니다. 귀하는 아마 그런 사람이 되고 싶지는 않을 것입니다.
귀하의 스택을 위한 실질적인 교훈
현재 커스텀 리포팅 파이프라인을 구축하거나 유지보수하고 있다면, 지금 바로 확인해 볼 가치가 있는 몇 가지 사항이 있습니다.
첫째, 귀하의 재시도 로직(retry logic)이 고정 간격 재시도(fixed-interval retries)가 아닌, 실제로 지수 백오프 (exponential backoff)를 구현하고 있는지 확인하십시오. 지속적인 부하 상황에서 고정 간격은 속도 제한(rate limiting) 문제를 개선하는 것이 아니라 오히려 악화시킵니다.
둘째, OAuth 토큰 실패에 대한 전용 알림(alerting)을 추가하십시오. 조용한 인증 실패(silent auth failures)는 "왜 이 클라이언트의 데이터가 누락되었나요?"라는 티켓이 발생하는 가장 흔한 원인입니다.
셋째, 전용 자동화 리포팅 도구와 비교하여 유지보수 비용을 솔직하게 평가하십시오. 때로는 구매하는 것이 수학적으로 유리합니다. 때로는 그렇지 않을 수도 있습니다. 어느 쪽이든, "그냥 우리가 만들자"라고 기본 설정하기 전에 수치를 계산해 보십시오.
마지막 생각
클라이언트 보고 인프라 (Client reporting infrastructure)는 진정으로 흥미로운 엔지니어링 문제입니다. 하지만 대부분의 팀에게 있어, 이는 처음부터 직접 구축하여 해결할 가치가 있는 문제는 아닙니다. 그 차이를 식별하십시오. 그러면 미래의 새벽 2시에 고생할 당신의 모습이 고마워할 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기