StageUp
모바일 SDK인앱 이벤트

iOS

AdStage 인앱 이벤트 통합 가이드 (iOS)

목차

  1. 개요
  2. 기본 설정
  3. 기본 이벤트 전송
  4. 사용자 및 디바이스 정보
  5. 이벤트 타입별 예제
  6. 베스트 프랙티스
  7. 트러블슈팅

개요

AdStage 인앱 이벤트는 사용자 행동과 앱 이벤트를 추적하여 마케팅 분석 및 최적화를 지원합니다.

주요 기능

  • 전역 컨텍스트 관리: 사용자/디바이스 정보를 한 번 설정하면 자동 포함
  • 간단한 API: 이벤트 이름과 파라미터만으로 간편하게 전송
  • 자동 세션 추적: 세션 ID 자동 생성 및 관리
  • 비동기 처리: 네트워크 통신이 UI를 블록하지 않음
  • 오프라인 지원: 네트워크 복구 시 자동 재전송

기본 설정

CocoaPods 설정

Podfile

platform :ios, '12.0'
 
target 'YourApp' do
  use_frameworks!
  
  # AdStage SDK
  pod 'AdapterAdStage', '3.0.5'
  
  # 의존성 (자동 설치됨)
  # Alamofire, SwiftyJSON 등
end
 
post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
    end
  end
end

설치:

pod install

2. SDK 초기화

import AdapterAdStage
 
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        
        // AdStage 초기화
        AdStageManager.shared.initialize(
            apiKey: "your-api-key-here",
            serverUrl: "https://api.adstage.app"
        )
        
        print("✅ AdStage SDK 초기화 완료")
        
        return true
    }
}

3. Info.plist 설정

<!-- 네트워크 사용 권한 -->
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <false/>
</dict>

타입 안전 이벤트 전송

1. 가장 간단한 방식

import AdapterAdStage
 
// 파라미터 없는 이벤트
AdStageManager.shared.trackEvent(AdStageEvent.FirstOpen())
 
// 로그인 이벤트
AdStageManager.shared.trackEvent(
    AdStageEvent.Login(method: .email)
)

2. 구매 이벤트 (검증 예제)

// ✅ GOOD: value와 currency 함께 제공
AdStageManager.shared.trackEvent(
    AdStageEvent.Purchase(
        value: 29.99,
        currency: "USD",
        transactionId: "TXN_123456"
    )
)
 
// ❌ BAD: value만 제공 시 컴파일 에러!
AdStageManager.shared.trackEvent(
    AdStageEvent.Purchase(
        value: 29.99  // ❌ 에러: currency 필수!
    )
)
 
// ✅ GOOD: currency 없이 transactionId만
AdStageManager.shared.trackEvent(
    AdStageEvent.Purchase(
        transactionId: "TXN_123456"
    )
)

3. 상품 조회

// 파라미터와 함께
AdStageManager.shared.trackEvent(
    AdStageEvent.ProductDetailsView(
        itemId: "PROD_123",
        itemName: "Wireless Earbuds"
    )
)
 
// 파라미터 없이
AdStageManager.shared.trackEvent(AdStageEvent.ProductListView())

4. 커스텀 이벤트 (event_name 자동 승격)

표준 이벤트 외에 앱 고유의 이벤트를 전송할 때는 AdStageEvent.Custom을 사용합니다. 파라미터에 event_name 키를 포함하면, 해당 값이 실제 이벤트 이름으로 자동 승격되어 대시보드에 저장됩니다.

// 예: 'promotion_click'이라는 커스텀 이벤트 전송
AdStageManager.shared.trackEvent(
    AdStageEvent.Custom(
        params: [
            "event_name": "promotion_click",
            "promotion_id": "summer_sale_2025",
            "screen": "home_banner"
        ]
    )
)
// 대시보드에는 "promotion_click" 이벤트로 수집됨
 
// event_name 없이 일반 커스텀 이벤트
AdStageManager.shared.trackEvent(
    AdStageEvent.Custom(
        params: [
            "action": "button_click",
            "button_id": "promo_banner",
            "screen": "home",
            "timestamp": Date().timeIntervalSince1970
        ]
    )
)
// 대시보드에는 "custom" 이벤트로 수집됨
 
