sanga-log
Portfolio

스마트스코어 · 2024.12 — 2025.06 · 사내 최초 도입

모바일 네트워크 광고 시스템

사내 최초로 도입한 모바일 네트워크 광고 시스템에서 4가지 광고 타입을 통합 처리하는 공통 Mixin을 설계 — 다른 앱에서 import 한 줄로 도입 가능.

Context

  • 누적 회원 400만+ 국내 1위 골프 O2O 플랫폼 앱에 광고 수익 모델 도입
  • 사내 최초로 도입한 모바일 네트워크 광고 시스템
  • 다른 앱에서도 그대로 import 가능한 공통 Mixin으로 설계

Stack & Role

Vue2Webpack

광고 네트워크 SDK 연동 + 공통 Mixin 모듈 설계 + 포인트 차감 가드 같은 도메인 로직을 구현.

Decisions

ADR 01

광고 호출 3단계 분리 — preload / load / open

광고 호출 시 SDK 라이브러리 로딩으로 노출이 지연되고, load·open 동시 호출 중 페이지 이탈 시 다른 페이지에서 광고가 뜨는 race condition도 발생. 동기 호출(load+open 한 함수)은 단순하지만 페이지 흐름과 맞지 않음. 사용자 흐름에 따라 preload / load / open 3단계로 분리 + RepeatableLoad 옵션으로 Open/Close 후 자동 재 Load — 모든 흐름에서 끊김 없이 즉시 노출.

Result

  • 광고 호출 3단계 분리(preload / load / open)
  • RepeatableLoad 옵션으로 Open/Close 후 자동 재 Load
  • 페이지 이탈 race condition 해결, 끊김 없는 광고 노출

Retrospective

알게 된 것 — 외부 SDK는 lifecycle을 본인 페이스로 가져감. 동기 가정 + 단순 await는 결국 페이지 이탈 race로 돌아옴.

ADR 02

4가지 광고 타입 통합 Mixin — 사내 공통 모듈

4가지 광고 타입(InterstitialImage / InterstitialVideo / RewardVideo / BottomModal)을 통합 처리하는 공통 Mixin 모듈 설계. 타입별로 컴포넌트를 따로 만들지 않고 OS·Placement ID 상수화, 각 타입별 콜백 액션 매핑(RewardVideo는 영상 시청 완료 여부 status: completed/skipped 분기), idx만으로 광고 호출 가능한 구조로 일원화. 네이버 광고(NAM)와 제3자 비디오 광고 SDK 두 종을 동일 인터페이스로 통합한 것도 같은 추상화 사고. 다른 프로젝트에서도 그대로 import해서 쓸 수 있는 모듈로 설계.
광고 시스템 통합 인터페이스
다른 팀: idx 하나만 알면 광고 호출

this.preloadAd(this.getPlacementId(3))

공통 Mixin이 SDK·OS·위치 매핑 자동 해결
SDK 라우팅 — 네이버 광고 vs 비디오 광고

네이버 NAM → adUnitId 동적 생성 (1:1 매핑, placementId 불필요)
제3자 SDK → PLACEMENT_ID_LIST[idx][OS] (14 × 2 매핑)

광고 노출

4가지 타입 통합 — InterstitialImage / InterstitialVideo / RewardVideo / BottomModal

다른 팀은 idx 인자만 알면 호출 가능. SDK 라우팅·OS 분기·placementId 매핑은 Mixin이 내부에서 자동 처리.

네이버 광고 (NAM) Mixin
📌 네이버 광고 (NAM) — 동적 adUnitId 생성 (위치명을 idx로 상수화)
const ADUNIT_NAMES = {
  1: "ScoreEnter", 2: "ScoreEnter_Round", 3: "Main_Round",
  4: "Main_Search", 5: "Selfcheck", 6: "Spoint", 7: "Attendance_Check",
};

get adUnitId() {
  📌 형식: <COMPANY>_<위치명>_Webnative_<OS>-<NAM_ID>
  📌 어드민에서 NAM adUnitId와 1:1 매핑되어 리포트 집계 정확 (별도 placementId 불필요)
  return `<COMPANY>_${this.adUnitName}_Webnative_${this.osType}-<NAM_ID>`;
}

