Android
คู่มือการเชื่อมต่อ In-App Event ของ AdStage (Android)
สารบัญ
- ภาพรวม
- การตั้งค่าพื้นฐาน
- การส่งอีเวนต์แบบ Type-Safe
- แค็ตตาล็อกอีเวนต์มาตรฐาน
- ตัวอย่างตามประเภทอีเวนต์
- แนวทางปฏิบัติที่ดี
- การแก้ไขปัญหา
ภาพรวม
In-App Event ของ AdStage ติดตามพฤติกรรมผู้ใช้และอีเวนต์ในแอปเพื่อสนับสนุนการวิเคราะห์และการเพิ่มประสิทธิภาพทางการตลาด
ฟีเจอร์หลัก
- การส่งอีเวนต์ที่ยืดหยุ่น: ตั้งแต่การเรียกใช้แบบง่ายไปจนถึงบริบทที่ละเอียด
- การเก็บบริบทอัตโนมัติ: รวมข้อมูลอุปกรณ์และคุณสมบัติผู้ใช้โดยอัตโนมัติ
- การจัดการเซสชัน: ติดตามและจัดการเซสชันโดยอัตโนมัติ
- Builder pattern: รองรับสไตล์ DSL ที่อ่านง่าย
- การประมวลผลแบบอะซิงโครนัส: ฟังก์ชัน suspend ที่อิงตาม Coroutine
- รองรับออฟไลน์: ส่งซ้ำโดยอัตโนมัติเมื่อเครือข่ายเชื่อมต่อใหม่
การตั้งค่าพื้นฐาน
1. เพิ่ม Gradle Dependencies
settings.gradle.kts
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://repo.nbase.io/repository/nbase-releases") }
}
}build.gradle.kts (Module: app)
dependencies {
implementation("io.nbase:nbase-adapter-adstage:3.0.9")
// 필수 의존성
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
implementation("com.squareup.okhttp3:okhttp:4.11.0")
}2. เริ่มต้นใช้งาน SDK
import io.nbase.adapter.adstage.AdStage
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// AdStage 초기화
AdStage.initialize(
context = this,
apiKey = "your-api-key-here",
serverUrl = "https://api.adstage.app" // 선택사항, 기본값 사용 가능
)
Log.d("AdStage", "✅ SDK 초기화 완료")
}
}3. AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 필수 권한 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name">
<!-- ... -->
</application>
</manifest>การส่งอีเวนต์แบบ Type-Safe
1. วิธีที่ง่ายที่สุด
trackEvent มีทั้งฟังก์ชัน suspend และ overload แบบ callback
เรียกใช้ฟังก์ชัน suspend ภายในขอบเขตของ coroutine และใช้เวอร์ชัน callback เมื่ออยู่นอก coroutine
import io.nbase.adapter.adstage.AdStage
import io.nbase.adapter.adstage.models.AdStageEvent
import io.nbase.adapter.adstage.models.SignUpMethod
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
CoroutineScope(Dispatchers.IO).launch {
// 파라미터 없는 이벤트 (suspend)
AdStage.trackEvent(AdStageEvent.FirstOpen())
// 로그인 이벤트 (method는 문자열, SignUpMethod enum의 value를 사용)
AdStage.trackEvent(
AdStageEvent.Login(method = SignUpMethod.EMAIL.value)
)
}
// 콜백 버전 (코루틴 없이 호출)
AdStage.trackEvent(AdStageEvent.Login(method = SignUpMethod.EMAIL.value)) { result ->
when (result) {
is TrackEventResult.Success -> Log.d("AdStage", "전송 성공")
is TrackEventResult.Failure -> Log.e("AdStage", "전송 실패: ${result.error}")
}
}2. อีเวนต์การซื้อ (ตัวอย่างการตรวจสอบ)
เมื่อมี value จะต้องระบุ currency ด้วย หากขาดหายไป จะเกิด
IllegalArgumentException ณ จุดที่สร้างออบเจ็กต์อีเวนต์ (การตรวจสอบขณะรันไทม์)
// ✅ GOOD: value와 currency 함께 제공
AdStage.trackEvent(
AdStageEvent.Purchase(
value = 29.99,
currency = "USD",
transactionId = "TXN_123456"
)
)
// ❌ BAD: value만 제공 시 런타임 예외 (IllegalArgumentException)
AdStage.trackEvent(
AdStageEvent.Purchase(
value = 29.99 // ❌ 예외: value가 있으면 currency 필수!
)
)
// ✅ GOOD: currency 없이 transactionId만
AdStage.trackEvent(
AdStageEvent.Purchase(
transactionId = "TXN_123456"
)
)3. การดูสินค้า
// 파라미터와 함께
AdStage.trackEvent(
AdStageEvent.ProductDetailsView(
itemId = "PROD_123",
itemName = "Wireless Earbuds"
)
)
// 파라미터 없이
AdStage.trackEvent(AdStageEvent.ProductListView())4. อีเวนต์แบบกำหนดเอง (การเลื่อนระดับ event_name อัตโนมัติ)
เมื่อต้องการส่งอีเวนต์เฉพาะของแอปนอกเหนือจากอีเวนต์มาตรฐาน ให้ใช้ AdStageEvent.Custom
หากคุณใส่คีย์ event_name ในพารามิเตอร์ ค่านั้นจะถูกเลื่อนระดับขึ้นเป็นชื่ออีเวนต์จริงโดยอัตโนมัติและบันทึกลงในแดชบอร์ด
// 예: 'promotion_click'이라는 커스텀 이벤트 전송
AdStage.trackEvent(
AdStageEvent.Custom(
params = mapOf(
"event_name" to "promotion_click",
"promotion_id" to "summer_sale_2025",
"screen" to "home_banner"
)
)
)
// 대시보드에는 "promotion_click" 이벤트로 수집됨
// event_name 없이 일반 커스텀 이벤트
AdStage.trackEvent(
AdStageEvent.Custom(
params = mapOf(
"action" to "button_click",
"button_id" to "promo_banner",
"screen" to "home",
"timestamp" to System.currentTimeMillis()
)
)
)
// 대시보드에는 "custom" 이벤트로 수집됨
// Click, View도 자유 파라미터 지원
AdStage.trackEvent(
AdStageEvent.Click(
params = mapOf(
"campaign_id" to "SUMMER2025",
"ad_group" to "electronics"
)
)
)แค็ตตาล็อกอีเวนต์มาตรฐาน
AdStage SDK มีอีเวนต์มาตรฐาน 46 รายการ
📌 การติดตามโฆษณา (4 รายการ)
// 1. 광고 클릭
AdStage.trackEvent(
AdStageEvent.Click(
params = mapOf("campaign_id" to "CAMP_123")
)
)
// 2. 광고 노출
AdStage.trackEvent(
AdStageEvent.View(
params = mapOf("impression_id" to "IMP_456")
)
)
// 3. 앱 설치
AdStage.trackEvent(AdStageEvent.Install())
// 4. 커스텀 이벤트 (event_name으로 이벤트 이름 지정)
AdStage.trackEvent(
AdStageEvent.Custom(
params = mapOf(
"event_name" to "promotion_click",
"promotion_id" to "summer_sale_2025"
)
)
)👤 วงจรชีวิตของผู้ใช้ (5 รายการ)
method ของ SignUp และ Login เป็นพารามิเตอร์ชนิดสตริง (String?) คุณสามารถส่ง
สตริงโดยตรง หรือใช้ .value ของ enum SignUpMethod ที่ SDK มีให้ก็ได้
// SDK 제공 SignUpMethod enum (참고용)
enum class SignUpMethod(val value: String) {
EMAIL("email"),
GOOGLE("google"),
APPLE("apple"),
FACEBOOK("facebook"),
KAKAO("kakao"),
NAVER("naver")
}
// 1. 회원가입 완료
AdStage.trackEvent(
AdStageEvent.SignUp(method = SignUpMethod.GOOGLE.value)
)
// 2. 회원가입 시작
AdStage.trackEvent(AdStageEvent.SignUpStart())
// 3. 로그인 (문자열 직접 사용도 가능: method = "email")
AdStage.trackEvent(
AdStageEvent.Login(method = SignUpMethod.EMAIL.value)
)
// 4. 로그아웃
AdStage.trackEvent(AdStageEvent.Logout())
// 5. 앱 최초 실행
AdStage.trackEvent(AdStageEvent.FirstOpen())📄 การดูเนื้อหา (6 รายการ)
// 1. 홈 화면
AdStage.trackEvent(AdStageEvent.HomeView())
// 2. 상품 목록
AdStage.trackEvent(
AdStageEvent.ProductListView(itemCategory = "electronics")
)
// 3. 검색 결과
AdStage.trackEvent(
AdStageEvent.SearchResultView(searchTerm = "wireless headphones")
)
// 4. 상품 상세
AdStage.trackEvent(
AdStageEvent.ProductDetailsView(
itemId = "PROD_123",
itemName = "Wireless Earbuds"
)
)
// 5. 페이지 조회 (웹)
AdStage.trackEvent(
AdStageEvent.PageView(
pageUrl = "https://example.com/products",
pageTitle = "Products"
)
)
// 6. 화면 조회 (앱)
AdStage.trackEvent(
AdStageEvent.ScreenView(
screenName = "product_detail",
screenClass = "ProductDetailActivity"
)
)🛒 อีคอมเมิร์ซ (7 รายการ)
EcommerceItem มีเพียง 4 ฟิลด์: itemId, itemName, price, quantity
// 1. 장바구니 추가
AdStage.trackEvent(
AdStageEvent.AddToCart(
value = 99000.0,
currency = "KRW",
items = listOf(
EcommerceItem(
itemId = "PROD_123",
itemName = "Wireless Earbuds",
price = 99000.0,
quantity = 1
)
)
)
)
// 2. 장바구니 제거
AdStage.trackEvent(
AdStageEvent.RemoveFromCart(
value = 50000.0,
currency = "KRW"
)
)
// 3. 위시리스트 추가
AdStage.trackEvent(
AdStageEvent.AddToWishlist(
itemId = "PROD_456",
itemName = "Smart Watch"
)
)
// 4. 결제 정보 입력
AdStage.trackEvent(
AdStageEvent.AddPaymentInfo(paymentType = "credit_card")
)
// 5. 결제 시작
AdStage.trackEvent(
AdStageEvent.BeginCheckout(
value = 150000.0,
currency = "KRW"
)
)
// 6. 구매 완료 ⭐⭐⭐
AdStage.trackEvent(
AdStageEvent.Purchase(
value = 129000.0,
currency = "KRW",
transactionId = "ORDER_20250105_001",
tax = 12900.0,
shipping = 3000.0,
coupon = "SUMMER2025",
items = listOf(
EcommerceItem(
itemId = "PROD_123",
itemName = "Wireless Earbuds",
price = 126000.0,
quantity = 1
)
)
)
)
// 7. 환불
AdStage.trackEvent(
AdStageEvent.Refund(
transactionId = "ORDER_20250105_001",
value = 129000.0,
currency = "KRW"
)
)🎮 ความคืบหน้า/ความสำเร็จ (4 รายการ)
// 1. 튜토리얼 시작
AdStage.trackEvent(
AdStageEvent.TutorialBegin(
params = mapOf("tutorial_id" to "intro")
)
)
// 2. 튜토리얼 완료
AdStage.trackEvent(
AdStageEvent.TutorialComplete(
params = mapOf("duration_seconds" to 120)
)
)
// 3. 레벨 업
AdStage.trackEvent(
AdStageEvent.LevelUp(
level = 25,
character = "warrior"
)
)
// 4. 업적 달성
AdStage.trackEvent(
AdStageEvent.Achievement(achievementId = "first_win")
)💬 การโต้ตอบ (7 รายการ)
// 1. 검색
AdStage.trackEvent(
AdStageEvent.Search(searchTerm = "gaming laptop")
)
// 2. 콘텐츠 선택
AdStage.trackEvent(
AdStageEvent.SelectContent(
contentType = "product",
contentId = "PROD_789"
)
)
// 3. 공유
AdStage.trackEvent(
AdStageEvent.Share(
contentType = "product",
method = "kakao"
)
)
// 4. 좋아요
AdStage.trackEvent(
AdStageEvent.Like(
contentType = "product",
contentId = "PROD_789"
)
)
// 5. 평가
AdStage.trackEvent(
AdStageEvent.Rate(
contentType = "product",
contentId = "PROD_789",
score = 4.5
)
)
// 6. 일정 등록
AdStage.trackEvent(
AdStageEvent.Schedule(
params = mapOf("event_type" to "appointment")
)
)
// 7. 크레딧 사용
AdStage.trackEvent(
AdStageEvent.SpendCredits(
value = 10.0,
itemName = "premium_feature"
)
)📢 โฆษณาในแอป (2 รายการ)
// 1. 인앱 광고 노출
AdStage.trackEvent(
AdStageEvent.AdImpression(
adPlatform = "admob",
adFormat = "rewarded_video",
adUnitName = "main_reward"
)
)
// 2. 인앱 광고 클릭
AdStage.trackEvent(
AdStageEvent.AdClick(
adPlatform = "admob",
adFormat = "banner",
adUnitName = "home_bottom"
)
)🎮 เฉพาะเกม (4 รายการ)
// 1. 게임 플레이
AdStage.trackEvent(
AdStageEvent.GamePlay(
level = 10,
levelName = "Dragon's Lair",
character = "mage",
contentType = "dungeon"
)
)
// 2. 보너스 획득
AdStage.trackEvent(
AdStageEvent.AcquireBonus(
contentType = "reward",
itemId = "ITEM_123",
itemName = "Gold Chest",
quantity = 1
)
)
// 3. 게임 서버 선택
AdStage.trackEvent(
AdStageEvent.SelectGameServer(
contentId = "SERVER_01",
contentType = "pvp",
itemName = "Asia Server"
)
)
// 4. 패치 완료
AdStage.trackEvent(
AdStageEvent.CompletePatch(
contentId = "PATCH_2.1.0",
contentType = "update"
)
)📅 การสมัครสมาชิก/ทดลองใช้ (3 รายการ)
// 1. 무료 체험 시작
AdStage.trackEvent(
AdStageEvent.StartTrial(
value = 9900.0,
currency = "KRW",
trialDays = 14
)
)
// 2. 구독 시작
AdStage.trackEvent(
AdStageEvent.Subscribe(
value = 9900.0,
currency = "KRW",
subscriptionId = "premium_monthly"
)
)
// 3. 구독 취소
AdStage.trackEvent(
AdStageEvent.Unsubscribe(subscriptionId = "premium_monthly")
)💰 เฉพาะการเงิน (4 รายการ)
// 1. 주식 매수
AdStage.trackEvent(
AdStageEvent.BuyStock(
itemId = "005930",
itemName = "삼성전자",
quantity = 10,
price = 70000.0,
value = 700000.0,
currency = "KRW"
)
)
// 2. 주식 매도
AdStage.trackEvent(
AdStageEvent.SellStock(
itemId = "005930",
itemName = "삼성전자",
quantity = 5,
price = 72000.0,
value = 360000.0,
currency = "KRW"
)
)
// 3. 계좌 개설 완료
AdStage.trackEvent(
AdStageEvent.CompleteOpenAccount(
contentType = "증권계좌",
contentId = "ACC_123",
method = "mobile"
)
)
// 4. 카드 신청
AdStage.trackEvent(
AdStageEvent.ApplyCard(
contentType = "신용카드",
itemName = "프리미엄 카드",
itemId = "CARD_001"
)
)ตัวอย่างตามประเภทอีเวนต์
1. วงจรชีวิตของแอป
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// SDK 초기화
AdStage.initialize(this, apiKey, serverUrl)
// 첫 실행 이벤트는 SDK가 자동으로 전송
Log.d("App", "✅ 앱 시작")
}
}2. การยืนยันตัวตนผู้ใช้
class AuthManager {
// 회원가입 (method는 문자열 파라미터)
fun onSignUpComplete(method: String) {
AdStage.trackEvent(
AdStageEvent.SignUp(method = method)
)
Log.d("Auth", "✅ 회원가입 완료: $method")
}
// 로그인
fun onLoginSuccess(userId: String, method: String) {
AdStage.trackEvent(
AdStageEvent.Login(method = method)
)
Log.d("Auth", "✅ 로그인: $userId")
}
// 로그아웃
fun onLogout() {
AdStage.trackEvent(AdStageEvent.Logout())
Log.d("Auth", "🚪 로그아웃")
}
}3. การติดตามหน้าจอ (รูปแบบ BaseActivity)
abstract class BaseActivity : AppCompatActivity() {
override fun onResume() {
super.onResume()
trackScreenView()
}
abstract fun getScreenName(): String
private fun trackScreenView() {
AdStage.trackEvent(
AdStageEvent.ScreenView(
screenName = getScreenName(),
screenClass = this::class.java.simpleName
)
)
Log.d("Screen", "📺 ${getScreenName()}")
}
}
// 사용 예제
class HomeActivity : BaseActivity() {
override fun getScreenName() = "home"
}
class ProductDetailActivity : BaseActivity() {
override fun getScreenName() = "product_detail"
private fun loadProduct(productId: String) {
// 상품 데이터 로딩...
// 상품 상세 조회 이벤트
AdStage.trackEvent(
AdStageEvent.ProductDetailsView(
itemId = productId,
itemName = product.name
)
)
}
}4. โฟลว์อีคอมเมิร์ซแบบครบวงจร
class EcommerceManager {
// 1. 상품 목록 조회
fun trackProductList(category: String) {
AdStage.trackEvent(
AdStageEvent.ProductListView(itemCategory = category)
)
}
// 2. 검색
fun trackSearch(query: String) {
AdStage.trackEvent(
AdStageEvent.Search(searchTerm = query)
)
}
// 3. 검색 결과 조회
fun trackSearchResults(query: String) {
AdStage.trackEvent(
AdStageEvent.SearchResultView(searchTerm = query)
)
}
// 4. 상품 상세 조회
fun trackProductView(product: Product) {
AdStage.trackEvent(
AdStageEvent.ProductDetailsView(
itemId = product.id,
itemName = product.name
)
)
}
// 5. 장바구니 추가
fun trackAddToCart(product: Product, quantity: Int) {
val item = EcommerceItem(
itemId = product.id,
itemName = product.name,
price = product.price,
quantity = quantity
)
AdStage.trackEvent(
AdStageEvent.AddToCart(
value = product.price * quantity,
currency = "KRW",
items = listOf(item)
)
)
}
// 6. 위시리스트 추가
fun trackAddToWishlist(product: Product) {
AdStage.trackEvent(
AdStageEvent.AddToWishlist(
itemId = product.id,
itemName = product.name
)
)
}
// 7. 결제 시작
fun trackBeginCheckout(cart: Cart) {
val items = cart.items.map { cartItem ->
EcommerceItem(
itemId = cartItem.product.id,
itemName = cartItem.product.name,
price = cartItem.product.price,
quantity = cartItem.quantity
)
}
AdStage.trackEvent(
AdStageEvent.BeginCheckout(
value = cart.totalAmount,
currency = "KRW",
items = items
)
)
}
// 8. 결제 정보 입력
fun trackAddPaymentInfo(paymentMethod: String) {
AdStage.trackEvent(
AdStageEvent.AddPaymentInfo(paymentType = paymentMethod)
)
}
// 9. 구매 완료 ⭐⭐⭐
fun trackPurchase(order: Order) {
val items = order.items.map { orderItem ->
EcommerceItem(
itemId = orderItem.product.id,
itemName = orderItem.product.name,
price = orderItem.product.price,
quantity = orderItem.quantity
)
}
AdStage.trackEvent(
AdStageEvent.Purchase(
value = order.totalAmount,
currency = "KRW",
transactionId = order.id,
tax = order.tax,
shipping = order.shippingFee,
coupon = order.couponCode,
items = items
)
)
Log.d("Ecommerce", "✅ 구매 완료: ${order.id}, ${order.totalAmount}원")
}
// 10. 환불
fun trackRefund(order: Order) {
AdStage.trackEvent(
AdStageEvent.Refund(
transactionId = order.id,
value = order.totalAmount,
currency = "KRW"
)
)
}
// 11. 공유
fun trackShare(product: Product, method: String) {
AdStage.trackEvent(
AdStageEvent.Share(
contentType = "product",
method = method
)
)
}
}5. อีเวนต์เกม
class GameEventManager {
// 튜토리얼
fun trackTutorialBegin(tutorialId: String) {
AdStage.trackEvent(
AdStageEvent.TutorialBegin(
params = mapOf("tutorial_id" to tutorialId)
)
)
}
fun trackTutorialComplete(tutorialId: String, duration: Long) {
AdStage.trackEvent(
AdStageEvent.TutorialComplete(
params = mapOf(
"tutorial_id" to tutorialId,
"duration_seconds" to duration / 1000
)
)
)
}
// 레벨
fun trackLevelUp(level: Int, character: String) {
AdStage.trackEvent(
AdStageEvent.LevelUp(
level = level,
character = character
)
)
}
// 업적
fun trackAchievement(achievementId: String) {
AdStage.trackEvent(
AdStageEvent.Achievement(achievementId = achievementId)
)
}
// 게임 플레이
fun trackGameStart(level: Int, levelName: String, character: String) {
AdStage.trackEvent(
AdStageEvent.GamePlay(
level = level,
levelName = levelName,
character = character,
contentType = "pvp"
)
)
}
// 보너스 획득
fun trackBonusAcquired(itemId: String, itemName: String, quantity: Int) {
AdStage.trackEvent(
AdStageEvent.AcquireBonus(
contentType = "reward",
itemId = itemId,
itemName = itemName,
quantity = quantity
)
)
}
// 크레딧(재화) 사용
fun trackSpendCredits(amount: Double, itemName: String) {
AdStage.trackEvent(
AdStageEvent.SpendCredits(
value = amount,
itemName = itemName
)
)
}
}แนวทางปฏิบัติที่ดี
1. ใช้ประโยชน์จาก Extension Functions
// ActivityExtensions.kt
fun Activity.trackScreen() {
val screenName = this::class.java.simpleName
.removeSuffix("Activity")
.lowercase()
AdStage.trackEvent(
AdStageEvent.ScreenView(
screenName = screenName,
screenClass = this::class.java.simpleName
)
)
}
// 사용
class ProfileActivity : AppCompatActivity() {
override fun onResume() {
super.onResume()
trackScreen() // ✅ 간단!
}
}2. ติดตามอีเวนต์ใน ViewModel
class ProductViewModel : ViewModel() {
private val _productState = MutableLiveData<Product>()
val productState: LiveData<Product> = _productState
fun loadProduct(productId: String) {
viewModelScope.launch {
try {
val product = repository.getProduct(productId)
_productState.value = product
// 상품 조회 이벤트
AdStage.trackEvent(
AdStageEvent.ProductDetailsView(
itemId = product.id,
itemName = product.name
)
)
} catch (e: Exception) {
Log.e("ProductVM", "Failed to load product", e)
}
}
}
fun addToCart(product: Product, quantity: Int) {
viewModelScope.launch {
try {
repository.addToCart(product, quantity)
val item = EcommerceItem(
itemId = product.id,
itemName = product.name,
price = product.price,
quantity = quantity
)
AdStage.trackEvent(
AdStageEvent.AddToCart(
value = product.price * quantity,
currency = "KRW",
items = listOf(item)
)
)
} catch (e: Exception) {
Log.e("ProductVM", "Failed to add to cart", e)
}
}
}
}3. ใช้งานใน Compose UI
@Composable
fun ProductDetailScreen(
productId: String,
viewModel: ProductViewModel = hiltViewModel()
) {
val product by viewModel.productState.collectAsState()
// 화면 진입 시 이벤트
LaunchedEffect(productId) {
viewModel.loadProduct(productId)
AdStage.trackEvent(
AdStageEvent.ScreenView(
screenName = "product_detail",
screenClass = "ProductDetailScreen"
)
)
}
Column {
// UI...
Button(
onClick = {
viewModel.addToCart(product, 1)
AdStage.trackEvent(
AdStageEvent.Custom(
params = mapOf(
"action" to "add_to_cart_button",
"product_id" to product.id
)
)
)
}
) {
Text("장바구니에 추가")
}
}
}4. คลาส Wrapper สำหรับอีเวนต์
object AnalyticsManager {
private const val TAG = "Analytics"
fun trackScreen(screenName: String, screenClass: String? = null) {
AdStage.trackEvent(
AdStageEvent.ScreenView(
screenName = screenName,
screenClass = screenClass
)
)
Log.d(TAG, "📺 Screen: $screenName")
}
fun trackPurchase(order: Order) {
val items = order.items.map {
EcommerceItem(
itemId = it.product.id,
itemName = it.product.name,
price = it.product.price,
quantity = it.quantity
)
}
AdStage.trackEvent(
AdStageEvent.Purchase(
value = order.totalAmount,
currency = "KRW",
transactionId = order.id,
items = items
)
)
Log.d(TAG, "💰 Purchase: ${order.id}")
}
fun trackError(error: Throwable, context: String) {
AdStage.trackEvent(
AdStageEvent.Custom(
params = mapOf(
"event_type" to "error",
"error_message" to (error.message ?: "unknown"),
"context" to context
)
)
)
Log.e(TAG, "❌ Error: $context", error)
}
}5. การเพิ่มประสิทธิภาพ: Throttling
class ThrottledEventTracker {
private val lastTrackTimes = mutableMapOf<String, Long>()
private val throttleInterval = 2000L // 2초
fun trackEvent(event: AdStageEvent) {
val eventKey = event.eventName
val now = System.currentTimeMillis()
val lastTime = lastTrackTimes[eventKey] ?: 0L
if (now - lastTime >= throttleInterval) {
AdStage.trackEvent(event)
lastTrackTimes[eventKey] = now
} else {
Log.d("AdStage", "⏱️ Throttled: $eventKey")
}
}
}
// 사용 (스크롤 이벤트 등)
val throttledTracker = ThrottledEventTracker()
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
throttledTracker.trackEvent(
AdStageEvent.Custom(
params = mapOf(
"action" to "scroll",
"delta_y" to dy
)
)
)
}
})ตัวอย่างการแปลง (อิงสตริง → API แบบ Type-Safe)
ปัจจุบัน SDK รองรับเฉพาะอีเวนต์แบบ Type-Safe AdStageEvent เท่านั้น ด้านล่างเป็นตัวอย่างการ
แปลงชื่ออีเวนต์ที่อิงสตริงให้เป็นอีเวนต์แบบ Type-Safe (รูปแบบทางขวาของลูกศรคือรูปแบบการใช้งานจริง)
// 1. 기본 이벤트
"first_open"
→ AdStage.trackEvent(AdStageEvent.FirstOpen())
// 2. 로그인 (method는 문자열 파라미터)
"login", method = "email"
→ AdStage.trackEvent(AdStageEvent.Login(method = "email"))
// 3. 화면 조회
"screen_view", screen_name = "home"
→ AdStage.trackEvent(AdStageEvent.ScreenView(screenName = "home"))
// 4. 커스텀
"custom", action = "click"
→ AdStage.trackEvent(AdStageEvent.Custom(params = mapOf("action" to "click")))การแก้ไขปัญหา
1. ข้อยกเว้นขณะรันไทม์: "เมื่อมี value จะต้องระบุ currency ด้วย"
ปัญหา:
// ❌ IllegalArgumentException (이벤트 객체 생성 시점)
AdStage.trackEvent(
AdStageEvent.Purchase(value = 9900.0)
)วิธีแก้ไข:
// ✅ currency 추가 (ISO 4217, 3자리)
AdStage.trackEvent(
AdStageEvent.Purchase(
value = 9900.0,
currency = "KRW"
)
)2. การเติมข้อความอัตโนมัติของ IDE ไม่ทำงาน
วิธีแก้ไข:
- Gradle Sync:
File → Sync Project with Gradle Files - Invalidate Caches:
File → Invalidate Caches / Restart... - ตรวจสอบ import:
import io.nbase.adapter.adstage.models.AdStageEvent.*
// 이제 자동완성 작동
AdStage.trackEvent(Purchase(...))3. ปัญหาการทำ Obfuscation ของ ProGuard/R8
# proguard-rules.pro
# AdStage 이벤트 모델 보존
-keep class io.nbase.adapter.adstage.models.** { *; }
-keepclassmembers class io.nbase.adapter.adstage.models.** { *; }
# AdStageEvent sealed class
-keep class io.nbase.adapter.adstage.models.AdStageEvent { *; }
-keep class io.nbase.adapter.adstage.models.AdStageEvent$* { *; }
# Kotlin Metadata
-keep class kotlin.Metadata { *; }
4. ข้อผิดพลาดการตรวจสอบรหัสสกุลเงิน
// ❌ 2자리 코드
AdStageEvent.Purchase(value = 100.0, currency = "KR")
// IllegalArgumentException: "currency는 ISO 4217 3자리 코드여야 합니다 (예: USD, KRW)"
// ✅ 3자리 ISO 4217 코드
AdStageEvent.Purchase(value = 100.0, currency = "KRW") // 한국 원
AdStageEvent.Purchase(value = 100.0, currency = "USD") // 미국 달러
AdStageEvent.Purchase(value = 100.0, currency = "JPY") // 일본 엔รหัสสกุลเงินหลัก:
| ประเทศ | สกุลเงิน | รหัส |
|---|---|---|
| เกาหลีใต้ | วอน | KRW |
| สหรัฐอเมริกา | ดอลลาร์ | USD |
| ญี่ปุ่น | เยน | JPY |
| สหภาพยุโรป | ยูโร | EUR |
| สหราชอาณาจักร | ปอนด์ | GBP |
5. การตรวจสอบบันทึกดีบัก
SDK จะแสดงบันทึกด้วยแท็ก AdStage / AdapterAdStage ใช้ตัวกรองด้านล่างเพื่อ
ตรวจสอบโฟลว์การส่งอีเวนต์ใน Logcat
ตัวกรอง Logcat:
# Android Studio
tag:AdStage OR tag:AdapterAdStage
# adb
adb logcat | grep -i adstageเอกสารอ้างอิง
การอ้างอิงมาตรฐาน
คำถามที่พบบ่อย
Q: จะส่งอีเวนต์แบบกำหนดเองได้อย่างไร?
A: ใช้ AdStageEvent.Custom(params = mapOf(...))
Q: ใช้ currency โดยไม่มี value ได้ไหม?
A: ไม่ได้ เมื่อใช้ value จะต้องระบุ currency ด้วย
Q: ทำงานแบบออฟไลน์ได้ไหม?
A: ได้ SDK จะจัดคิวอีเวนต์โดยอัตโนมัติและส่งเมื่อเครือข่ายกลับมาทำงาน
การสนับสนุน
ติดต่อ:
- อีเมล: support@nbase.io
© 2025 NBase. All rights reserved.