// Click, View도 자유 파라미터 지원
AdStageManager.shared.trackEvent(
    AdStageEvent.Click(
        params: [
            "campaign_id": "SUMMER2025",
            "ad_group": "electronics"
        ]
    )
)

표준 이벤트 카탈로그

AdStage SDK는 46개의 표준 이벤트를 제공합니다.

📌 광고 추적 (4개)

// 1. 광고 클릭
AdStageManager.shared.trackEvent(
    AdStageEvent.Click(
        params: ["campaign_id": "CAMP_123"]
    )
)
 
// 2. 광고 노출
AdStageManager.shared.trackEvent(
    AdStageEvent.View(
        params: ["impression_id": "IMP_456"]
    )
)
 
// 3. 앱 설치
AdStageManager.shared.trackEvent(AdStageEvent.Install())
 
// 4. 커스텀 이벤트 (event_name으로 이벤트 이름 지정)
AdStageManager.shared.trackEvent(
    AdStageEvent.Custom(
        params: [
            "event_name": "promotion_click",
            "promotion_id": "summer_sale_2025"
        ]
    )
)

👤 사용자 라이프사이클 (5개)

// SignUpMethod enum
public enum SignUpMethod: String {
    case email = "email"
    case google = "google"
    case apple = "apple"
    case facebook = "facebook"
    case kakao = "kakao"
    case naver = "naver"
}
 
// 1. 회원가입 완료
AdStageManager.shared.trackEvent(
    AdStageEvent.SignUp(method: .google)
)
 
// 2. 회원가입 시작
AdStageManager.shared.trackEvent(AdStageEvent.SignUpStart())
 
// 3. 로그인
AdStageManager.shared.trackEvent(
    AdStageEvent.Login(method: .email)
)
 
// 4. 로그아웃
AdStageManager.shared.trackEvent(AdStageEvent.Logout())
 
// 5. 앱 최초 실행
AdStageManager.shared.trackEvent(AdStageEvent.FirstOpen())

📄 콘텐츠 조회 (6개)

// 1. 홈 화면
AdStageManager.shared.trackEvent(AdStageEvent.HomeView())
 
// 2. 상품 목록
AdStageManager.shared.trackEvent(
    AdStageEvent.ProductListView(itemCategory: "electronics")
)
 
// 3. 검색 결과
AdStageManager.shared.trackEvent(
    AdStageEvent.SearchResultView(searchTerm: "wireless headphones")
)
 
// 4. 상품 상세
AdStageManager.shared.trackEvent(
    AdStageEvent.ProductDetailsView(
        itemId: "PROD_123",
        itemName: "Wireless Earbuds"
    )
)
 
// 5. 페이지 조회 (웹)
AdStageManager.shared.trackEvent(
    AdStageEvent.PageView(
        pageUrl: "https://example.com/products",
        pageTitle: "Products"
    )
)
 
// 6. 화면 조회 (앱)
AdStageManager.shared.trackEvent(
    AdStageEvent.ScreenView(
        screenName: "product_detail",
        screenClass: "ProductDetailViewController"
    )
)

🛒 전자상거래 (8개)

// 1. 장바구니 추가
AdStageManager.shared.trackEvent(
    AdStageEvent.AddToCart(
        value: 99000.0,
        currency: "KRW",
        items: [
            EcommerceItem(
                itemId: "PROD_123",
                itemName: "Wireless Earbuds",
                price: 99000.0,
                quantity: 1
            )
        ]
    )
)
 
// 2. 장바구니 제거
AdStageManager.shared.trackEvent(
    AdStageEvent.RemoveFromCart(
        value: 50000.0,
        currency: "KRW"
    )
)
 
// 3. 위시리스트 추가
AdStageManager.shared.trackEvent(
    AdStageEvent.AddToWishlist(
        itemId: "PROD_456",
        itemName: "Smart Watch"
    )
)
 
// 4. 결제 정보 입력
AdStageManager.shared.trackEvent(
    AdStageEvent.AddPaymentInfo(paymentType: "credit_card")
)
 