📌 호출 측 — 다른 팀이 idx 하나만 알면 광고 노출 가능
this.adUnitName = ADUNIT_NAMES[idx];
this.requestAd(this.adUnitId);

네이버 광고는 어드민 콘솔에서 NAM adUnitId와 1:1 매핑이라 별도 placementId 불필요 → 회사명·위치·OS·NAM ID 조합으로 adUnitId를 동적 생성. 다른 팀은 idx 하나만 알면 호출 가능.

비디오 광고 (제3자 SDK) Mixin
📌 비디오 광고 (제3자 SDK) — 위치·OS별 placementId 상수화
export const PLACEMENT_ID_LIST = {
  1: { IOS: "<PL_IOS_1>", Android: "<PL_AOS_1>" }, // 모달 이미지 배너 A
  2: { IOS: "<PL_IOS_2>", Android: "<PL_AOS_2>" }, // 모달 이미지 배너 B
  3: { IOS: "<PL_IOS_3>", Android: "<PL_AOS_3>" }, // 출석체크 리워드 비디오
  4: { IOS: "<PL_IOS_4>", Android: "<PL_AOS_4>" }, // 포인트 적립 리워드 비디오
  📌 ... 총 14개 위치 × 2 OS = 28개 항목 (전면 비디오 8개 포함)
};

📌 공통 Mixin — idx만으로 위치·OS 매핑 자동 해결
getPlacementId(idx) {
  const osType = this.$native.getOsType() === "android" ? "Android" : "IOS";
  return PLACEMENT_ID_LIST[idx]?.[osType] || null;
}

📌 호출 측 — 두 SDK가 동일 인터페이스로 통합 (idx 인자만)
this.preloadAd(this.getPlacementId(3)); // 출석체크 리워드 비디오

비디오 광고는 SDK 제공 업체가 달라 placementId가 필수 → 14개 위치 × 2 OS = 28개 항목을 단일 getPlacementId(idx) 함수로 일원화. 네이버 광고와 동일한 idx 인터페이스로 두 SDK 통합.

Result

  • 4가지 광고 타입 통합 처리 Mixin 모듈
  • OS·Placement ID 상수화 + 콜백 액션 매핑
  • idx만으로 광고 호출 가능 — 다른 프로젝트 재사용 가능

Retrospective

알게 된 것idx만 알면 광고 호출이 끝나는 인터페이스 덕에 다른 앱에서 import 한 줄로 도입 완료. 모듈을 만든 다음 사용하는 사람의 인지 부담을 측정한 게 자산화의 핵심.

ADR 03

포인트 차감 가드 — 광고 로딩 중 중복 클릭

앱 내 보상 시스템 — 광고 시청·미니게임으로 s포인트를 적립하고, 모은 s포인트를 실사용 가능한 s캐시로 전환하는 흐름을 직접 구현. 이 전환 버튼에서 5회/10회 진입 시 5초 전면 비디오 광고가 트리거되는 구조였음. 광고 로딩(4~10초) + 재생(5초) 동안 사용자가 전환 버튼을 추가 클릭하면 광고는 이미 진행 중인데 s포인트만 계속 차감되는 사용자 손해 발생. 단순 disabled로 막으면 UX 불명확 → 광고 호출 시 카운팅 플래그를 false로 잠그고 OnInterstitialVideoAdClosed 콜백에서 true로 복귀하는 가드로 해결. 사용자 입장에선 광고 진행 중 추가 차감 0.

Result

  • 광고 로딩·재생 중 포인트 추가 차감 차단
  • 카운팅 플래그 + OnInterstitialVideoAdClosed 콜백 가드
  • 사용자 인지 가능한 명확한 흐름

Retrospective

다시 한다면 잠금 상태를 명시적 ref가 아니라 store (Pinia/Vuex)로 흡수했을 것. 명령형 ref는 컴포넌트 깊이 들어갈수록 추적이 어려워졌음.

Result

  • 사내 최초로 모바일 네트워크 광고 도입
  • 4가지 광고 타입(InterstitialImage / Video / Reward / BottomModal) 통합 Mixin 공통 모듈
  • preload / load / open 3단계 분리로 끊김 없는 노출
  • 광고 로딩 중 중복 클릭 가드로 포인트 차감 보호
  • 다른 앱에서 import 한 줄로 도입 가능한 공통 Mixin