본문으로 건너뛰기

컨텍스추얼 타겟팅

페이지 본문의 컨텍스트(카테고리·키워드·지역·데모그래픽)를 브라우저 안에서 분석하거나, 매체가 보유한 검색어 등의 키워드를 직접 주입하여 광고 요청에 컨텍스추얼 타겟팅(Contextual Targeting) 정보를 함께 전달할 수 있습니다.

  • context: 컨텍스트 분석 스크립트(ctx-sdk.js)가 본문을 분석한 결과를 광고 슬롯에 전달합니다.
  • keyword: 매체가 검색어 등 단순 키워드를 광고 슬롯에 직접 전달합니다.

두 값은 서로 독립적이며, 함께 설정하면 두 값 모두 광고 요청에 포함됩니다.

1. Concepts​

항목contextkeyword
설정 위치defineAdSlot()의 adSlotInfo.contextdefineAdSlot()의 adSlotInfo.keyword
값ctx-sdk.js의 분석 결과 객체(res.context)문자열(검색어 등 단순 키워드)
생성 주체ctx-sdk.js의 온디바이스(On-device) 분석매체
용도본문 기반 컨텍스추얼 타겟팅분석 결과가 없을 때의 폴백 또는 분석 결과 보완
  • 분석은 사용자 브라우저 안에서만 수행됩니다. 본문 텍스트는 어떤 서버로도 전송되지 않으며, 광고 요청에는 분석 결과만 포함됩니다.
  • 쿠키나 사용자 식별자를 사용하지 않으므로 쿠키리스(Cookieless) 환경에서도 동작합니다.
  • 매체는 분석 결과 객체를 가공하지 않고 그대로 context에 전달하면 됩니다. 광고 요청 파라미터 인코딩은 SDK가 처리합니다.

전체 흐름은 다음과 같습니다.

컨텍스추얼 타겟팅 전체 흐름

2. 준비 사항​