// 5. 결제 시작
AdStageManager.shared.trackEvent(
    AdStageEvent.BeginCheckout(
        value: 150000.0,
        currency: "KRW"
    )
)
 
// 6. 구매 완료 ⭐⭐⭐
AdStageManager.shared.trackEvent(
    AdStageEvent.Purchase(
        value: 129000.0,
        currency: "KRW",
        transactionId: "ORDER_20250105_001",
        tax: 12900.0,
        shipping: 3000.0,
        coupon: "SUMMER2025",
        items: [
            EcommerceItem(
                itemId: "PROD_123",
                itemName: "Wireless Earbuds",
                itemCategory: "electronics",
                price: 126000.0,
                quantity: 1
            )
        ]
    )
)
 
// 7. 환불
AdStageManager.shared.trackEvent(
    AdStageEvent.Refund(
        transactionId: "ORDER_20250105_001",
        value: 129000.0,
        currency: "KRW"
    )
)

🎮 진행/성취 (4개)

// 1. 튜토리얼 시작
AdStageManager.shared.trackEvent(
    AdStageEvent.TutorialBegin(
        params: ["tutorial_id": "intro"]
    )
)
 
// 2. 튜토리얼 완료
AdStageManager.shared.trackEvent(
    AdStageEvent.TutorialComplete(
        params: ["duration_seconds": 120]
    )
)
 
// 3. 레벨 업
AdStageManager.shared.trackEvent(
    AdStageEvent.LevelUp(
        level: 25,
        character: "warrior"
    )
)
 
// 4. 업적 달성
AdStageManager.shared.trackEvent(
    AdStageEvent.Achievement(achievementId: "first_win")
)

💬 상호작용 (3개)

// 1. 검색
AdStageManager.shared.trackEvent(
    AdStageEvent.Search(searchTerm: "gaming laptop")
)
 
// 2. 공유
AdStageManager.shared.trackEvent(
    AdStageEvent.Share(
        contentType: "product",
        itemId: "PROD_789",
        method: "kakao"
    )
)
 
// 3. 광고 클릭
AdStageManager.shared.trackEvent(
    AdStageEvent.AdClick(adId: "AD_12345")
)

🎮 게임 특화 (4개)

// 1. 게임 플레이
AdStageManager.shared.trackEvent(
    AdStageEvent.GamePlay(
        level: 10,
        levelName: "Dragon's Lair",
        character: "mage",
        contentType: "dungeon"
    )
)
 
// 2. 보너스 획득
AdStageManager.shared.trackEvent(
    AdStageEvent.AcquireBonus(
        contentType: "reward",
        itemId: "ITEM_123",
        itemName: "Gold Chest",
        quantity: 1
    )
)
 
// 3. 게임 서버 선택
AdStageManager.shared.trackEvent(
    AdStageEvent.SelectGameServer(
        contentId: "SERVER_01",
        contentType: "pvp",
        itemName: "Asia Server"
    )
)
 
// 4. 패치 완료
AdStageManager.shared.trackEvent(
    AdStageEvent.CompletePatch(
        contentId: "PATCH_2.1.0",
        contentType: "update"
    )
)

📅 구독/체험 (3개)

// 1. 무료 체험 시작
AdStageManager.shared.trackEvent(
    AdStageEvent.StartTrial(
        value: 9900.0,
        currency: "KRW",
        trialDays: 14
    )
)
 
// 2. 구독 시작
AdStageManager.shared.trackEvent(
    AdStageEvent.Subscribe(
        value: 9900.0,
        currency: "KRW",
        subscriptionId: "premium_monthly"
    )
)
 
// 3. 구독 취소
AdStageManager.shared.trackEvent(
    AdStageEvent.Unsubscribe(subscriptionId: "premium_monthly")
)

💰 가상 화폐 (2개)

// 1. 가상 화폐 획득
AdStageManager.shared.trackEvent(
    AdStageEvent.EarnVirtualCurrency(
        virtualCurrencyName: "gold",
        value: 500.0
    )
)
 
