adstage
Web SDKโฆษณา

ฟีเจอร์ที่ใช้ร่วมกัน

เอกสารนี้รวบรวม "แนวคิดที่ใช้ร่วมกัน" ซึ่งเคยปรากฏซ้ำในเอกสารของโฆษณาแต่ละประเภทมาไว้ที่เดียว เพื่อให้ดูแลรักษาได้ง่ายขึ้น และเพื่อให้เอกสารของโฆษณาแต่ละประเภทสามารถมุ่งเน้นไปที่ ฟีเจอร์เฉพาะของแต่ละประเภท ได้

🧱 โครงสร้างพื้นฐาน & รูปแบบการเรียกใช้ที่ใช้ร่วมกัน

// 1) SDK 초기화 (앱 전역 1회)
AdStage.init({
  apiKey: 'your-api-key',
  debug: process.env.NODE_ENV === 'development'
});
 
// 2) 컨테이너 DOM 준비 후 호출 (id 또는 Element 모두 허용)
AdStage.ads.banner('banner-container', { /* 옵션 */ });
// AdStage.ads.text(element, { /* 옵션 */ });
// AdStage.ads.video('video-wrapper', { /* 옵션 */ });
 
// 3) 필요 시 수동 제거 (대부분 필요 없음: 자동 정리 지원)
AdStage.ads.destroy(slotId);

รูปแบบที่ใช้ร่วมกันสำหรับ React / Next.js Provider

import { AdStageProvider, useAdStageInstance } from '@adstage/web-sdk';
 
// layout / root
<AdStageProvider config={{ apiKey: process.env.NEXT_PUBLIC_ADSTAGE_API_KEY, debug: true }}>
  {children}
</AdStageProvider>
 
// 개별 컴포넌트
function BannerSlot() {
  const adstage = useAdStageInstance();
  const ref = useRef(null);
  useEffect(() => {
    if (!adstage || !ref.current) return;
    const id = adstage.ads.banner(ref.current, { width: '100%', height: 250 });
    return () => adstage.ads.destroy(id);
  }, [adstage]);
  return <div ref={ref} style={{ height: 250 }} />;
}

⚙️ สรุปตัวเลือกที่ใช้ร่วมกัน

ตัวเลือกประเภทคำอธิบายใช้กับ
onClickfunction(adData)Callback เมื่อคลิกโฆษณาทุกประเภท
adIdstringกำหนดโฆษณาเฉพาะแบบบังคับทุกประเภท
language'ko' | 'en' | 'ja' | 'zh'ตัวกรองภาษาทุกประเภท
deviceType'MOBILE' | 'DESKTOP'ตัวกรองอุปกรณ์ทุกประเภท
countryรหัสประเทศ (ISO2)ตัวกรองประเทศทุกประเภท

สำหรับตัวเลือกเฉพาะของโฆษณาแต่ละประเภท โปรดดูเอกสารที่เกี่ยวข้อง (แบนเนอร์/ข้อความ/วิดีโอ)

🔄 ไลฟ์ไซเคิล & การทำงานภายใน

graph TD
  A[init 호출] --> B[slot 함수 호출]
  B --> C[컨테이너 탐색/재시도]
  C --> D[서버 광고 요청]
  D --> E[비동기 로딩]
  E --> F[렌더링]
  F --> G[노출 추적]
  G --> H[클릭/상호작용 추적]

คุณลักษณะหลัก

  • แบบคืนค่าทันที: slotId จะถูกคืนค่าทันทีก่อนที่เซิร์ฟเวอร์จะตอบกลับ ⇒ ไม่บล็อก UI
  • รองรับคอนเทนเนอร์ที่มาทีหลัง: แม้คอนเทนเนอร์ยังไม่อยู่ใน DOM ก็จะลองใหม่ตามช่วงเวลาหนึ่ง และเมื่อถูกแนบเข้ามาแล้วจึงเรนเดอร์
  • การล้างข้อมูลอัตโนมัติแบบอิงกับ MutationObserver: destroy() จะทำงานอัตโนมัติเมื่อ DOM ถูกลบออก
  • ปลอดภัยกับโฆษณาหลายตัว: เมื่อเรียกคอนเทนเนอร์เดียวกันซ้ำ มีเพียงการเรียกครั้งล่าสุดเท่านั้นที่มีผล (นโยบายของตัวการนำไปใช้งาน)