항목설명
광고 유닛 ID슬롯별로 발급받은 adUnitId
광고 슬롯 엘리먼트슬롯마다 고유한 id를 가진 div 엘리먼트
분석 스크립트https://sdk.naverads.net/libs/ctx-check/ctx-sdk.js
키워드 번들ctx_model2_100k.bin. 분석 스크립트 경로 아래의 버전 디렉터리(<버전>/ctx_model2_100k.bin)에서 자동으로 로드됩니다. 분석 스크립트 버전과 항상 짝이 맞는 번들을 받으며, SDK가 갱신되어도 분석 스크립트 URL은 바뀌지 않습니다.
본문 영역본문을 감싸는 엘리먼트의 CSS 선택자(예: #postViewArea, article). 지정하지 않으면 자동으로 추출합니다.
지원 브라우저Chrome 85+, Safari 14+, Firefox 79+, Edge 85+ (IE 미지원)

3. 컨텍스트 분석 결과 적용하기​

3-1. 스크립트 삽입​

  • <head> 또는 본문 앞에 아래 스크립트를 삽입합니다. gfp-core.js는 async로, ctx-sdk.js는 defer 로 로드합니다.

    <script async src="https://sdk.naverads.net/libs/glad/prod/gfp-core.js"></script>
    <script>
    window.gladsdk = window.gladsdk || { cmd: [] };
    </script>
    <script defer src="https://sdk.naverads.net/libs/ctx-check/ctx-sdk.js" data-root="#postViewArea"></script>
    • defer 스크립트는 HTML 파싱을 막지 않으면서 DOMContentLoaded 이전에 실행이 보장됩니다. 따라서 DOMContentLoaded 이후에는 별도의 큐 없이 gladsdk.CtxCheck를 바로 호출할 수 있습니다.
    • data-root에 본문 영역 선택자를 지정합니다. 자세한 내용은 5. 본문 영역 지정하기를 참고해주세요.
    • ctx-sdk.js는 gladsdk.CtxCheck(고정 별칭 gladsdkCtxCheck) 외에 전역 변수를 만들지 않으며, 로드만으로는 분석을 수행하지 않습니다.

3-2. 상단 광고 슬롯 - 분석을 기다리지 않고 즉시 요청​

  • 첫 화면에 노출되는 광고 슬롯은 분석을 기다리지 않고 기존과 동일하게 요청합니다.

    <div id="ad-top"></div>
    <script>
    window.gladsdk.cmd.push(function () {
    window.gladsdk.defineAdSlot({ adUnitId: 'TOP_UNIT_ID', adSlotElementId: 'ad-top' });
    window.gladsdk.displayAd('ad-top');
    });
    </script>

3-3. 하단 광고 슬롯 - 분석 완료 후 context를 포함하여 요청​

  • 본문 아래의 광고 슬롯은 분석이 끝난 뒤 정의합니다. 분석은 DOMContentLoaded에서 시작하고, 광고 슬롯 정의와 요청만 gladsdk.cmd에 추가합니다.

    <article id="postViewArea">…본문…</article>
    <div id="ad-mid"></div>
    <div id="ad-bottom"></div>
    <script>
    document.addEventListener('DOMContentLoaded', function () {
    // 1) 분석 시작 - recommend()는 로드 시간을 포함해 3초(maxMs 기본값) 안에 항상 응답합니다.
    // 스크립트가 로드되지 않은 경우(차단, 404 등)에만 null로 처리하여 context 없이 광고를 요청합니다.
    var analysis = window.gladsdk.CtxCheck
    ? window.gladsdk.CtxCheck.recommend({ idle: true }).catch(function () { return null; })
    : Promise.resolve(null);

    // 2) 분석이 끝나면 광고 슬롯을 정의하고 광고를 요청합니다.
    analysis.then(function (res) {
    window.gladsdk.cmd.push(function () {
    ['ad-mid', 'ad-bottom'].forEach(function (id) {
    var adSlotInfo = { adUnitId: 'BOTTOM_UNIT_ID', adSlotElementId: id };
    if (res && res.context) {
    adSlotInfo.context = res.context;
    }
    window.gladsdk.defineAdSlot(adSlotInfo);
    window.gladsdk.displayAd(id);
    });
    });
    });
    });
    </script>
  • 아래 세 가지 규칙을 지켜주세요.

    1. res.context는 가공하지 않고 그대로 전달합니다. 객체 규격은 ctx-sdk.js가 만들고, 광고 요청 인코딩은 SDK가 처리합니다.
    2. res.context가 null이면 context를 설정하지 않습니다. 분석 결과가 없거나 로드가 시간 상한을 넘긴 경우이며, 광고는 context 없이 정상 요청됩니다.
    3. 상단 광고 슬롯을 지연시키지 않습니다. recommend()는 로드 시간을 포함해 3초 안에 항상 응답하며, 시간 상한을 넘기면 그때까지 분석한 부분 결과(partial: true)를 반환합니다. 매체 코드에 별도의 타이머를 둘 필요가 없습니다(defer로 로드한 경우. async로 로드하는 경우는 7. 로딩 방식별 주의 사항을 참고해주세요).
  • 설정된 값은 adSlot.getContext()로 확인할 수 있습니다.

  • 전체 코드는 컨텍스추얼 타겟팅 예제 - 기본 예제를 참고해주세요.

3-4. 중복 제어와 함께 사용하기​

  • 같은 페이지의 여러 광고 슬롯에 동일한 광고가 노출되지 않도록 하려면 광고 중복 제어 매니저로 광고를 요청합니다. context 전달 방식은 동일하며, 3-2와 3-3의 광고 요청 부분만 아래와 같이 변경합니다.

    var adDedupManager; // 상단, 하단 광고 슬롯이 공유하는 중복 제어 매니저

    // 상단: 즉시 요청
    window.gladsdk.cmd.push(function () {
    adDedupManager = window.gladsdk.setAdDeduplication('dedupReqId', 2); // 광고 슬롯 수 - 1
    var topAdSlot = window.gladsdk.defineAdSlot({ adUnitId: 'TOP_UNIT_ID', adSlotElementId: 'ad-top' });
    adDedupManager.displayAds([topAdSlot]);
    });

    // 하단: 분석 완료 후 요청
    document.addEventListener('DOMContentLoaded', function () {
    var analysis = window.gladsdk.CtxCheck
    ? window.gladsdk.CtxCheck.recommend({ idle: true }).catch(function () { return null; })
    : Promise.resolve(null);

    analysis.then(function (res) {
    window.gladsdk.cmd.push(function () {
    var adSlots = ['ad-mid', 'ad-bottom'].map(function (id) {
    var adSlotInfo = { adUnitId: 'BOTTOM_UNIT_ID', adSlotElementId: id };
    if (res && res.context) {
    adSlotInfo.context = res.context;
    }
    return window.gladsdk.defineAdSlot(adSlotInfo);
    });
    adDedupManager.displayAds(adSlots); // 상단 광고를 제외 기준으로 ad-mid, ad-bottom 순서대로 요청
    });
    });
    });
  • 아래 규칙을 지켜주세요.

    1. 중복 제어 매니저의 displayAds()로만 광고를 요청합니다. gladsdk.displayAd()로 요청한 광고 슬롯은 중복 제어에서 제외됩니다.
    2. 광고는 배열 순서대로 순차 요청됩니다. 앞 광고 슬롯의 응답을 받은 뒤 다음 광고 슬롯을 요청합니다. 같은 중복 제어 매니저에 displayAds()를 여러 번 호출해도 요청은 이어서 진행되고 중복 제외 기준도 누적되므로, 상단은 즉시 요청하고 하단은 분석 완료 후 요청하는 흐름을 그대로 유지할 수 있습니다.
    3. maxDedupReqCount는 광고 슬롯 수 - 1로 명시합니다. 광고 슬롯이 3개이면 2입니다.
    4. context는 광고 슬롯 단위로 설정합니다. 중복 제어 적용 여부와 관계없이 광고 슬롯별 광고 요청에 포함됩니다.
  • 광고 요청의 QueryString에서 적용 여부를 확인할 수 있습니다. 첫 번째 광고 요청에는 ba, bx 값이 없고 두 번째 요청부터 포함되며, 하단 광고 요청에는 context 값이 함께 포함됩니다. 자세한 내용은 중복 제어 적용 여부 확인을 참고해주세요.

  • 전체 코드는 컨텍스추얼 타겟팅 예제 - 중복 제어 적용 예제를 참고해주세요.

3-5. 첫 화면 광고 슬롯에 context를 적용하는 경우​

  • 기본 권장은 첫 화면 광고 슬롯은 즉시 요청하고(3-2), 본문 아래 광고 슬롯에만 context를 전달하는 방식(3-3)입니다.
  • 첫 화면 광고 슬롯에도 context가 꼭 필요한 경우의 구성은 컨텍스추얼 타겟팅 예제 - 첫 화면 광고 슬롯에 context 적용하기를 참고해주세요. 이 경우 첫 화면 광고는 분석을 기다리는 만큼 늦게 요청됩니다.

4. 매체 주입 키워드 설정하기​

  • 매체가 이미 보유한 검색어 등의 단순 키워드를 adSlotInfo.keyword에 설정하면 광고 요청에 함께 전달됩니다.

  • 키워드는 문장이 아닌 단어 형태로 전달합니다. 아이팟 프로, 아이팟 신형처럼 여러 단어로 된 키워드도 가능하며, 콘텐츠 제목은 전달하지 않습니다.

  • 컨텍스트 분석 결과를 사용할 수 없는 경우(스크립트 미로드, timeout: 'load', status: 'empty' 등)의 폴백으로 사용하거나, 분석 결과를 보완하는 용도로 사용합니다.

  • context와 독립적으로 동작하며, 둘 다 설정하면 두 값 모두 전달됩니다.

    window.gladsdk.cmd.push(function () {
    window.gladsdk.defineAdSlot({
    adUnitId: 'adUnitId',
    adSlotElementId: 'division',
    keyword: '아이팟 프로',
    });
    window.gladsdk.displayAd('division');
    });
  • 광고 슬롯 단위로 설정하므로 피드형 지면처럼 슬롯마다 다른 콘텐츠가 노출되는 경우 슬롯별로 다른 키워드를 지정할 수 있습니다.

    window.gladsdk.cmd.push(function () {
    feedItems.forEach(function (item, index) {
    var id = 'feed-ad-' + index;
    window.gladsdk.defineAdSlot({ adUnitId: 'adUnitId', adSlotElementId: id, keyword: item.keyword });
    window.gladsdk.displayAd(id);
    });
    });
  • 값 처리 규칙

    • 연속된 공백(개행, 탭 포함)은 단일 공백으로 합쳐지고 앞뒤 공백은 제거됩니다.
    • 설정하지 않았거나 빈 문자열, 공백만 있는 문자열인 경우 전달되지 않습니다.
    • 문자열이 아닌 값(숫자, 객체, 배열, null 등)은 오류 없이 무시됩니다.
    • 길이 제한은 없으며, 인코딩은 SDK가 처리합니다.
  • 설정된 값은 adSlot.getKeyword()로 확인할 수 있습니다.

5. 본문 영역 지정하기​

분석 품질은 본문 영역을 정확히 지정하는 데서 가장 크게 좌우됩니다. 광고, 댓글, 추천 목록이 섞이지 않은 본문 컨테이너를 지정해주세요.

방법예시비고
스크립트 속성<script defer src="…/ctx-sdk.js" data-root="#postViewArea">가장 간단한 방법. 이후 recommend() 호출에 기본으로 적용됩니다.
호출 옵션(선택자)recommend({ root: '.article-body' })페이지 유형별로 다르게 지정할 때
호출 옵션(Element)recommend({ root: document.querySelector('article') })이미 엘리먼트 참조가 있을 때
호출 옵션(함수)recommend({ root: () => document.querySelector('#a') || document.querySelector('#b') })템플릿이 여러 가지일 때. 함수는 호출 시점에 평가됩니다.
미지정recommend()의미 태그와 텍스트 밀도를 기반으로 자동 추출합니다. 선택자가 매칭되지 않는 경우에도 자동 추출로 동작합니다.
  • 본문이 iframe 안에 렌더링되는 지면은 해당 iframe 문서 안에 스크립트를 삽입해야 합니다. 상위 문서에서 실행하면 본문을 찾을 수 없어 status: 'empty'가 반환됩니다. 스크립트는 iframe 내부를 탐색하지 않습니다.
  • 제목은 <title>, og:title, h1에서 자동으로 읽으며 사이트명 꼬리는 제거됩니다. 본문이 짧을수록 제목의 비중이 커집니다.
  • 댓글 영역이 본문 컨테이너 안에 있으면 함께 분석되지만 가중치가 절반으로 낮아집니다. 가능하면 댓글 영역은 root 밖에 두세요.

6. recommend() 옵션과 응답​

6-1. 옵션​

옵션기본값설명
idletrue계산 중간마다 메인 스레드에 양보하여 광고 렌더링과 페이지 상호작용을 막지 않습니다. 운영 환경에서는 true를 권장합니다. false인 경우 한 번에 계산합니다(동기 250~500ms 수준).
rootdata-root 값본문 영역. 5. 본문 영역 지정하기 참고
count2최종 키워드 개수(kw 길이)
minSim0.8키워드 매칭 최소 유사도. 낮출수록 키워드가 늘어나지만 정확도가 떨어집니다. 기본값 사용을 권장합니다.
maxMs3000응답 시간 상한(ms). 번들 로드 시간을 포함합니다. 상한을 넘기면 그때까지의 부분 결과(partial: true)로 응답하고, 로드가 끝나지 않았다면 빈 응답(timeout: 'load', context: null)을 반환합니다. 0이면 제한이 없습니다.
text-DOM 대신 전달한 문자열을 분석합니다.

6-2. 응답​

{
status: 'ok' | 'empty' | 'no_fill',
context: {
top_cat: '자동차>신차·중고차·시세',
iab: ['30'],
kw: ['GV90', '제네시스'],
region: '용인|역북동||',
demo: 'F|30S',
safety: ['legal'],
} | null,
partial: false, // true면 maxMs 상한으로 중단된 부분 결과 (context는 그대로 사용 가능)
timeout: 'load', // (있을 때만) 번들 로드가 상한 안에 끝나지 않음 - context는 null
elapsedMs: 232, // 호출부터 응답까지 걸린 시간 (로드 포함)
// topCategory, keywords, region, demo, safety, terms 등 - 디버깅용 상세 필드. 광고 요청에는 사용하지 않습니다.
}
status의미매체 처리
ok분석 성공. context에 값이 있습니다.context: res.context로 광고 슬롯 정의
empty본문에서 광고용 키워드를 찾지 못했습니다(본문 미노출, 너무 짧은 본문, 비상업 주제 등). context는 대부분 null입니다.context를 설정하지 않고 광고 요청
no_fill여론조사 등 비상업 콘텐츠로 판정되었습니다. context는 null입니다.위와 동일
empty + timeout: 'load'번들 로드가 maxMs 안에 끝나지 않았습니다(느린 네트워크). context는 null입니다.위와 동일. 로드는 계속 진행되므로 다음 호출은 정상 동작합니다.
  • context의 각 키는 값이 있을 때만 포함됩니다.
  • iab는 IAB Content Taxonomy의 Unique ID 문자열 배열입니다. 영문자가 포함된 ID도 있으므로 숫자로 변환하지 않습니다.
  • safety가 있으면 해당 이슈(정치, 형사법률 등)와 관련된 키워드는 이미 kw에서 제외된 상태입니다. 매체가 별도로 걸러낼 필요가 없습니다.

6-3. 이벤트 (선택)​

  • document에서 아래 이벤트가 발생합니다. 로깅이나 모니터링 용도로 사용할 수 있습니다.
    • ctxcheck:ready: 번들 로드 완료
    • ctxcheck:result: 분석 완료 (event.detail에 응답 객체 전달)

7. 로딩 방식별 주의 사항​

상황동작처리 방법
defer 로드 + DOMContentLoaded 안에서 호출 (권장)항상 로드가 완료된 상태큐 없이 gladsdk.CtxCheck.recommend() 직접 호출
defer 로드 + 인라인 스크립트에서 바로 호출인라인 스크립트가 defer 스크립트보다 먼저 실행되어 gladsdk.CtxCheck가 undefinedDOMContentLoaded 핸들러 안으로 호출을 옮기거나, ctx-sdk.js 뒤에 둔 별도 defer 외부 스크립트에서 호출
태그 매니저 등으로 async 삽입이 강제되는 경우실행 시점이 보장되지 않음아래 선행 큐와 바깥 타이머 사용
gfp-core.js가 window.gladsdk를 교체하는 경우gfp-core.js는 로드 시 window.gladsdk를 새 객체로 교체ctx-sdk.js가 자동으로 다시 연결하므로 별도 처리 불필요. 선행 설정은 gladsdk.CtxCheck가 아닌 gladsdkCtxCheck에 지정
  • async 삽입이 불가피한 경우 선행 큐를 사용합니다. 로드 전에는 큐에 쌓이고, 로드 후에는 즉시 실행됩니다. 콜백 인자가 { error } 형태이면 실패입니다.

  • 선행 큐를 사용할 때는 매체 코드에 바깥 타이머를 함께 둡니다.

    • maxMs는 ctx-sdk.js가 큐의 recommend()를 실행한 시점부터 측정됩니다. 스크립트가 늦게 로드되면 그만큼 늦게 시작합니다.
    • 스크립트가 실행되지 않으면(차단, 404, 미지원 브라우저 등) 콜백이 호출되지 않습니다. 광고 요청을 보장하려면 바깥 타이머가 필요합니다.
    • maxMs는 바깥 타이머보다 짧게 지정합니다(예: 바깥 타이머 3000ms, maxMs: 2000). 두 값의 차이는 스크립트 로드 지연에 대한 여유분입니다.
    window.gladsdkCtxCheck = window.gladsdkCtxCheck || { q: [] };

    var done = false;
    function finish(res) {
    if (done) return; // 분석 결과와 바깥 타이머 중 먼저 온 쪽으로 한 번만 요청
    done = true;
    clearTimeout(timer);
    requestBottomAds(res && !res.error ? res : null); // res.context가 있으면 광고 슬롯에 전달
    }
    var timer = setTimeout(function () { finish(null); }, 3000); // 스크립트 미로드 대비 - maxMs보다 길게
    window.gladsdkCtxCheck.q.push(['recommend', { idle: true, maxMs: 2000 }, finish]);
  • 설정을 코드로 지정하려면 스크립트 로드 전에 아래와 같이 선언합니다.

    window.gladsdkCtxCheck = { q: [], config: { root: '#postViewArea', count: 2 } };

8. 성능 및 리소스​

항목값비고
ctx-sdk.js62KB (br 22KB)외부 의존성 없음
키워드 번들 ctx_model2_100k.bin991KB (br 약 655KB)최초 1회 다운로드. 버전 디렉터리 경로라 내용이 바뀌지 않으며, 이후 브라우저 캐시 사용
번들 로드 및 초기화약 120ms최초 recommend() 호출 시 1회
분석동기 250~500ms / idle: true는 페이지 상황에 따라 더 소요maxMs(기본 3초, 로드 포함) 안에 항상 응답
메인 스레드idle: true면 계산 중간마다 양보상단 광고 렌더링과 스크롤을 막지 않음
네트워크스크립트와 번들 다운로드만 발생분석 중 외부 요청 없음. 본문 텍스트는 기기 밖으로 전송되지 않음
  • CSP(Content Security Policy)를 운영하는 지면만 아래 허용 목록을 적용합니다. CSP를 사용하지 않는 지면은 별도 작업이 필요하지 않습니다.
script-src https://sdk.naverads.net
connect-src https://sdk.naverads.net
  • script-src는 gfp-core.js와 ctx-sdk.js 로드에, connect-src는 키워드 번들(ctx_model2_100k.bin) 다운로드에 필요합니다. 키워드 번들은 분석 스크립트와 같은 호스트에서 받습니다.
  • SDK는 eval, new Function을 사용하지 않으므로 unsafe-eval은 필요하지 않습니다.

9. 개인정보 및 브랜드 세이프티(Brand Safety)​

  • 분석은 모두 브라우저 안에서 완료됩니다. 광고 요청에는 context의 요약값(카테고리, 키워드 몇 개, 지역, 데모그래픽 코드)만 포함되며, 본문 원문이나 사용자 식별 정보는 생성하지 않습니다.
  • demo는 본문이 대상으로 하는 독자층(예: F|30S)이며, 방문자를 추정한 값이 아닙니다.
  • 정치, 형사법률, 사고 등 브랜드 세이프티(Brand Safety) 이슈가 검출되면 관련 키워드를 제외하고 safety에 이슈 코드를 포함합니다. 페이지 자체의 광고 노출을 막지는 않으며, 여론조사와 같은 비상업 콘텐츠만 no_fill로 판정됩니다.

10. 적용 확인​

  • 적용한 페이지의 브라우저 개발자 도구 콘솔에서 분석 결과를 확인할 수 있습니다.

    const res = await gladsdk.CtxCheck.recommend({ idle: false });
    console.log(res.status, res.context);
  • 광고 슬롯에 설정된 값은 adSlot getter 함수로 확인할 수 있습니다.

    const adSlot = gladsdk.findAdSlot('ad-mid');
    console.log(adSlot.getContext(), adSlot.getKeyword());

11. 문제 해결​

증상원인조치
gladsdk.CtxCheck is undefined인라인 스크립트가 defer 스크립트보다 먼저 실행됨, async로 삽입됨, 또는 스크립트 로드 실패(차단, 404 등)호출을 DOMContentLoaded 안으로 옮기고, async가 불가피하면 gladsdkCtxCheck.q 선행 큐를 사용합니다(7. 로딩 방식별 주의 사항). 호출 전에 window.gladsdk.CtxCheck 존재 여부를 확인하면 미로드 시에도 광고는 정상 요청됩니다.
항상 status: 'empty'본문이 iframe 안에 있는데 상위 문서에서 실행 / data-root 선택자가 본문과 다름 / 본문이 매우 짧음iframe 문서 안에 스크립트를 삽입합니다. 콘솔에서 document.querySelector(선택자)로 본문이 선택되는지 확인합니다.
키워드가 광고나 추천 목록의 어휘로 추출됨root가 본문보다 넓게 지정됨본문 컨테이너로 범위를 좁힙니다. 자동 추출은 광고와 내비게이션 영역을 제거하지만, 지정한 영역 안은 그대로 분석합니다.
하단 광고가 매번 3초 뒤에 요청됨 (partial: true)본문이 매우 길거나 페이지가 바빠 분석이 지연됨정상 동작이며 3초 시점의 부분 결과가 전달됩니다. 더 빨리 요청하려면 recommend({ maxMs: 1500 })처럼 상한을 낮추거나, 스크롤 진입 시점에 광고 슬롯을 정의합니다.
context가 nullempty / no_fill, 또는 브랜드 세이프티 이슈로 키워드가 모두 제외됨정상 동작이며 context 없이 광고가 요청됩니다. 필요한 경우 keyword를 함께 설정합니다.