// 2. 가상 화폐 사용
AdStageManager.shared.trackEvent(
    AdStageEvent.SpendVirtualCurrency(
        virtualCurrencyName: "gold",
        value: 100.0,
        itemName: "health_potion"
    )
)

🎯 기타 (7개)

// 1. 일정 등록
AdStageManager.shared.trackEvent(
    AdStageEvent.Schedule(
        params: ["event_type": "appointment"]
    )
)
 
// 2. 크레딧 사용
AdStageManager.shared.trackEvent(
    AdStageEvent.SpendCredits(
        value: 10.0,
        itemName: "premium_feature"
    )
)
 
// 3. 프로모션 조회
AdStageManager.shared.trackEvent(
    AdStageEvent.ViewPromotion(
        promotionId: "PROMO_SUMMER",
        promotionName: "Summer Sale 2025",
        creativeSlot: "home_banner_1"
    )
)
 
// 4. 프로모션 선택
AdStageManager.shared.trackEvent(
    AdStageEvent.SelectPromotion(
        promotionId: "PROMO_SUMMER",
        promotionName: "Summer Sale 2025"
    )
)

이벤트 타입별 예제

1. 앱 라이프사이클

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        
        // SDK 초기화
        AdStageManager.shared.initialize(
            apiKey: "your-api-key",
            serverUrl: "https://api.adstage.app"
        )
        
        // 첫 실행 이벤트는 SDK가 자동으로 전송
        print("✅ 앱 시작")
        
        return true
    }
    
    func applicationDidEnterBackground(_ application: UIApplication) {
        print("📱 앱 백그라운드 진입")
    }
    
    func applicationWillEnterForeground(_ application: UIApplication) {
        print("📱 앱 포그라운드 진입")
    }
}

2. 사용자 인증

class AuthManager {
    
    // 회원가입
    func onSignUpComplete(method: String) {
        let signUpMethod: SignUpMethod
        switch method {
        case "email": signUpMethod = .email
        case "google": signUpMethod = .google
        case "apple": signUpMethod = .apple
        case "kakao": signUpMethod = .kakao
        case "naver": signUpMethod = .naver
        default: signUpMethod = .email
        }
        
        AdStageManager.shared.trackEvent(
            AdStageEvent.SignUp(method: signUpMethod)
        )
        
        print("✅ 회원가입 완료: \(method)")
    }
    
    // 로그인
    func onLoginSuccess(userId: String, method: String) {
        let loginMethod: SignUpMethod = method == "google" ? .google : .email
        
        AdStageManager.shared.trackEvent(
            AdStageEvent.Login(method: loginMethod)
        )
        
        print("✅ 로그인: \(userId)")
    }
    
    // 로그아웃
    func onLogout() {
        AdStageManager.shared.trackEvent(AdStageEvent.Logout())
        print("🚪 로그아웃")
    }
}

3. 화면 추적 (BaseViewController 패턴)

class BaseViewController: UIViewController {
    
    // 하위 클래스에서 오버라이드
    var screenName: String {
        return String(describing: type(of: self))
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        trackScreenView()
    }
    
    private func trackScreenView() {
        AdStageManager.shared.trackEvent(
            AdStageEvent.ScreenView(
                screenName: screenName,
                screenClass: String(describing: type(of: self))
            )
        )
        
        print("📺 \(screenName)")
    }
}
 
// 사용 예제
class HomeViewController: BaseViewController {
    override var screenName: String { "home" }
}
 
class ProductDetailViewController: BaseViewController {
    override var screenName: String { "product_detail" }
    var productId: String?
    
    func loadProduct(_ productId: String) {
        self.productId = productId
        // 데이터 로딩...
        
        // 상품 상세 조회 이벤트
        AdStageManager.shared.trackEvent(
            AdStageEvent.ProductDetailsView(
                itemId: productId,
                itemName: product.name
            )
        )
    }
}

4. 전자상거래 전체 플로우

class EcommerceManager {
    
