Skip to content

SSAI는 광고를 어떻게 스트림 안에 자연스럽게 넣는가 ​

SSAI는 광고를 서버 측에서 본편 스트림에 합성해 클라이언트에 내려보내는 기술입니다. 클라이언트 입장에서는 본편과 광고가 하나의 끊김 없는 스트림으로 보입니다. 광고 차단기를 우회할 수 있고, 버퍼링 없는 매끄러운 전환이 핵심 장점입니다.


들어가기 전에 ​

광고 삽입은 크게 두 방식으로 나뉩니다.

방식동작 위치장점단점
CSAI (Client-Side)플레이어구현 간단, 개인화 쉬움, Beacon 직접 발사광고차단기에 취약, 버퍼링 발생
SSAI (Server-Side)오리진/매니페스트 서버광고차단 우회, seamless 전환서버 복잡도 증가, Beacon 처리 까다로움

CSAI는 플레이어가 광고 SDK를 로딩해서 별도 스트림을 재생하는 구조입니다. 문제는 광고와 본편 전환 시 버퍼링이 생기고, 광고 차단기가 SDK 자체를 막아버리면 매출이 사라진다는 점입니다.

SSAI는 이 문제를 서버에서 해결합니다. 매니페스트 조작(Manifest Manipulation) 방식으로 본편 세그먼트 사이에 광고 세그먼트를 끼워 넣습니다. 클라이언트는 하나의 연속된 스트림으로 인식하기 때문에 차단할 수 없습니다.


SSAI 동작 흐름 ​

SSAI 동작 흐름

핵심 구성요소:

구성요소역할
SCTE-35 마커"여기서 광고 넣어라"는 신호. Avail의 트리거
Avail광고가 삽입될 수 있는 구간. Duration 정보 포함
Ad Decision Server (ADS)VAST/VMAP 요청을 받아 광고 Creative를 반환
Manifest Manipulator매니페스트를 세션별로 조작해 광고 세그먼트를 삽입
Ad Conditioning광고 소재를 본편과 동일 스펙으로 맞추는 전처리
Beacon/Tracking광고 노출, 재생 진행률을 ADS에 보고

VAST: 광고 결정의 표준 프로토콜 ​

VAST는 IAB가 정한 비디오 광고 서빙 표준입니다. SSAI 서버가 ADS에 "광고 줘"라고 요청하면, ADS가 VAST XML로 응답합니다.

VAST 요청 ​

SSAI 서버가 ADS를 호출할 때 전달하는 정보:

GET https://ads.example.com/vast?
    duration=30
    &session_id=abc123
    &device=mobile
    &geo=KR
    &content_id=show_001
    &ip=203.0.113.1
파라미터설명
durationAvail 길이 (초). SCTE-35에서 추출
session_idSSAI 세션 식별자
device디바이스 타입 (mobile, ctv, desktop)
geo시청자 지역
content_id현재 콘텐츠 식별자
ip시청자 IP (개인화 타게팅용)

VAST 응답 구조 ​

