광고 예산 제어 시스템 개선기: Part 1 – 시스템과 가시성 개선

Image not Found

Introduction

안녕하세요, 광고 엔진 팀의 Bale, 그리고 Liam입니다. 버즈빌 광고 플랫폼의 변천 과정을 소개해 드리는 시간입니다. 최근에는 수평 확장 가능한 광고 서버를 만드는 법에 대해 소개해 드렸는데요. 이번에는 버즈빌의 광고 플랫폼에서 광고 서버와 만만찮게 중요한 기능인 예산 제어 시스템을 어떻게 개선해나가고 있는지 살펴보고자 합니다. 예산 제어는 캠페인에 할당된 예산을 운영 기간 동안 잘 분배해 광고 성과를 극대화하기 위한 기능입니다. 만약 예산 제어를 하지 않거나 잘못 동작한다면, 캠페인은 게시되자마자 한 번에 예산이 다 소진되거나 갑작스러운 지출 스파이크가 발생할 수 있습니다. 또한 단기간 내에 모든 예산을 사용하여 유입이 이뤄진다면, 장기적으로 최적의 성과를 얻을 수 없고 광고주의 만족도 하락으로 이어질 것입니다.

초기 버즈빌의 예산 제어 기능은 광고 서버에 통합된 커맨드 형태로 구현되어 있었으나, 다양한 비즈니스 요구사항들을 수용하기 위해 변경을 거듭했고 현재는 별도 마이크로서비스로 분리하여 고도화를 해나가고 있습니다. 이번 시리즈에서는 버즈빌에서 예산 제어 시스템을 고도화하면서 얻은 인사이트를 공유하고자 합니다.

예산 제어 시스템 소개

가장 먼저 광고 시스템에서 예산 제어가 어떻게 작동하는지 살펴보도록 하겠습니다.

예산 제어 시스템은 현재까지의 광고 예산 소진액이 계획된 예산 소진 속도와 비교해서 빠른지 느린지를 판단하여 다음 주기 동안의 예산 지출 속도를 결정하는 “예산 제어 상태”를 도출해냅니다. 도출된 예산 제어 상태는 광고 서버에 반영되어 개별 캠페인의 입찰 빈도를 결정하는데 활용합니다. 입찰 빈도의 증감에 따라 광고의 송출량이 결정되고, 그에 따라 실제 예산 지출 속도가 빨라지거나 느려집니다. 간단히 말하자면 예산 소진 속도가 계획보다 빠르면 줄이고, 소진 속도가 느리다면 빠르게 만드는 제어 시스템입니다. 특정 주기에 결정된 예산 제어 상태는 다음 주기의 예산 소진 속도에 영향을 주기 때문에 일종의 피드백 시스템이라고 볼 수 있습니다.

예산 제어 시스템 개요

예산 제어 시스템의 고려 사항

캠페인의 대상 오디언스가 예산에 비해 충분한 상황이라고 했을 때 배정된 일 예산 또는 전체 예산을 넘겨서 사용하거나 미달하면 안 됩니다. 또한 성과를 극대화하기 위해서는 너무 빠르게 예산이 사용되어서도, 너무 느리게 사용되어도 안됩니다. 이런 정교한(?) 제어가 잘 수행되기 위해서는 크게 다음 사항들을 고려해야 합니다.

1. 캠페인의 현재 지출 목표

예산은 다양한 시계열을 기준으로 분배됩니다. 캠페인 전체 운영 기간 동안 예산을 매일 얼마나 배정해야 할지, 그리고 하루 동안에는 매시간, 분 단위로 예산이 얼마나 지출되어야 하는지 결정할 수 있어야 합니다. 일간 예산을 매시간마다 동일하게 나눠서 지출할 수도 있겠지만, 캠페인의 대상 오디언스가 많은 시간대에 집중적으로 지출을 하는 것이 전환 성과가 더 좋을 것입니다. 따라서 트래픽 패턴, 그리고 실제로 캠페인 대상과 부합하는 오디언스의 비중 등을 고려해 매 분 단위로 지출 목표 금액을 미리 산정할 수 있어야 합니다.

2. 현재까지 지출된 예산, 그리고 곧 지출로 전환될 예산