    // 1. 상품 목록 조회
    func trackProductList(category: String) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.ProductListView(itemCategory: category)
        )
    }
    
    // 2. 검색
    func trackSearch(query: String) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.Search(searchTerm: query)
        )
    }
    
    // 3. 검색 결과 조회
    func trackSearchResults(query: String) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.SearchResultView(searchTerm: query)
        )
    }
    
    // 4. 상품 상세 조회
    func trackProductView(product: Product) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.ProductDetailsView(
                itemId: product.id,
                itemName: product.name
            )
        )
    }
    
    // 5. 장바구니 추가
    func trackAddToCart(product: Product, quantity: Int) {
        let item = EcommerceItem(
            itemId: product.id,
            itemName: product.name,
            itemCategory: product.category,
            price: product.price,
            quantity: quantity
        )
        
        AdStageManager.shared.trackEvent(
            AdStageEvent.AddToCart(
                value: product.price * Double(quantity),
                currency: "KRW",
                items: [item]
            )
        )
    }
    
    // 6. 위시리스트 추가
    func trackAddToWishlist(product: Product) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.AddToWishlist(
                itemId: product.id,
                itemName: product.name
            )
        )
    }
    
    // 7. 결제 시작
    func trackBeginCheckout(cart: Cart) {
        let items = cart.items.map { cartItem in
            EcommerceItem(
                itemId: cartItem.product.id,
                itemName: cartItem.product.name,
                itemCategory: cartItem.product.category,
                price: cartItem.product.price,
                quantity: cartItem.quantity
            )
        }
        
        AdStageManager.shared.trackEvent(
            AdStageEvent.BeginCheckout(
                value: cart.totalAmount,
                currency: "KRW",
                items: items
            )
        )
    }
    
    // 8. 결제 정보 입력
    func trackAddPaymentInfo(paymentMethod: String) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.AddPaymentInfo(paymentType: paymentMethod)
        )
    }
    
    // 9. 구매 완료 ⭐⭐⭐
    func trackPurchase(order: Order) {
        let items = order.items.map { orderItem in
            EcommerceItem(
                itemId: orderItem.product.id,
                itemName: orderItem.product.name,
                itemCategory: orderItem.product.category,
                price: orderItem.product.price,
                quantity: orderItem.quantity
            )
        }
        
        AdStageManager.shared.trackEvent(
            AdStageEvent.Purchase(
                value: order.totalAmount,
                currency: "KRW",
                transactionId: order.id,
                tax: order.tax,
                shipping: order.shippingFee,
                coupon: order.couponCode,
                items: items
            )
        )
        
        print("✅ 구매 완료: \(order.id), \(order.totalAmount)원")
    }
    
    // 10. 환불
    func trackRefund(order: Order) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.Refund(
                transactionId: order.id,
                value: order.totalAmount,
                currency: "KRW"
            )
        )
    }
    
    // 11. 공유
    func trackShare(product: Product, method: String) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.Share(
                contentType: "product",
                itemId: product.id,
                method: method
            )
        )
    }
}

5. 게임 이벤트

class GameEventManager {
    
    // 튜토리얼
    func trackTutorialBegin(tutorialId: String) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.TutorialBegin(
                params: ["tutorial_id": tutorialId]
            )
        )
    }
    
    func trackTutorialComplete(tutorialId: String, duration: TimeInterval) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.TutorialComplete(
                params: [
                    "tutorial_id": tutorialId,
                    "duration_seconds": Int(duration)
                ]
            )
        )
    }
    
    // 레벨
    func trackLevelUp(level: Int, character: String) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.LevelUp(
                level: level,
                character: character
            )
        )
    }
    
    // 업적
    func trackAchievement(achievementId: String) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.Achievement(achievementId: achievementId)
        )
    }
    
    // 게임 플레이
    func trackGameStart(level: Int, levelName: String, character: String) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.GamePlay(
                level: level,
                levelName: levelName,
                character: character,
                contentType: "pvp"
            )
        )
    }
    
    // 보너스 획득
    func trackBonusAcquired(itemId: String, itemName: String, quantity: Int) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.AcquireBonus(
                contentType: "reward",
                itemId: itemId,
                itemName: itemName,
                quantity: quantity
            )
        )
    }
    
    // 가상 화폐
    func trackEarnCurrency(currencyName: String, amount: Double) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.EarnVirtualCurrency(
                virtualCurrencyName: currencyName,
                value: amount
            )
        )
    }
    
    func trackSpendCurrency(currencyName: String, amount: Double, itemName: String) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.SpendVirtualCurrency(
                virtualCurrencyName: currencyName,
                value: amount,
                itemName: itemName
            )
        )
    }
}