xml
<VAST version="4.2">
  <Ad id="ad_001" sequence="1">
    <InLine>
      <AdSystem>ExampleAdServer</AdSystem>
      <AdTitle>Coca-Cola Summer</AdTitle>
      <Impression><![CDATA[https://tracker.example.com/impression?id=abc]]></Impression>
      <Creatives>
        <Creative>
          <Linear>
            <Duration>00:00:15</Duration>
            <TrackingEvents>
              <Tracking event="start"><![CDATA[https://tracker.example.com/start]]></Tracking>
              <Tracking event="firstQuartile"><![CDATA[https://tracker.example.com/q1]]></Tracking>
              <Tracking event="midpoint"><![CDATA[https://tracker.example.com/mid]]></Tracking>
              <Tracking event="thirdQuartile"><![CDATA[https://tracker.example.com/q3]]></Tracking>
              <Tracking event="complete"><![CDATA[https://tracker.example.com/complete]]></Tracking>
            </TrackingEvents>
            <MediaFiles>
              <MediaFile delivery="progressive" type="video/mp4"
                         width="1920" height="1080" bitrate="5000">
                <![CDATA[https://cdn.example.com/ads/cocacola_1080p.mp4]]>
              </MediaFile>
              <MediaFile delivery="progressive" type="video/mp4"
                         width="1280" height="720" bitrate="2500">
                <![CDATA[https://cdn.example.com/ads/cocacola_720p.mp4]]>
              </MediaFile>
            </MediaFiles>
          </Linear>
        </Creative>
      </Creatives>
    </InLine>
  </Ad>
</VAST>

VAST 핵심 요소 ​

요소설명
<Ad sequence>광고 Pod 내 재생 순서
<Impression>광고 노출 시 호출할 URL (Beacon)
<Duration>광고 길이
<TrackingEvents>재생 진행률별 Beacon URL
<MediaFiles>실제 광고 Creative 미디어 URL (해상도/비트레이트별)
<Error>에러 발생 시 보고할 URL

VMAP (Video Multiple Ad Playlist) ​

VAST가 "광고 하나"의 응답이라면, VMAP은 "전체 타임라인에서 어디에 광고를 넣을지"를 정의합니다:

xml
<vmap:VMAP version="1.0">
  <vmap:AdBreak timeOffset="start" breakType="linear">
    <!-- Pre-roll -->
    <vmap:AdSource><vmap:VASTAdData>...</vmap:VASTAdData></vmap:AdSource>
  </vmap:AdBreak>
  <vmap:AdBreak timeOffset="00:10:00" breakType="linear">
    <!-- Mid-roll at 10분 -->
    <vmap:AdSource><vmap:VASTAdData>...</vmap:VASTAdData></vmap:AdSource>
  </vmap:AdBreak>
  <vmap:AdBreak timeOffset="end" breakType="linear">
    <!-- Post-roll -->
    <vmap:AdSource><vmap:VASTAdData>...</vmap:VASTAdData></vmap:AdSource>
  </vmap:AdBreak>
</vmap:VMAP>
timeOffset의미
startPre-roll (콘텐츠 시작 전)
HH:MM:SSMid-roll (특정 시점)
endPost-roll (콘텐츠 끝난 후)
#N%퍼센트 기반 (콘텐츠의 N% 지점)

Avail: 광고가 들어갈 수 있는 구간 ​

Avail은 SCTE-35 Cue-Out ~ Cue-In 사이의 구간을 말합니다. SSAI 서버는 Avail이 감지되면 ADS를 호출해서 그 구간을 광고로 채웁니다.

Avail 채우기 로직 ​

Avail Duration: 60초

ADS 응답:
  Ad 1: 15초
  Ad 2: 30초
  Ad 3: 15초
  ─────────
  합계: 60초 (정확히 채움) ✓

Avail을 정확히 채우지 못하는 경우:

상황처리
광고 합계 < Avail 길이남는 구간에 Slate(대기 화면) 삽입
광고 합계 > Avail 길이마지막 광고를 잘라내거나 제외
ADS 응답 없음 / 타임아웃전체를 Slate로 채움
ADS가 빈 VAST 반환Avail Suppression 정책에 따라 본편 유지 또는 Slate

Avail을 채우지 못하면 검은 화면이 나가는 게 최악의 시나리오입니다. 그래서 Slate(브랜딩 화면, "잠시 후 돌아옵니다" 등)는 SSAI에서 필수 구성요소입니다.


Beacon: 광고 추적과 보고 ​

SSAI에서 가장 까다로운 부분이 Beacon(트래킹 픽셀) 처리입니다. CSAI는 플레이어가 직접 Beacon을 쏘지만, SSAI에서는 클라이언트가 "이게 광고인지 모르니까" Beacon을 직접 쏠 수 없습니다.

Beacon Fire 방식 ​

방식동작장단점
Server-Side BeaconSSAI 서버가 직접 Beacon 호출구현 단순. 하지만 클라이언트 실제 재생 여부 모름
Client-Side Beacon (Sidecar)SSAI가 Beacon URL을 클라이언트에게 전달, 클라이언트가 직접 발사정확한 viewability. 구현 복잡
HybridImpression은 서버, Quartile은 클라이언트실무에서 가장 흔한 조합

Tracking Events (VAST 표준) ​

이벤트발사 시점설명
impression광고 첫 프레임 노출과금 기준. 가장 중요
start재생 시작impression과 동시 또는 직후
firstQuartile25% 재생
midpoint50% 재생
thirdQuartile75% 재생
complete100% 재생완시청 여부 확인
skip사용자가 스킵SSAI에서는 보통 스킵 불가
pause / resume일시정지/재개
mute / unmute음소거 전환
error재생 실패

Server-Side Beacon의 한계 ​

서버가 Beacon을 쏘면 "광고가 실제로 재생됐는지"를 보장할 수 없습니다. 클라이언트가 세그먼트를 다운로드만 하고 재생하지 않았을 수도 있고, 유저가 탭을 내렸을 수도 있습니다.

이 문제 때문에 IAB의 Open Measurement SDK(OM SDK)가 등장했습니다. 클라이언트에 측정 스크립트를 삽입해서 실제 viewability(화면에 보이는지, 소리가 나오는지)를 검증합니다.

광고주 입장에서 "impression 찍혔는데 진짜 봤는지 모르겠다"는 SSAI의 태생적 약점입니다. OM SDK 연동이 안 되면 프리미엄 광고주가 SSAI 인벤토리를 기피할 수 있습니다. 이건 기술 문제가 아니라 비즈니스 문제입니다.


SCTE-35: 광고 신호의 표준 ​

SCTE-35는 방송/스트리밍에서 광고 구간을 알리는 표준 신호입니다.

핵심 메시지 타입 ​

메시지역할
splice_insert특정 시점에 광고 삽입/복귀를 지시
time_signal시간 기반 이벤트 신호. segmentation_descriptor와 조합
splice_nullKeepalive. "아직 여기 있다"는 의미

splice_insert 구조 ​

splice_insert {
  splice_event_id          // 이벤트 고유 ID
  splice_event_cancel      // 이전 이벤트 취소 여부
  out_of_network_indicator // 1=Cue-Out, 0=Cue-In
  splice_immediate_flag    // 즉시 실행 여부
  splice_time {
    pts_time              // 실행할 PTS 시점
  }
  break_duration {
    duration              // Avail 길이 (90kHz 단위)
  }
  unique_program_id        // 프로그램 식별자
  avail_num               // Avail 번호
  avails_expected         // 총 예상 Avail 수
}

segmentation_descriptor (time_signal과 조합) ​

time_signal + segmentation_descriptor로 더 풍부한 메타데이터를 전달할 수 있습니다:

segmentation_type_id의미
0x22Break Start (광고 시작)
0x23Break End (광고 종료)
0x30Provider Ad Start
0x31Provider Ad End
0x34Distributor Ad Start
0x35Distributor Ad End
0x40Unscheduled Event Start
0x41Unscheduled Event End

Cue-Out / Cue-In ​

SCTE-35의 핵심 동작:

본편 ──── [Cue-Out] ──── 광고 구간 (Avail) ──── [Cue-In] ──── 본편
              │                                      │
              └─ splice_insert(out=1, duration=60s)   └─ splice_insert(out=0)

전송 방식 ​

입력SCTE-35 전달 방법
MPEG-2 TS (RTP/UDP/SRT)TS 내 전용 PID. 가장 표준적
RTMPAMF Data Tag로 전달 (제한적)
HLS 입력EXT-X-DATERANGE 또는 EXT-X-CUE-OUT 태그

HLS에서의 광고 신호: EXT-X-DATERANGE ​

HLS 환경에서 SCTE-35 정보를 클라이언트까지 전달하는 표준 방법은 EXT-X-DATERANGE 태그입니다.

m3u8
#EXT-X-DATERANGE:ID="splice-6FFFFFF0",START-DATE="2026-06-01T10:30:00.000Z",PLANNED-DURATION=60.0,SCTE35-OUT=0xFC3025000000000000FFF00506FE...
#EXTINF:6.006,
segment_100.ts
#EXTINF:6.006,
segment_101.ts
...
#EXT-X-DATERANGE:ID="splice-6FFFFFF0",START-DATE="2026-06-01T10:30:00.000Z",END-DATE="2026-06-01T10:31:00.000Z",DURATION=60.0,SCTE35-IN=0xFC301...

주요 속성:

속성설명
ID광고 구간 고유 식별자. splice_event_id에 대응
START-DATE광고 시작 시각 (ISO 8601)
DURATION확정된 광고 구간 길이 (초)
PLANNED-DURATION예상 길이 (아직 Cue-In 안 온 경우)
SCTE35-OUTCue-Out SCTE-35 바이너리 (Base64 또는 Hex)
SCTE35-INCue-In SCTE-35 바이너리
END-DATE광고 종료 시각
CLASS이벤트 분류 (예: com.apple.hls.interstitial)

Apple은 EXT-X-DATERANGE로 광고를 시그널링하는 방식을 권장합니다. 구형 #EXT-X-CUE-OUT:DURATION=30 / #EXT-X-CUE-IN 태그도 업계에서 관행적으로 쓰이지만, 이건 비표준이고 Apple 규격에 정의되지 않은 벤더 확장입니다.


DASH에서의 광고 신호: Period와 Event ​

Multi-Period 방식 ​

광고 구간을 별도 Period로 분리합니다:

xml
<MPD type="dynamic" availabilityStartTime="2026-06-01T10:00:00Z">
  <Period id="content-1" start="PT0S" duration="PT10M">
    <AdaptationSet mimeType="video/mp4">
      <Representation bandwidth="5000000">
        <SegmentTemplate media="content_$Number$.m4s" startNumber="1" duration="180000" timescale="90000"/>
      </Representation>
    </AdaptationSet>
  </Period>

  <Period id="ad-break-1" start="PT10M" duration="PT60S">
    <!-- 광고 세그먼트: SSAI가 삽입 -->
    <AdaptationSet mimeType="video/mp4">
      <Representation bandwidth="5000000">
        <SegmentTemplate media="ad_$Number$.m4s" startNumber="1" duration="180000" timescale="90000"/>
      </Representation>
    </AdaptationSet>
  </Period>

  <Period id="content-2" start="PT11M">
    <!-- 본편 이어서 -->
  </Period>
</MPD>

EventStream 방식 ​

Period 내에서 EventStream으로 SCTE-35 신호를 전달합니다:

xml
<Period>
  <EventStream schemeIdUri="urn:scte:scte35:2013:xml" timescale="90000">
    <Event presentationTime="54000000" duration="5400000" id="1">
      <Signal xmlns="http://www.scte.org/schemas/35/2016">
        <SpliceInfoSection>
          <SpliceInsert spliceEventId="1" outOfNetworkIndicator="true">
            <BreakDuration autoReturn="true" duration="5400000"/>
          </SpliceInsert>
        </SpliceInfoSection>
      </Signal>
    </Event>
  </EventStream>
</Period>
방식장점단점
Multi-Period광고/본편 분리 명확, 세그먼트 독립MPD 갱신 필요, Period 전환 시 디코더 리셋 가능
EventStreamMPD 구조 불변, 인밴드 시그널링클라이언트 구현 복잡, SSAI 처리 까다로움

SSAI에서 Manifest Manipulation ​

SSAI의 실체는 세션별 매니페스트 조작입니다. 같은 스트림을 보는 시청자 A와 B에게 다른 광고를 보여주려면, 각각 다른 매니페스트를 내려줘야 합니다.

세션 기반 동작 ​

1. 클라이언트 → Session Init (player params 포함)
2. SSAI 서버 → 세션 ID + 세션별 매니페스트 URL 반환
3. 클라이언트 → 세션별 URL로 매니페스트 요청
4. SSAI 서버 → 해당 세션의 ADS 결과가 반영된 조작된 매니페스트 반환
5. 클라이언트 → 세그먼트 요청 (광고 세그먼트도 동일 URL 패턴)
6. SSAI 서버 → 광고 세그먼트를 본편과 동일한 도메인/경로로 프록시
원본 매니페스트:
  seg_098.ts → seg_099.ts → [Avail: 30s] → seg_105.ts → ...

시청자 A의 매니페스트:
  seg_098.ts → seg_099.ts → /session/A/ad/coca_001.ts → /session/A/ad/coca_002.ts → seg_105.ts

시청자 B의 매니페스트:
  seg_098.ts → seg_099.ts → /session/B/ad/samsung_001.ts → /session/B/ad/samsung_002.ts → seg_105.ts

URL 패턴이 본편과 동일한 도메인을 쓰므로 광고 차단기가 구분할 수 없습니다.


Ad Conditioning (광고 소재 전처리) ​

SSAI가 seamless하려면 광고 소재가 본편과 동일한 포맷이어야 합니다:

  • 같은 코덱 (H.264, H.265)
  • 같은 해상도 래더 (1080p, 720p, 480p — 본편 ABR과 1:1 대응)
  • 같은 세그먼트 길이 (본편이 6초면 광고도 6초 단위)
  • 같은 프레임레이트 (29.97fps, 30fps 등)
  • 같은 오디오 코덱/샘플레이트 (AAC 48kHz)
  • 같은 GOP 구조 (IDR 간격)

이 전처리를 "Ad Conditioning" 또는 "Ad Transcoding"이라 부릅니다. 스펙이 안 맞으면:

  • 디코더 리셋 → 화면 깜빡임/버퍼링
  • ABR 스위칭 실패
  • 오디오 팝/클릭 노이즈
  • 타임스탬프 불연속 → 플레이어 오류

실무에서 이 부분이 가장 까다롭습니다. 광고주가 던져주는 소재는 제각각이고, 이걸 모든 ABR 래더에 맞춰 미리 트랜스코딩해둬야 합니다. SSAI 도입 프로젝트에서 시간의 60%가 Ad Conditioning 파이프라인 구축에 들어가는 경우도 흔합니다.


Ad Pod: 여러 광고 연속 재생 ​

하나의 Avail에 여러 광고가 들어가는 것을 Ad Pod이라 합니다.

Avail (60초)
├── Ad 1: 15초 (Coca-Cola)    ← sequence="1"
├── Ad 2: 30초 (Samsung)      ← sequence="2"
└── Ad 3: 15초 (Hyundai)      ← sequence="3"

VAST 응답에서 <Ad sequence="N">으로 재생 순서가 지정됩니다. SSAI 서버는 이 순서대로 세그먼트를 매니페스트에 배치합니다.

Pod 내에서 개별 광고의 Beacon은 독립적으로 발사됩니다 — Ad 1의 complete이 찍혀야 Ad 2의 impression이 유효합니다.


마치며 ​

SSAI의 핵심을 정리하면 다음과 같습니다.

신호 계층:

  • SCTE-35 → Avail 생성 (어디에 광고를 넣을지)
  • VAST/VMAP → 어떤 광고를 넣을지 (Creative, Duration, Tracking)
  • Beacon → 광고가 실제로 재생됐는지 보고

처리 계층:

  • Manifest Manipulation → 세션별 매니페스트 조작
  • Ad Conditioning → 광고 소재를 본편 포맷에 맞춤
  • Segment Stitching → 본편과 광고 세그먼트를 하나의 스트림으로 합성

비즈니스 현실:

  • Impression Beacon이 돈입니다. Beacon이 안 찍히면 광고비를 받을 수 없습니다.
  • Viewability 검증(OM SDK)이 안 되면 프리미엄 광고주가 기피합니다.
  • Avail을 못 채우면(Slate 노출) 해당 구간 매출이 0입니다.

결국 SSAI는 "기술적으로 seamless한 전환"만큼이나 "비즈니스적으로 Beacon이 정확히 찍히는 것"이 중요합니다. 광고 기술의 핵심은 항상 돈의 추적입니다.

참고 자료 ​

基于 VitePress 构建 · 部署于腾讯云 EdgeOne Pages