목표 예산이 있다면 현재까지 지출된 예산을 알아야 판단을 할 수 있겠죠. 그래서 현재까지 지출된 예산 정보를 필요로 하는데요. 광고 송출 시점으로부터 사용자가 광고와 상호작용하여 노출, 클릭, 전환 등의 지출 이벤트가 발생할 때까지의 짧게는 수초에서 길게는 수시간의 지연이 발생합니다. 또한 이벤트가 발생한 예산이 집계될 때까지의 데이터 파이프라인 지연시간도 존재하죠. 정확한 예산 제어를 하기 위해서는 파이프라인의 집계 지연을 가능한 줄이고 광고 송출은 했으나 아직 발생하지 않은 지출 이벤트가 얼마나 될지 예측할 수 있어야 합니다.

3. 예산 제어 상태 결정

예산 제어 상태를 자동차로 비유하자면 액셀러레이터, 브레이크와 비슷합니다. 달리는 자동차가 목표한 지점에서 잘 멈추기 위해서는 브레이크를 적절한 시점부터 밟아야 할 텐데요. 예산 제어 시스템은 예산 지출 목표와 지출 예산을 가지고 액셀러레이터를 밟을 것인지 브레이크를 밟을 것인지, 얼마나 세게 밟을 것인지를 결정해야 합니다. 빠르게 달리다가 급하게 브레이크를 밟으면 쭉 밀려나가서 위험한 것처럼, 캠페인 예산 지출도 종료지점을 미리 인지하고 점진적으로 속도를 줄일 수 있어야 목표 예산보다 과도하게 지출하는 것을 방지할 수 있습니다.

예산 제어 시스템 개선 여정

최초에는 광고 할당, 캠페인 관리, 예산 제어 기능이 모노리틱 광고 서버에 모두 포함되어 있는 형태였습니다. 송출된 광고에 대해 노출, 클릭 이벤트가 발생하면 실시간 예산 소진 상태를 기록하는 Redis에 소진 상태를 업데이트하고, 배치 커맨드(Jenkins)를 활용해 주기적으로 운영 중인 모든 광고의 실제 소진 예산과 목표 소진 예산을 비교하여 캠페인 송출을 중단하거나 재개하도록 구현되어 있었습니다. 개략적으로 표현하면 아래와 같습니다(디테일은 대부분 생략되었습니다).

기존 시스템은 당시의 목표는 달성했지만 계속해서 버즈빌의 캠페인 풀이 성장하고 광고 상품도 다변화됨에 따라 예산 제어 로직의 고도화가 필요했습니다. 고도화로 인한 복잡도를 줄이기 위해 단일 광고 서버에서 캠페인 메타데이터를 관리하는 캠페인 관리 시스템과 광고 예산 제어 시스템의 관심사를 분리하기로 결정하였습니다. 시스템을 한 번에 분리하기에는 캠페인 관리 시스템과 예산 제어 시스템의 의존도가 상당히 컸는데, 약 2년 반 동안 여러 단계로 나눠 분리를 진행했습니다.

1단계. 로직을 그대로 새로운 마이크로서비스로 옮겨서 분리하기

저희는 기존 구현에 대해 젠킨스 커맨드는 앞으로 필요한 성능 제공이 어려울 것 그리고 광고 서버 소스코드를 공유하는 형태는 앞으로 더 큰 구조적 복잡성과 효율 저하를 가져올 것이라는 판단하에 먼저 동일 기능을 수행하는 별도 마이크로서비스로의 분리를 진행했습니다.

이 과정에서 기존 캠페인 데이터베이스에 직접 접근하던 방식에서 광고 서버가 제공하는 API를 이용해 소진 상태를 업데이트하는 방식으로 변경했습니다. API 호출은 DB에 직접 접근하는 방식에 비해 네트워크 실패를 핸들링하거나 더 길어진 지연시간을 고려하는 등 추가적으로 대응해야 하는 부분들이 많았지만, 예산 제어 시스템은 일부 지연시간을 허용하고, 주기적으로 현재 상태에 기반한 새로운 소진 상태를 도출해 내기 때문에 최종적 일관성을 보장하는 수준에서 서비스 분리를 마무리했습니다.

예산 제어 시스템을 별도 마이크로서비스로 분리함에 따라 더 명확해진 컨텍스트 아래 오너십을 분리할 수 있었고 젠킨스 커맨드에서 활용하지 못한 APM, Prometheus 등을 활용해 서비스 가시성을 확보하는 등 필요한 기능을 자유롭게 추가해나갈 수 있었습니다.