베스트 프랙티스

1. Extension 활용

// UIViewController+AdStage.swift
extension UIViewController {
    func trackScreen() {
        let screenName = String(describing: type(of: self))
            .replacingOccurrences(of: "ViewController", with: "")
            .lowercased()
        
        AdStageManager.shared.trackEvent(
            AdStageEvent.ScreenView(
                screenName: screenName,
                screenClass: String(describing: type(of: self))
            )
        )
    }
}
 
// 사용
class ProfileViewController: UIViewController {
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        trackScreen()  // ✅ 간단!
    }
}

2. ViewModel에서 이벤트 추적

class ProductViewModel: ObservableObject {
    
    @Published var product: Product?
    
    func loadProduct(productId: String) {
        Task {
            do {
                let product = try await repository.getProduct(productId)
                self.product = product
                
                // 상품 조회 이벤트
                AdStageManager.shared.trackEvent(
                    AdStageEvent.ProductDetailsView(
                        itemId: product.id,
                        itemName: product.name
                    )
                )
                
            } catch {
                print("Failed to load product: \(error)")
            }
        }
    }
    
    func addToCart(product: Product, quantity: Int) {
        Task {
            do {
                try await repository.addToCart(product, quantity: quantity)
                
                let item = EcommerceItem(
                    itemId: product.id,
                    itemName: product.name,
                    price: product.price,
                    quantity: quantity
                )
                
                AdStageManager.shared.trackEvent(
                    AdStageEvent.AddToCart(
                        value: product.price * Double(quantity),
                        currency: "KRW",
                        items: [item]
                    )
                )
                
            } catch {
                print("Failed to add to cart: \(error)")
            }
        }
    }
}

3. SwiftUI에서 사용

import SwiftUI
import AdapterAdStage
 
struct ProductDetailView: View {
    let productId: String
    @StateObject private var viewModel = ProductViewModel()
    
    var body: some View {
        VStack {
            // UI...
            
            Button("장바구니에 추가") {
                viewModel.addToCart(viewModel.product, quantity: 1)
                
                // 버튼 클릭 이벤트
                AdStageManager.shared.trackEvent(
                    AdStageEvent.Custom(
                        params: [
                            "action": "add_to_cart_button",
                            "product_id": productId
                        ]
                    )
                )
            }
        }
        .onAppear {
            viewModel.loadProduct(productId: productId)
            
            // 화면 조회 이벤트
            AdStageManager.shared.trackEvent(
                AdStageEvent.ScreenView(
                    screenName: "product_detail",
                    screenClass: "ProductDetailView"
                )
            )
        }
    }
}

4. 이벤트 래퍼 클래스

class AnalyticsManager {
    
    static let shared = AnalyticsManager()
    private init() {}
    
    func trackScreen(_ screenName: String, screenClass: String? = nil) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.ScreenView(
                screenName: screenName,
                screenClass: screenClass
            )
        )
        print("📺 Screen: \(screenName)")
    }
    
    func trackPurchase(_ order: Order) {
        let items = order.items.map {
            EcommerceItem(
                itemId: $0.product.id,
                itemName: $0.product.name,
                price: $0.product.price,
                quantity: $0.quantity
            )
        }
        
        AdStageManager.shared.trackEvent(
            AdStageEvent.Purchase(
                value: order.totalAmount,
                currency: "KRW",
                transactionId: order.id,
                items: items
            )
        )
        
        print("💰 Purchase: \(order.id)")
    }
    
    func trackError(_ error: Error, context: String) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.Custom(
                params: [
                    "event_type": "error",
                    "error_message": error.localizedDescription,
                    "context": context
                ]
            )
        )
        print("❌ Error: \(context) - \(error)")
    }
}

5. 성능 최적화: Throttling

class ThrottledEventTracker {
    private var lastTrackTimes: [String: TimeInterval] = [:]
    private let throttleInterval: TimeInterval = 2.0  // 2초
    
