2025년 12월, 소스팀은 크리에이터-광고주 어필리에이트 마케팅 서비스 “소스링크(Saucelink)”를 런칭했습니다.
제휴 링크 생성, 클릭/전환 추적, 정산까지 하나의 파이프라인으로 묶는 것이 목표였습니다.
핵심은 단순했습니다.
“누가, 어떤 링크를 통해, 어떤 액션을 했는지를 가능한 한 정확하게 알고 싶다.”
이를 위해 업계 표준인 MMP(Mobile Measurement Partner) 연동부터 시작했고,
이후 그 메커니즘을 직접 이해·내재화해 자체 웹 트래킹 SDK를 설계·개발했습니다.
이 글은 그 과정에서 했던 기술적 선택과 시행착오를 정리한 기록입니다.
어필리에이트 구조는 단순합니다.
광고주: 상품/서비스를 제공
크리에이터: 본인 채널에 트래킹 링크를 게시
사용자: 그 링크를 통해 가입/구매
이때 중요한 건, 다음 세 가지를 놓치지 않는 것입니다.
어떤 링크가 (캠페인, 채널, 크리에이터)
언제/어디서 클릭되었고
최종적으로 어떤 전환으로 이어졌는지
그래야 광고주 입장에서는 “성과 기반 과금”이 가능하고, 크리에이터 입장에서는 “공정한 정산”이 가능합니다.
문제는 이 흐름이 웹 ↔ 앱 ↔ 여러 채널을 넘나든다는 점입니다.
여기에서 자연스럽게 MMP가 필요해졌습니다.
왜 MMP를 붙였을까?
MMP(Mobile Measurement Partner)는 앱 설치/실행/인앱 전환을 측정해 주는 서비스입니다. (예: Airbridge, AppsFlyer 등)
우리가 필요한 흐름은 아래와 같습니다.
제휴 링크 클릭 → 랜딩(웹/스토어) → 앱 설치/실행 → 구매/가입
웹 단에서의 클릭은 우리가 잡을 수 있지만,
앱 내부의 이벤트(설치, 실행, 결제 완료 등)는 각 앱/채널마다 직접 계측하기 까다롭습니다.
그래서 전략을 아래와 같이 설정했습니다.
클릭: 우리 숏링크 서버에서 1차 집계
앱/채널 내 전환: MMP SDK가 수집
연결: MMP가 보내주는 postback 데이터를 우리가 다시 링크 데이터와 맵핑
이 구조를 제대로 설계해 두면, 이후에는 자사 웹 트래킹, MMP 기반 앱 트래킹 둘 다 동일한 정산 파이프라인 안으로 통합할 수 있습니다.
1. MMP 연동에서 신경 쓴 설계 포인트
1-1. “트래킹 타입”을 설정으로 분리
우리가 생성하는 링크는 크게 두 타입으로 나뉩니다.
자사 트래킹
쿼리 파라미터 및 리다이렉트 URL을 우리가 전부 제어
MMP 트래킹
각 MMP(예: AppsFlyer)가 요구하는 파라미터/딥링크 구조를 맞춰야 함
각 MMP는 필요한 쿼리 파라미터 키, 딥링크/디퍼드 딥링크 처리 방식, clickid, campaign id 등 식별자 포맷 등의 설정이 조금씩 다릅니다.
그래서 “트래킹 타입”을 하나의 설정값으로 두고, 다음 두 레이어에서 일관되게 분기하도록 만들었습니다.
링크 생성 레이어 – 파라미터/목적지 URL 생성 시
클릭 처리 레이어 – 클릭 로그 및 리다이렉트 처리 시
새로운 MMP를 추가할 때는 이 타입에 대한 전용 핸들러만 추가하면 로직 전체가 깨지지 않도록 구성했습니다.
1-2. postback 파라미터 매핑
MMP는 특정 이벤트(앱설치, 앱 첫 실행, 특정 페이지 진입, 구매/가입 완료 등) 발생 시, 우리 서버로 postback을 보냅니다.
하지만 이 데이터에서 우리가 진짜로 알고 싶은 건 이것뿐입니다.
“이 전환이 어떤 링크에서 출발했는가?”
AppsFlyer를 예로 들면:
클릭 시점에 우리가 넘기는
clickid(혹은af_click_lookback과 함께 쓰이는 값)postback 시 그
clickid를 그대로 돌려줌
그래서 설계는 이렇게 했습니다.
클릭 시
우리 링크별로 고유 식별자(
link_id)를 만들고MMP에 넘길 외부용 식별자(
clickid)와 매핑
postback 수신 시
MMP마다 다른 파라미터 키에서
clickid를 꺼내고내부
link_id와 조인이후 파이프라인에서는 전부
link_id기준으로 처리
핵심은 “MMP별 파라미터 스키마”를 정리해 두는 것입니다.
어떤 파라미터에 캠페인 코드가 들어가는지
어떤 파라미터에 크리에이터/채널 식별자가 들어가는지
optional/required 구분, 데이터 타입 등
이 작업을 연동 초기에 제대로 하지 않으면, 나중에 전환 로그는 쌓이는데 “누구 성과인지 모르는 데이터”만 잔뜩 남게 됩니다.
1-3. 클릭–전환 조인 전략
큰 흐름은 아래와 같습니다.
클릭 로그
click_id,link_id,creator_id,campaign_id,channel…
전환 로그 (자사/웹 SDK or MMP postback)
MMP 타입,
clickid, 이벤트 타입, 매출 금액, 통화 등
조인
clickid→link_id로 매핑이후 정산/리포트는 전부
link_id를 기준으로 집계
이렇게 해두면 MMP 종류가 늘어나도,
파이프라인 downstream(정산/통계/리포트)은 전혀 건드릴 필요가 없고
upstream(연동 계층)에 MMP 어댑터만 추가하면 됩니다.
처음부터 조인 키 설계와 파이프라인 통합 구조를 확정하고, MMP를 그 위에 얹는 순서가 중요했습니다.
1-4. 캠페인 시점 데이터는 스냅샷으로 고정
링크 생성 이후에도 상품 정보는 변경될 수 있습니다. (가격, 상품명, URL 등)
하지만 정산과 리포트에는 캠페인 당시의 정보가 필요합니다.
따라서 링크/캠페인 생성 시점에 상품 및 요금 정보를 스냅샷으로 저장하고, 리다이렉트/정산 로직은 항상 이 스냅샷을 기준으로 동작합니다.
이를 통해 다음 문제를 방지합니다.
정산 시점에 상품명이 변경되어 발생하는 불일치
캠페인 당시 가격과 현재 가격이 혼재되는 문제
즉, 트래킹/정산 시스템은 시점이 고정된 스냅샷 데이터를 기준으로 동작하게 됩니다.
2. 자체 웹 트래킹 SDK 설계하기
MMP 연동을 마친 뒤, 다음 과제가 자연스럽게 이어졌습니다.
“MMP를 쓰지 않는 파트너도, 비슷한 수준의 트래킹을 쓸 수 있게 해보자.”
특히 다음과 같은 케이스를 커버하고 싶었습니다.
자체 앱 없이 웹만 운영하는 이커머스
카페24 등 웹빌더를 사용하는 쇼핑몰
개발 인력이 부족해 MMP 연동까지는 어려운 소규모 셀러
그래서 웹 환경 전용 트래킹 SDK를 설계했습니다.
MMP에서 배운 설계 패턴을 가능한 한 그대로 가져오되,
브라우저 환경과 웹빌더 제약에 맞춰 재구성했습니다.
2-1. 퍼스트파티 쿠키 기반 식별 체계
서드파티 쿠키가 점점 막히는 환경에서, 퍼스트파티로 최대한 버텨야 합니다.
기본 전략은 도메인 기준의 퍼스트파티 쿠키에 유저/세션/링크 정보를 저장하고, 링크 클릭 시 유입 정보(utm, link_id 등)를 함께 기록하는 것입니다. 이후 페이지 이동이나 재방문에서도 쿠키를 기반으로 동일 사용자를 식별합니다.
여기서 겪은 대표적인 문제는 두 가지였습니다.
(1) 유령 쿠키(Ghost Cookie) & 세션 오염
동일 방문에서 불필요한 새 세션이 계속 생성되거나, URL에 남아 있는 파라미터 때문에 만료된 캠페인이 계속 태깅되는 문제가 있었습니다.
해결 방향은 두 가지입니다.
Early Return 패턴 SDK 초기화 시 URL 파라미터와 기존 쿠키 상태를 비교해, 이미 동일 캠페인/링크로 태깅된 상태라면 쿠키 업데이트를 스킵합니다. 이를 통해 불필요한 세션 갱신과 중복 기록을 최소화합니다.
히스토리 정화(History Sanitize) 최초 진입 시 유입 파라미터(
?aff_id=...&src=...)를 읽은 뒤,history.replaceState로 즉시 파라미터를 제거합니다. 북마크/새로고침/공유 등에서 잘못된 유입 정보가 반복 전파되는 것을 방지하기 위함입니다.
이 두 가지를 적용하자 데이터 품질이 눈에 띄게 안정되었습니다.
(2) "0.1초"의 딜레마: UX vs. 계측 정확도
SDK는 페이지 렌더링을 방해하지 않으면서 초기 진입 데이터를 놓치지 않아야 했습니다. <head> 동기 로드 vs. 비동기, 첫 페인트 전 세션 생성 vs. lazy init, SPA 환경에서의 재계측 전략 등을 고려했고, 결론적으로 다음과 같이 구성했습니다.
초기화 & 데이터 전송의 책임 분리 SDK 내부에서
init()과track()을 명확히 분리했습니다. 초기화는 식별자/쿠키 세팅 위주로 최대한 빠르게, 네트워크 전송은 비동기로 처리해 UI 스레드를 점유하지 않도록 했습니다.1-Day Validation 캐싱 도메인 유효성 검증 등 무거운 인증 API 결과를 하루 단위로 캐싱합니다. 같은 도메인/스토어에서 오는 호출은 당일 추가 검증을 생략해, 첫 호출 이후에는 가벼운 로그 전송만 진행합니다.
이렇게 설계하니 트래킹 정확도와 UX 사이에서 꽤 괜찮은 밸런스를 찾을 수 있었습니다.
2-1. 웹빌더(카페24 등) 연동
SDK를 만들었다고 해서 바로 쓸 수 있는 건 아닙니다. 실제 고객이 사용하는 환경은 대부분 웹빌더(카페24 등)였고, 개발자 리소스가 거의 없어 관리자 페이지에서 앱/플러그인 설치만 가능한 경우가 대부분이었습니다.
그래서 목표를 이렇게 잡았습니다.
"앱 설치 한 번으로, 트래킹 전체 플로우가 동작한다."
이를 위해 웹빌더별로 결제/장바구니/상품 상세 페이지의 DOM 구조, 주문 완료 페이지에서 접근 가능한 데이터, 라우팅/페이지 전환 방식, 앱 스토어 정책 등을 분석했습니다.
그 다음 SDK를 두 개의 레이어로 추상화했습니다.
플랫폼 어댑터 레이어
Cafe24Adapter,CustomShopAdapter등 플랫폼별로 "어디서 어떤 정보를 어떻게 꺼낼지"를 캡슐화합니다.공통 트래킹 레이어 "장바구니 진입", "주문 완료", "회원가입" 같은 이벤트의 공통 스키마를 정의하고, SDK 내부에서 동일한 포맷으로 전송합니다.
결제 완료 시나리오를 예로 들면, 플랫폼 어댑터에서 주문 정보를 파싱하고 공통 이벤트 스키마(OrderCompleted)로 변환해 트래킹 서버로 전송합니다. 이후 파이프라인에서는 MMP든 자사든 동일하게 처리됩니다.
이 구조 덕분에 파트너사는 코드 없이 앱 설치만으로 트래킹을 시작할 수 있고, 우리는 하나의 SDK/파이프라인으로 여러 웹빌더를 공통 처리할 수 있게 되었습니다.
마치며
결국 이 모든 작업은 하나의 질문에서 출발했습니다.
"이 전환이 어떤 링크에서 왔는가?"
단순한 질문이지만, 그 답을 정확하게 내려면 생각보다 많은 것을 미리 설계해야 했습니다. MMP별 파라미터 스키마, 조인 키 설계, 퍼스트파티 쿠키 식별 체계, 웹빌더 환경의 추상화까지, 전부 이 질문 하나에 답하기 위한 과정이었습니다.
툴은 바뀝니다. 새로운 MMP가 생기고, 웹빌더가 늘어나도 같은 질문에 답할 수 있는 구조가 있다면 흔들리지 않습니다. 그게 이번 작업에서 가장 크게 확인한 것이었고, 앞으로도 그 기준으로 계속 쌓아갈 생각입니다.
글 읽어주셔서 감사합니다.