2단계. 광고 송출 서버 분리 및 통계 시스템 구현

기존의 모노리틱 광고 서버는 광고 송출뿐 아니라 캠페인 관리, 지출 집계 등 많은 기능을 수행했는데요. 여기서 성능, 시스템 복잡도 등의 문제로 광고 송출 서버와 지출 집계를 수행하는 통계 시스템을 별도 마이크로서비스로 분리했습니다. 이 과정도 재밌는 얘깃거리가 많은데 예산 제어 맥락에서 벗어난 부분이 많아 기회가 되면 따로 다루어보겠습니다.

3단계. 광고 이벤트 트래커 구현 및 이벤트 집계 파이프라인 구현

앞서 설명드렸듯이 예산 제어 시스템은 집계되는 소진 정보와 캠페인의 예산 지출 설정에 기반해 예산이 의도대로 분배되도록 제어합니다. 이 시스템은 소진 정보 집계 파이프라인의 안정성과 지연 시간에 꽤 의존적인데 이 파이프라인이 여러 레거시들이 합쳐지면서 꼬여있어 관리하기 힘든 상태에 도달해 당시 전사적으로 도입한 kafka를 이용하여 집계 파이프라인을 최적화했습니다. 이 개선을 통해 집계 파이프라인이 안정화되고 소진 집계 지연이 줄어들어 최종적으로 예산 제어 시스템의 주기를 많이 줄일 수 있었습니다.

4단계. 캠페인 모델에서 예산 제어 상태 분리

예산 제어 커맨드를 마이크로서비스로 분리하고, 시스템 가시성을 확보했지만 아직 발목을 붙잡는 문제가 있었습니다. 이는 “예산 제어 상태"가 캠페인 모델의 속성으로 묶여있다는 점이었는데 이로 인해 우리는 “예산 제어 상태"가 업데이트될 때마다 캠페인을 수정해야 했습니다. 얼핏 보기에는 큰 문제점이 없을 수도 있겠지만 “예산 제어"는 운영 중인 모든 캠페인을 대상으로 1분에 1번씩 이루어졌기 때문에 캠페인은 1분마다 업데이트가 되어야 했습니다. 반면 캠페인을 구성하고 있는 대부분의 속성은 캠페인의 예산 정보, 타게팅, 게재 빈도 등의 메타데이터이기 때문에 하루에 많아봐야 한두 번 정도 업데이트가 됨에도 불구하고 예산 제어 상태의 업데이트 주기에 맞춰 레코드 업데이트를 수행해야 했습니다.

이 문제를 해결하기 위해서 최종적으로 캠페인 데이터베이스에서 ‘예산 제어 상태’를 제거해야 했습니다. 간단하게 그 과정을 소개 드리면 먼저 예산 제어 시스템에 ‘예산 제어 상태' 모델을 구현하고 영속화 가능한 상태로 만들었습니다. 그다음 캠페인 관리 시스템에서 ‘예산 제어 상태’를 제거할 예정이기에 광고 송출 서버의 캠페인 캐시에 ‘예산 제어 상태’를 동기화했으며 마지막으로 캠페인 관리 시스템에서 예산 제어 상태를 제거하여 목표를 달성할 수 있었습니다.

결과를 봤을 때 큰 효용이 와닿지 않을 수 있을 것 같은데요. 기능적으로는 캠페인 데이터베이스의 부하를 줄일 수 있었고 구조적으로 모델을 분리하여 변경과 확장이 용이해졌으며 두 개의 컴포넌트를 거쳐 전파되었던 예산 제어 상태를 직접 광고 캐시에 동기화하여 실패 지점을 줄여 안정성을 확보할 수 있었습니다.

지금까지 버즈빌의 예산 제어 시스템 개선 여정을 컴포넌트 수준에서 얘기해 드렸는데요. 저희는 후에 예산 제어 기능을 고도화하면서 이런 구조 개선 작업의 혜택을 실감할 수 있었고 이를 바탕으로 비즈니스 성과까지 이어갈 수 있었습니다. 개인적으로 재밌었던 경험이라 빨리 얘기드리고 싶지만 그전에 팀의 속도를 높이는데 큰 도움을 준 가시성 확보에 대해 먼저 얘기해 보겠습니다.

시스템의 가시성 확보하기