🧪 Slot Management API

// 모든 슬롯 정보
const all = AdStage.ads.getAllSlots();
 
// 특정 슬롯 조회
const slot = AdStage.ads.getSlotById(slotId);
 
// 수동 제거 (필요한 경우)
AdStage.ads.destroy(slotId);

ออบเจกต์ slot (ฟิลด์จริง):

interface AdSlot {
  id: string;                 // 슬롯 ID (slotId)
  containerId: string;        // 컨테이너 DOM id
  adType: 'BANNER' | 'TEXT' | 'VIDEO' | 'NATIVE' | 'INTERSTITIAL' | 'POPUP';
  width: number | string;     // 숫자(px) 또는 문자열('100%' 등)
  height: number | string;    // 숫자(px) 또는 문자열('auto' 등)
  isLoaded: boolean;          // 광고 로드 완료 여부
  isVisible: boolean;         // 노출 상태
  refreshRate: number;        // 자동 새로고침 주기(초)
  lazyLoad: boolean;          // 지연 로딩 여부
  targeting: Record<string, any>;
 
  // 슬롯 메서드
  load(): Promise<Advertisement | null>;
  render(ad: Advertisement): void;
  refresh(): Promise<void>;
  destroy(): void;
}

🚀 กลยุทธ์ด้านประสิทธิภาพ & การเพิ่มประสิทธิภาพที่ใช้ร่วมกัน

กลยุทธ์คำอธิบายหมายเหตุ
การโหลดเบื้องหลังไม่บล็อกการเรนเดอร์เริ่มต้นด้วยการโหลดแบบอะซิงโครนัสใช้ slotId ได้ทันที
รองรับคอนเทนเนอร์ที่มาทีหลังจัดการกรณีที่ DOM ถูกแทรกเข้ามาภายหลังโดยอัตโนมัติทำให้โค้ดการเริ่มต้นเรียบง่ายขึ้น
การล้างข้อมูลอัตโนมัติป้องกันการรั่วไหลของหน่วยความจำ/อีเวนต์มีประโยชน์เมื่อมีการเปลี่ยนหน้าใน SPA
ปรับแต่ง DOM ให้น้อยที่สุดอัปเดตเฉพาะ DOM ภายในคอนเทนเนอร์เท่านั้นลดผลกระทบต่อเลย์เอาต์ของพาเรนต์
เพิ่มประสิทธิภาพขนาดตามประเภทแบนเนอร์/วิดีโอกำหนดขนาด ส่วนข้อความใช้ความสูงอัตโนมัติลด CLS

เทมเพลตที่ใช้ร่วมกันสำหรับรูปแบบ Responsive

function selectDeviceType() {
  return window.innerWidth <= 768 ? 'MOBILE' : 'DESKTOP';
}
 
const base = { language: 'ko', deviceType: selectDeviceType() };
const id = AdStage.ads.banner('rwd-banner', { width: '100%', height: 250, ...base });
 
window.addEventListener('resize', () => {
  AdStage.ads.destroy(id);
  const next = AdStage.ads.banner('rwd-banner', { width: '100%', height: 250, deviceType: selectDeviceType() });
});

📦 สรุปรูปแบบที่แนะนำสำหรับ React / Next.js

รูปแบบคำอธิบายใช้เมื่อไหร่
การเริ่มต้นแบบ global ผ่าน Providerตั้งค่า SDK เพียงครั้งเดียวApp/RootLayout
Hook (useAdStageInstance)การเข้าถึง SDK อย่างปลอดภัยคอมโพเนนต์ CSR
cleanup ใน useEffectรับประกันการเรียก destroyทำให้อายุการใช้งานของ slot ชัดเจน
สไตล์ Skeletonปรับแต่งสถานะ .adstage-loadingปรับปรุง UX

🎨 คลาส CSS ที่ใช้ร่วมกัน

