Gmarket CPC 광고 전체 워크플로우 — 입찰에서 리포트까지
Gmarket 파워클릭(CPC 키워드광고)의 전체 워크플로우를 해부합니다. 광고주의 입찰가 설정부터 랭킹 계산, 광고 서빙, 클릭 추적, 과금, 그리고 최종 리포트 생성까지 — 이벤트 기반 파이프라인이 어떻게 유기적으로 맞물려 돌아가는지 상세히 기록합니다.
참고: 이 글의 코드 및 수치는 실제 업무 경험을 바탕으로 개념적으로 재구성한 것입니다. 실제 회사 코드와는 무관합니다.
전체 파이프라인 다이어그램
들어가며
Gmarket AdTech팀에서 CPC 광고 시스템 전반을 담당하며, 광고주의 입찰가 설정 하나가 최종 리포트로 조회되기까지 수많은 컴포넌트가 어떻게 유기적으로 맞물려 돌아가는지 전체 파이프라인을 분석하고 고도화하는 작업을 진행했습니다.
표면적으로는 "광고 클릭 시 광고주에게 비용이 청구된다"는 단순한 CPC 모델이지만, 그 이면에는 실시간 랭킹 계산 → 광고 서빙 → 이벤트 수집 → 과금 → 리포트 생성으로 이어지는 복잡한 이벤트 기반 파이프라인이 있습니다.
이 글에서는 파워클릭(CPC 키워드광고)의 전체 워크플로우를 단계별로 해부합니다.
1단계 — 광고주 입찰가 변경 & Facade API
입찰가 설정의 시작점
광고주는 어드민 시스템을 통해 키워드별 입찰가, 일 예산, 광고그룹 상태 등을 설정합니다. 이 변경 요청은 Facade API로 진입합니다.
Facade API는 Spring Webflux 기반의 논블로킹 리액티브 서버입니다. 광고주 요청을 받는 진입점이기 때문에 높은 동시성 처리가 필요했고, 동기식 블로킹 서버 대신 리액티브 스택을 채택한 이유가 여기 있습니다.
// Facade API — 입찰가 변경 요청 처리 (개념 코드)
@PostMapping("/ad-groups/{adGroupId}/bid")
public Mono<ResponseEntity<Void>> updateBid(
@PathVariable String adGroupId,
@RequestBody BidUpdateRequest request) {
return bidService.updateBid(adGroupId, request.getBidPrice())
.then(rankingEventProducer.publish(new RankingUpdateEvent(adGroupId))) // Kafka 발행
.thenReturn(ResponseEntity.accepted().<Void>build());
}
핵심은 Kafka 토픽 발행 후 즉시 응답입니다. 무거운 랭킹 계산을 API 응답 시간에 묶지 않고 비동기로 분리했습니다.
2단계 — Ranking Consumer (랭킹 계산 엔진)
Kafka 토픽을 구독하는 Ranking Consumer는 이 파이프라인의 핵심 두뇌입니다.
입찰가 계산
CPC 광고의 순위는 단순히 입찰가가 높다고 1위가 되는 구조가 아닙니다. 일반적으로 아래와 같은 요소를 복합적으로 계산합니다.
랭킹 점수 = f(입찰가, 품질지수, CTR 예측값, ...)
다단계 Validation
랭킹 계산 전, 광고가 실제로 노출될 자격이 있는지 다각도로 검증합니다.
| Validation 항목 | 내용 |
|---|---|
| 판매 제한 여부 | 셀러의 판매 정지 / 제한 상태 확인 |
| 광고그룹 상태 | 광고그룹의 활성/일시정지/종료 여부 |
| 일 예산 소진 | 오늘 예산이 이미 소진되었는지 |
| 품절 여부 | 해당 상품의 재고 상태 |
| 광고 소재 검수 | 광고 소재가 정상 승인 상태인지 |
이 Validation을 통과한 광고만 랭킹 계산 대상이 됩니다.
결과 저장 & Snapshot 발행
랭킹 계산 완료
├── MongoDB에 최종 랭킹 1~50위 저장 (광고 서빙 API가 읽는 실시간 데이터)
└── Kafka에 Ranking Snapshot 이벤트 발행 → Databricks 적재
Databricks에 적재되는 Ranking Snapshot은 이후 리포트 워크플로우와 성과 분석의 원천 데이터가 됩니다.
3단계 — AD Serving API & 광고 노출
MongoDB에 저장된 랭킹 데이터를 AD Serving API가 읽어 사용자에게 광고를 내려줍니다.
Gmarket 검색 결과, 상품 상세 등 다양한 지면에서 AD Serving API를 호출하면, 해당 키워드/지면에 맞는 상위 N개 광고를 실시간으로 반환합니다.
사용자 검색 "스마트폰 케이스"
→ AD Serving API 호출
→ MongoDB에서 해당 키워드 랭킹 상위 광고 조회
→ 광고 리스트 반환 → 사용자에게 노출
4단계 — ATS (Ad Tracking System): 이벤트 수집
사용자가 광고를 보거나 클릭하면, ATS(Ad Tracking System)가 이 이벤트를 수집합니다.
기존에 동작하던 Node.js v6 기반의 레거시 ATS 서버를 Node.js v16으로 업그레이드하고 Kubernetes 환경으로 이관하는 마이그레이션을 진행했습니다. 이 과정에서 Datadog 모니터링을 추가하여 레거시 환경을 현대화하고 이벤트 수집의 안정성을 확보했습니다.
ATS가 수집하는 이벤트:
- Impression(노출): 광고가 화면에 보여졌을 때
- Click(클릭): 사용자가 광고를 클릭했을 때
수집된 이벤트는 즉시 Kafka 토픽에 발행됩니다.
5단계 — Impression/Click Consumer & Databricks 적재
ATS가 발행한 Kafka 이벤트를 별도의 Consumer가 구독하여 Databricks에 적재합니다.
ATS → Kafka 발행
→ Impression/Click Consumer (Kafka 구독)
→ Databricks 적재 (노출/클릭 원시 데이터)
이 단계까지 오면 Databricks에는 다음 데이터가 쌓여 있게 됩니다.
- Ranking Snapshot (2단계에서 적재)
- Impression 로그 (현 단계)
- Click 로그 (현 단계)
6단계 — 과금 / 잔액 차감 Job
클릭이 발생했다고 바로 과금이 이루어지는 것은 아닙니다. Databricks에 적재된 클릭 데이터를 기반으로, 별도의 백엔드 과금 Job이 주기적으로 실행됩니다.
[클릭 데이터 Databricks 적재 완료]
→ 과금 Job 실행
→ 유효 클릭 판별 (어뷰징, 중복 클릭 등 필터링)
→ 광고주 잔액 차감
→ 차감 이력 기록
유효하지 않은 클릭(어뷰징, 봇, 짧은 간격의 중복 클릭 등)은 이 단계에서 걸러지며, 실제 과금 대상 클릭만 처리됩니다.
7단계 — 리포트 워크플로우 (Databricks)
과금 Job이 완료된 후, Databricks 리포트 워크플로우가 실행됩니다.
기존 Hadoop 기반 워크플로우의 문제
기존에는 Hadoop 기반으로 리포트 파이프라인이 동작했습니다. 문제는:
- 새벽에 대용량 배치를 수동으로 재처리해야 하는 상황이 반복됨
- 중간 스텝 실패 시 전체 재실행이 필요해 운영 부담이 컸음
Databricks Workflow로 개선
과금 Job 완료
→ Databricks Workflow 트리거
├── 노출/클릭 집계 (키워드 / 광고그룹 / 캠페인 단위)
├── 비용 집계 (일/주/월 단위)
├── CTR / CVR 지표 계산
└── 리포트 테이블 생성
Databricks Workflow는 각 스텝을 독립적으로 관리하고, 특정 스텝 실패 시 해당 스텝만 재실행할 수 있어 운영 부담이 대폭 감소했습니다. 기존에 반복되던 새벽 수동 재처리 작업도 사라졌습니다.
8단계 — 광고 어드민 리포트 조회
모든 리포트 워크플로우가 완료되면, 광고주는 어드민에서 리포트 데이터를 조회할 수 있습니다.
- 노출수 / 클릭수 / CTR
- 소진 금액 / 평균 CPC
- 키워드별 / 광고그룹별 / 기간별 필터링
- 리포트 CSV 다운로드
전체 파이프라인 요약
| 단계 | 컴포넌트 | 기술 |
|---|---|---|
| 1. 입찰가 변경 | Facade API | Spring Webflux, Kafka |
| 2. 랭킹 계산 | Ranking Consumer | Kafka, MongoDB |
| 3. 광고 서빙 | AD Serving API | MongoDB |
| 4. 이벤트 수집 | ATS | Node.js, Kafka |
| 5. 데이터 적재 | Click/Impression Consumer | Kafka, Databricks |
| 6. 과금 처리 | 과금 Job | Databricks |
| 7. 리포트 생성 | Report Workflow | Databricks Workflow |
| 8. 리포트 조회 | 광고 어드민 | REST API |
마치며
전체 파이프라인을 분석하고 각 컴포넌트들을 고도화하면서 가장 크게 체감한 것은 각 단계의 느슨한 결합(Loose Coupling) 의 중요성이었습니다. Kafka가 각 단계 사이의 비동기 버퍼 역할을 완벽하게 수행해주었기 때문에, 개별 시스템을 마이그레이션하거나 컴포넌트에 장애가 발생하더라도 전체 파이프라인으로 전파되는 것을 막을 수 있었습니다.
일부 레거시 환경을 현대화(ATS)하고, Hadoop에서 Databricks로의 이관 등을 진행한 것은 단순한 기술 교체가 아니라 운영 부담을 줄이고 데이터 파이프라인의 신뢰성을 높이기 위한 결정이었습니다. 결과적으로 반복적인 새벽 수동 재처리 없이 안정적으로 광고주 리포트를 제공할 수 있게 된 것이 큰 성과입니다.