예산 소진 제어는 컨트롤 루프 기반의 피드백 시스템이기 때문에 결과가 후행적으로 반영됩니다. 따라서 제어 알고리즘을 변경하더라도 영향도를 손쉽게 확인하기가 어렵습니다. 이전에도 광고 노출이나 클릭이 어떤 패턴으로 발생했는지를 확인함으로써 사후적인 분석을 할 수는 있었지만, 예산 제어 시스템이 어떤 정보를 기반으로 어떤 결정을 내렸는지에 대해 직관적으로 이해하는 것은 쉽지 않은 일이었습니다. 제어 알고리즘에 대한 가설을 실험을 하려면 알고리즘을 변경하고, 광고 운영팀의 도움을 받아 일부 예산을 할애하여 테스트하고, 그 결과는 다음날이 되어야 확인할 수 있었습니다. 그리고 결과를 해석하기 위해서는 또 많은 추정을 했어야만 했죠.

이대로는 반복적인 개선을 만들어내는데 시간이 너무 오래 걸릴 것이라 판단하여 무슨 일이 있었는지에 대한 가시성을 확보하기 시작했습니다. 매 예산 제어 루프가 수행될 때마다 각 캠페인별로 현재 지출 목표, 실제 소진 예산, 그로부터 예산 제어 시스템이 도출해낸 예산 제어 상태 값을 로그로 기록하고 Data Lake에 내려보낸 뒤 BI 도구를 통해 분석하였습니다.

이를 통해 예산이 어떻게 계획되었으며 제어 알고리즘에 의해 실제 지출이 어떻게 제어되었는지를 눈으로 확인하고 직관적으로 이해할 수 있게 되었습니다. 가시성을 확보함으로써 더 다양한 가설들을 동시다발적으로 확인할 수 있게 되었습니다.

빠르게 가설을 검증하는 데 있어 가시성이 중요함을 깨달은 우리는 가시성을 더욱 강화하기로 했습니다. 분석계에서 데이터를 조회할 수 있게 되기까지 지연시간이 있고, BI 도구에서는 데이터 조회를 자유자재로 하기에는 약간의 불편함이 있었기 때문입니다. 로그 기반으로 메트릭을 생성할지, 애플리케이션에서 바로 메트릭을 수집할지 고민하다가 실시간성이 좋은 Prometheus를 활용하기로 했습니다. Prometheus와 Grafana를 활용해 실시간 예산 지출 상태와 예산 제어 상태를 모니터링할 수 있게 되니, 새로운 가설 검증 실험 수행하기 위해 제어 알고리즘을 변경하더라도 실시간으로 확인하고 문제가 발생하더라도 즉각적으로 대응할 수 있게 되어서 심리적인 안정감이 높아졌습니다.

팀에서는 이러한 가시성 확보의 장점을 체감하고 시스템 지표나 APM뿐 아니라 다양한 비즈니스 메트릭을 TSDB를 통해 수집하기 시작했습니다. 이전에는 전사에서 공유하는 Prometheus 클러스터만 운영되고 있어서 카디널리티가 높은 메트릭을 마음 놓고 수집하기가 어려웠는데, DevOps 팀에서 마이크로서비스 단위로 단일 테넌시를 가지는 Prometheus + Thanos 스택을 손쉽게 운영할 수 있도록 제공해 주셔서 각 서비스에서 마음껏 메트릭 수집을 할 수 있게 되었습니다.

여담이지만, 많은 경우 로그와 메트릭은 도메인 이벤트를 중점적으로 관측하기 때문에 메트릭을 추가할 때에는 기존에 로그를 생성하던 지점을 Domain Probe 패턴으로 리팩토링을 수행하여 Instrumentation 코드를 구조화해 나갔습니다.

모든 것을 측정할 필요는 없습니다만, 시스템의 중요한 도메인 이벤트를 메트릭으로 수집하고 확인하는 것은 이해도를 높이는데 큰 도움이 되었습니다.

이어지는 글에서는 본편에서 소개해 드린 시스템 개선 사항들과 가시성 향상이 장기적으로 팀의 제품과 비즈니스에 어떤 영향을 주었는지 소개해 드리도록 하겠습니다.

버즈빌 개발자 지원하기 (클릭)

버즈빌 테크 리크루터와 Coffee Chat하기 (클릭)

You May Also Like

버즈빌, 아마도 당신이 원하던 회사!

지원하기