    func trackEvent(_ event: AdStageEventProtocol) {
        let eventKey = event.eventName
        let now = Date().timeIntervalSince1970
        let lastTime = lastTrackTimes[eventKey] ?? 0
        
        if now - lastTime >= throttleInterval {
            AdStageManager.shared.trackEvent(event)
            lastTrackTimes[eventKey] = now
        } else {
            print("⏱️ Throttled: \(eventKey)")
        }
    }
}
 
// 사용 (스크롤 이벤트 등)
let throttledTracker = ThrottledEventTracker()
 
func scrollViewDidScroll(_ scrollView: UIScrollView) {
    throttledTracker.trackEvent(
        AdStageEvent.Custom(
            params: [
                "action": "scroll",
                "offset_y": scrollView.contentOffset.y
            ]
        )
    )
}

변환 예제

// 1. 기본 이벤트
// OLD: EventTrackingManager.shared.trackEvent(...)
// NEW:
AdStageManager.shared.trackEvent(AdStageEvent.FirstOpen())
 
// 2. 로그인
// NEW:
AdStageManager.shared.trackEvent(AdStageEvent.Login(method: .email))
 
// 3. 화면 조회
// NEW:
AdStageManager.shared.trackEvent(AdStageEvent.ScreenView(screenName: "home"))
 
// 4. 커스텀
// NEW:
AdStageManager.shared.trackEvent(
    AdStageEvent.Custom(params: ["action": "click"])
)

트러블슈팅

1. 컴파일 에러: "currency 파라미터 필수"

문제:

// ❌ 에러
AdStageManager.shared.trackEvent(
    AdStageEvent.Purchase(value: 9900.0)
)

해결:

// ✅ currency 추가 (ISO 4217, 3자리)
AdStageManager.shared.trackEvent(
    AdStageEvent.Purchase(
        value: 9900.0,
        currency: "KRW"
    )
)

2. Xcode 자동완성 작동 안 함

해결:

  1. Clean Build Folder: Product → Clean Build Folder (⇧⌘K)
  2. Derived Data 삭제: ~/Library/Developer/Xcode/DerivedData
  3. Pod 재설치:
pod deintegrate
pod install

3. 통화 코드 검증 에러

// ❌ 2자리 코드
AdStageEvent.Purchase(value: 100.0, currency: "KR")
// ValidationException: "Currency must be 3-letter ISO 4217 code"
 
// ✅ 3자리 ISO 4217 코드
AdStageEvent.Purchase(value: 100.0, currency: "KRW")  // 한국 원
AdStageEvent.Purchase(value: 100.0, currency: "USD")  // 미국 달러
AdStageEvent.Purchase(value: 100.0, currency: "JPY")  // 일본 엔

4. SwiftUI Preview 충돌

문제: Preview에서 AdStageManager 초기화 에러

해결:

#if DEBUG
struct ProductDetailView_Previews: PreviewProvider {
    static var previews: some View {
        ProductDetailView(productId: "PROD_123")
            .onAppear {
                // Preview에서는 초기화하지 않음
                if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == nil {
                    AdStageManager.shared.initialize(
                        apiKey: "test-key",
                        serverUrl: "https://api.adstage.app"
                    )
                }
            }
    }
}
#endif

5. 디버그 로그 확인

// AppDelegate.swift
func application(...) -> Bool {
    #if DEBUG
    AdStageManager.shared.setDebugMode(true)
    #endif
    
    AdStageManager.shared.initialize(...)
    return true
}

콘솔 필터:

# Xcode Console
filter: AdStage

참고 자료

표준 참조


FAQ

Q: 커스텀 이벤트는 어떻게 전송하나요?
A: AdStageEvent.Custom(params: [...]) 사용

Q: Android SDK와 호환되나요?
A: 예, v3.0은 Android SDK와 100% 동일한 46개 이벤트를 지원합니다.

Q: SwiftUI에서 사용할 수 있나요?
A: 예, ObservableObject 패턴으로 완벽히 지원됩니다.


지원

연락처:


© 2025 NBase. All rights reserved.

목차