.adstage-ad, .adstage-text-ad, .adstage-video-ad { position: relative; overflow: hidden; }
.adstage-loading { opacity: .6; transition: opacity .15s; }
.adstage-loaded { opacity: 1; }
.adstage-error { outline: 1px solid #f87171; }

สำหรับสไตล์เพิ่มเติมตามแต่ละประเภท โปรดดูเอกสารแยกของแต่ละประเภท

🛠 การดีบัก & การมอนิเตอร์

การเปิดใช้งานโหมดดีบัก

AdStage.init({ apiKey: 'your-api-key', debug: true });

Click Hook (onClick callback)

SDK จะไม่ปล่อย DOM CustomEvent อย่าง adstage:ad:loaded / adstage:ad:error หากคุณต้องการ hook สำหรับการโต้ตอบกับโฆษณา ให้ส่ง onClick callback ตอนสร้าง slot

AdStage.ads.banner('banner-container', {
  width: '100%',
  height: 250,
  onClick: (adData) => {
    console.log('광고 클릭됨:', adData);
    // 추가 추적/라우팅 로직
  }
});

การตรวจสอบสถานะการโหลด

คุณสามารถตรวจสอบได้ว่าการโหลดเสร็จสมบูรณ์หรือไม่ โดยการ polling ฟิลด์ isLoaded ของ slot (หากเปิดโหมดดีบัก ผลการโหลด/ความล้มเหลวจะถูกพิมพ์ออกมาที่คอนโซล)

const slotId = AdStage.ads.banner('banner-container', { width: '100%', height: 250 });
 
const timer = setInterval(() => {
  const slot = AdStage.ads.getSlotById(slotId);
  if (slot?.isLoaded) {
    console.log('로드 완료:', slot);
    clearInterval(timer);
  }
}, 200);

ยูทิลิตีสำหรับวินิจฉัยสถานะ

function dumpAdStage() {
  const slots = AdStage.ads.getAllSlots();
  console.table(slots.map(s => ({
    id: s.id,
    containerId: s.containerId,
    adType: s.adType,
    isLoaded: s.isLoaded
  })));
}

🔐 ความปลอดภัย & แนวทางปฏิบัติที่ดี

หัวข้อข้อแนะนำ
API Keyสามารถเปิดเผยต่อสาธารณะได้ แต่ควรใช้คีย์ที่มีสิทธิ์น้อยที่สุด
การติดตามอีเวนต์เมื่อเพิ่มการติดตามใน onClick แนะนำให้ส่งแบบอะซิงโครนัสก่อนแล้วจึงทำการ routing
การจัดการข้อผิดพลาดวินิจฉัยความล้มเหลวในการโหลดด้วยล็อกคอนโซลโหมดดีบักและสถานะ isLoaded ของ slot
นโยบายการลองใหม่กรณีการแทรกคอนเทนเนอร์ทีหลังถูกจัดการภายในแล้ว → หลีกเลี่ยงการใช้ setTimeout เพิ่มเติมมากเกินไป

❓FAQ (ที่ใช้ร่วมกัน)

Q. จำเป็นต้องเรียก destroy() เสมอหรือไม่?
A. ส่วนใหญ่ไม่จำเป็น การตรวจจับการลบ DOM จะทำงานโดยอัตโนมัติ อย่างไรก็ตาม หากคุณนำคอนเทนเนอร์เดียวกันไปใช้ซ้ำเพื่อวัตถุประสงค์อื่นทันที แนะนำให้เรียก destroy() ด้วยตนเอง

Q. โฆษณาไม่แสดง แต่ slotId ออกมา
A. อาจกำลังโหลดแบบอะซิงโครนัสอยู่ หรืออาจถูกกรองออกด้วยตัวกรอง (adId / language / deviceType / country) ให้เปิดโหมดดีบักและตรวจสอบล็อกอีเวนต์

Q. ใน SSR (Next.js) จะเรนเดอร์ที่ฝั่งเซิร์ฟเวอร์หรือไม่?
A. ไม่ การเรนเดอร์โฆษณาจะทำที่ฝั่งไคลเอนต์เท่านั้น ให้เรียกใช้ภายในคอมโพเนนต์ use client

Q. หากเรียกซ้ำติดต่อกันที่ตำแหน่งเดียวกันจะเป็นอย่างไร?
A. ตามนโยบายของตัวการนำไปใช้งาน slot เดิมจะถูกลบออก และจะคงสถานะไว้ตามการเรียกครั้งสุดท้าย

สารบัญ