StageUp
모바일 SDK인앱 이벤트

iOS

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

목차

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

개요

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

주요 기능

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

기본 설정

1. 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"
        )
        
        // 전역 설정
        setupGlobalContext()
        
        return true
    }
    
    private func setupGlobalContext() {
        // 디바이스 정보 설정
        setupDeviceInfo()
        
        // 사용자가 로그인한 경우
        if let userId = UserDefaults.standard.string(forKey: "userId") {
            setupUserContext(userId: userId)
        }
    }
}

2. 전역 디바이스 정보 설정

import UIKit
import AdapterAdStage
 
func setupDeviceInfo() {
    let device = UIDevice.current
    let app = Bundle.main
    
    AdStageSDK.shared.setDeviceProperties(
        category: device.userInterfaceIdiom == .pad ? "tablet" : "mobile",
        platform: "iOS",
        model: deviceModelName(),
        appVersion: app.infoDictionary?["CFBundleShortVersionString"] as? String ?? "unknown",
        osVersion: device.systemVersion
    )
    
    print("✅ 디바이스 정보 설정 완료")
}
 
// 정확한 디바이스 모델명 가져오기
func deviceModelName() -> String {
    var systemInfo = utsname()
    uname(&systemInfo)
    let machineMirror = Mirror(reflecting: systemInfo.machine)
    let identifier = machineMirror.children.reduce("") { identifier, element in
        guard let value = element.value as? Int8, value != 0 else { return identifier }
        return identifier + String(UnicodeScalar(UInt8(value)))
    }
    return identifier
}

3. 사용자 정보 설정

func setupUserContext(userId: String) {
    // 사용자 ID 설정
    AdStageSDK.shared.setUserId(userId)
    
    // 사용자 속성 설정
    if let userProfile = loadUserProfile() {
        AdStageSDK.shared.setUserProperties(
            gender: userProfile.gender,
            country: userProfile.country,
            city: userProfile.city,
            age: "\(userProfile.age)",
            language: Locale.current.languageCode ?? "en"
        )
    }
    
    print("✅ 사용자 정보 설정 완료: \(userId)")
}
 
func clearUserContext() {
    // 로그아웃 시 호출
    AdStageSDK.shared.clearEventContext()
    print("🚪 사용자 정보 초기화")
}

기본 이벤트 전송

1. 가장 간단한 방식

import AdapterAdStage
 
// 이벤트 이름만 전송
AdStageSDK.shared.track("APP_OPEN")

2. 파라미터 포함

// 파라미터와 함께 전송
AdStageSDK.shared.track("BUTTON_CLICK", params: [
    "button_id": "promo_banner",
    "screen": "home",
    "action": "tap"
])

3. 다양한 데이터 타입 지원

AdStageSDK.shared.track("PURCHASE", params: [
    "transaction_id": "TX_123456",
    "value": 99000,                    // Int
    "tax": 9900.0,                     // Double
    "currency": "KRW",                 // String
    "is_first_purchase": true,         // Bool
    "items": ["item1", "item2"],       // Array
    "metadata": [                       // Dictionary
        "payment_method": "card",
        "shipping": "express"
    ]
])

4. 현재 설정 확인

// 현재 사용자 ID
if let userId = AdStageSDK.shared.getUserId() {
    print("Current User: \(userId)")
}
 
// 현재 세션 ID
if let sessionId = AdStageSDK.shared.getSessionId() {
    print("Current Session: \(sessionId)")
}
 
// 전체 상태 출력 (디버깅용)
AdStageSDK.shared.printEventStatus()

사용자 및 디바이스 정보

1. 사용자 ID 관리

class UserManager {
    
    // 로그인
    func onUserLogin(userId: String) {
        AdStageSDK.shared.setUserId(userId)
        
        // 로그인 이벤트
        AdStageSDK.shared.track("USER_LOGIN", params: [
            "login_method": "email"
        ])
        
        print("✅ 로그인: \(userId)")
    }
    
    // 로그아웃
    func onUserLogout() {
        // 로그아웃 이벤트 먼저 전송
        AdStageSDK.shared.track("USER_LOGOUT")
        
        // 사용자 정보 초기화
        AdStageSDK.shared.clearEventContext()
        
        print("🚪 로그아웃 완료")
    }
    
    // 회원가입
    func onUserSignup(userId: String, method: String) {
        AdStageSDK.shared.setUserId(userId)
        
        AdStageSDK.shared.track("USER_SIGNUP", params: [
            "signup_method": method,
            "timestamp": Date().timeIntervalSince1970
        ])
    }
}

2. 사용자 속성 업데이트

class UserProfileManager {
    
    func updateUserProfile(_ profile: UserProfile) {
        AdStageSDK.shared.setUserProperties(
            gender: profile.gender,
            country: profile.country,
            city: profile.city,
            age: "\(profile.age)",
            language: profile.language
        )
        
        // 프로필 업데이트 이벤트
        AdStageSDK.shared.track("PROFILE_UPDATED")
        
        print("✅ 프로필 업데이트 완료")
    }
    
    func updateLocation(country: String, city: String) {
        AdStageSDK.shared.setUserProperties(
            country: country,
            city: city
        )
        
        print("📍 위치 업데이트: \(city), \(country)")
    }
}

3. 디바이스 정보 업데이트

class DeviceInfoManager {
    
    func updateAppVersion() {
        let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
        
        AdStageSDK.shared.setDeviceProperties(
            appVersion: appVersion
        )
        
        print("📱 앱 버전 업데이트: \(appVersion ?? "unknown")")
    }
    
    func updateOSVersion() {
        let osVersion = UIDevice.current.systemVersion
        
        AdStageSDK.shared.setDeviceProperties(
            osVersion: osVersion
        )
        
        print("💿 OS 버전 업데이트: iOS \(osVersion)")
    }
}

4. 세션 관리

class SessionManager {
    
    private var sessionStartTime: Date?
    
    // 세션 시작
    func startSession() {
        sessionStartTime = Date()
        
        AdStageSDK.shared.track("SESSION_START", params: [
            "timestamp": sessionStartTime?.timeIntervalSince1970 ?? 0
        ])
        
        print("▶️ 세션 시작")
    }
    
    // 세션 종료
    func endSession() {
        guard let startTime = sessionStartTime else { return }
        
        let duration = Date().timeIntervalSince(startTime)
        
        AdStageSDK.shared.track("SESSION_END", params: [
            "duration_seconds": Int(duration),
            "timestamp": Date().timeIntervalSince1970
        ])
        
        print("⏹️ 세션 종료: \(Int(duration))초")
    }
}

이벤트 타입별 예제

1. 앱 라이프사이클 이벤트

import UIKit
import AdapterAdStage
 
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        
        // SDK 초기화
        AdStageManager.shared.initialize(
            apiKey: "your-api-key-here",
            serverUrl: "https://api.adstage.app"
        )
        
        // 앱 시작 이벤트
        trackAppLaunch(launchOptions: launchOptions)
        
        return true
    }
    
    private func trackAppLaunch(launchOptions: [UIApplication.LaunchOptionsKey: Any]?) {
        var params: [String: Any] = [
            "version": Bundle.main.infoDictionary?["CFBundleShortVersionString"] ?? "unknown",
            "build": Bundle.main.infoDictionary?["CFBundleVersion"] ?? "unknown"
        ]
        
        // 푸시 알림으로 실행된 경우
        if let notification = launchOptions?[.remoteNotification] {
            params["launch_source"] = "push_notification"
        }
        
        // URL로 실행된 경우
        if let url = launchOptions?[.url] {
            params["launch_source"] = "deep_link"
            params["url"] = "\(url)"
        }
        
        AdStageSDK.shared.track("APP_LAUNCH", params: params)
    }
    
    func applicationDidEnterBackground(_ application: UIApplication) {
        AdStageSDK.shared.track("APP_BACKGROUND")
    }
    
    func applicationWillEnterForeground(_ application: UIApplication) {
        AdStageSDK.shared.track("APP_FOREGROUND")
    }
    
    func applicationWillTerminate(_ application: UIApplication) {
        AdStageSDK.shared.track("APP_TERMINATE")
    }
}

2. 화면 조회 이벤트

import UIKit
import AdapterAdStage
 
class BaseViewController: UIViewController {
    
    // 화면 이름 (하위 클래스에서 오버라이드)
    var screenName: String {
        return String(describing: type(of: self))
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        trackScreenView()
    }
    
    private func trackScreenView() {
        AdStageSDK.shared.track("SCREEN_VIEW", params: [
            "screen_name": screenName,
            "screen_class": String(describing: type(of: self)),
            "timestamp": Date().timeIntervalSince1970
        ])
        
        print("📺 Screen View: \(screenName)")
    }
}
 
// 사용 예제
class HomeViewController: BaseViewController {
    override var screenName: String { "home" }
}
 
class ProductDetailViewController: BaseViewController {
    var productId: String?
    
    override var screenName: String { "product_detail" }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        if let productId = productId {
            AdStageSDK.shared.track("VIEW_ITEM", params: [
                "item_id": productId
            ])
        }
    }
}

3. 전자상거래 이벤트

import AdapterAdStage
 
class EcommerceTracker {
    
    // 상품 조회
    func trackProductView(product: Product) {
        AdStageSDK.shared.track("VIEW_ITEM", params: [
            "item_id": product.id,
            "item_name": product.name,
            "item_category": product.category,
            "price": product.price,
            "currency": "KRW"
        ])
    }
    
    // 장바구니 추가
    func trackAddToCart(product: Product, quantity: Int) {
        AdStageSDK.shared.track("ADD_TO_CART", params: [
            "item_id": product.id,
            "item_name": product.name,
            "quantity": quantity,
            "price": product.price,
            "value": product.price * quantity,
            "currency": "KRW"
        ])
    }
    
    // 장바구니 제거
    func trackRemoveFromCart(product: Product, quantity: Int) {
        AdStageSDK.shared.track("REMOVE_FROM_CART", params: [
            "item_id": product.id,
            "quantity": quantity,
            "value": product.price * quantity
        ])
    }
    
    // 구매 시작
    func trackBeginCheckout(cart: ShoppingCart) {
        AdStageSDK.shared.track("BEGIN_CHECKOUT", params: [
            "value": cart.total,
            "currency": "KRW",
            "item_count": cart.items.count,
            "coupon": cart.couponCode ?? ""
        ])
    }
    
    // 결제 정보 입력
    func trackAddPaymentInfo(paymentMethod: String) {
        AdStageSDK.shared.track("ADD_PAYMENT_INFO", params: [
            "payment_method": paymentMethod
        ])
    }
    
    // 구매 완료
    func trackPurchase(order: Order) {
        AdStageSDK.shared.track("PURCHASE", params: [
            "transaction_id": order.id,
            "value": order.total,
            "currency": "KRW",
            "tax": order.tax,
            "shipping": order.shipping,
            "coupon": order.couponCode ?? "",
            "payment_method": order.paymentMethod,
            "item_count": order.items.count,
            "items": order.items.map { [
                "id": $0.id,
                "name": $0.name,
                "price": $0.price,
                "quantity": $0.quantity
            ]}
        ])
        
        print("✅ 구매 완료: \(order.id)")
    }
    
    // 환불
    func trackRefund(orderId: String, refundAmount: Int, reason: String) {
        AdStageSDK.shared.track("REFUND", params: [
            "transaction_id": orderId,
            "value": refundAmount,
            "currency": "KRW",
            "reason": reason
        ])
    }
}

4. 사용자 행동 이벤트

class UserActionTracker {
    
    // 검색
    func trackSearch(query: String, filters: [String: Any]? = nil) {
        var params: [String: Any] = [
            "search_term": query
        ]
        
        if let filters = filters {
            params["filters"] = filters
        }
        
        AdStageSDK.shared.track("SEARCH", params: params)
    }
    
    // 검색 결과 클릭
    func trackSearchResultClick(query: String, resultIndex: Int, itemId: String) {
        AdStageSDK.shared.track("SEARCH_RESULT_CLICK", params: [
            "search_term": query,
            "position": resultIndex,
            "item_id": itemId
        ])
    }
    
    // 공유
    func trackShare(contentType: String, contentId: String, method: String) {
        AdStageSDK.shared.track("SHARE", params: [
            "content_type": contentType,
            "content_id": contentId,
            "method": method  // "messages", "kakao", "instagram"
        ])
    }
    
    // 좋아요
    func trackLike(contentType: String, contentId: String) {
        AdStageSDK.shared.track("LIKE", params: [
            "content_type": contentType,
            "content_id": contentId
        ])
    }
    
    // 북마크
    func trackBookmark(contentType: String, contentId: String, action: String) {
        AdStageSDK.shared.track("BOOKMARK", params: [
            "content_type": contentType,
            "content_id": contentId,
            "action": action  // "add", "remove"
        ])
    }
    
    // 댓글 작성
    func trackComment(contentType: String, contentId: String, commentLength: Int) {
        AdStageSDK.shared.track("COMMENT", params: [
            "content_type": contentType,
            "content_id": contentId,
            "length": commentLength
        ])
    }
    
    // 리뷰 작성
    func trackReview(productId: String, rating: Int, hasText: Bool, hasPhoto: Bool) {
        AdStageSDK.shared.track("WRITE_REVIEW", params: [
            "product_id": productId,
            "rating": rating,
            "has_text": hasText,
            "has_photo": hasPhoto
        ])
    }
}

5. 게임 이벤트

class GameEventTracker {
    
    // 레벨 시작
    func trackLevelStart(level: Int, levelName: String) {
        AdStageSDK.shared.track("LEVEL_START", params: [
            "level_number": level,
            "level_name": levelName
        ])
    }
    
    // 레벨 완료
    func trackLevelComplete(level: Int, score: Int, duration: TimeInterval, stars: Int) {
        AdStageSDK.shared.track("LEVEL_COMPLETE", params: [
            "level_number": level,
            "score": score,
            "duration_seconds": Int(duration),
            "stars": stars,
            "success": true
        ])
    }
    
    // 레벨 실패
    func trackLevelFail(level: Int, reason: String, attemptCount: Int) {
        AdStageSDK.shared.track("LEVEL_FAIL", params: [
            "level_number": level,
            "fail_reason": reason,
            "attempt_count": attemptCount,
            "success": false
        ])
    }
    
    // 가상 화폐 획득
    func trackEarnVirtualCurrency(currencyName: String, amount: Int, source: String) {
        AdStageSDK.shared.track("EARN_VIRTUAL_CURRENCY", params: [
            "virtual_currency_name": currencyName,
            "value": amount,
            "source": source  // "level_reward", "daily_bonus", "purchase"
        ])
    }
    
    // 가상 화폐 사용
    func trackSpendVirtualCurrency(currencyName: String, amount: Int, itemName: String, itemCategory: String) {
        AdStageSDK.shared.track("SPEND_VIRTUAL_CURRENCY", params: [
            "virtual_currency_name": currencyName,
            "value": amount,
            "item_name": itemName,
            "item_category": itemCategory
        ])
    }
    
    // 아이템 획득
    func trackUnlockAchievement(achievementId: String, achievementName: String) {
        AdStageSDK.shared.track("UNLOCK_ACHIEVEMENT", params: [
            "achievement_id": achievementId,
            "achievement_name": achievementName
        ])
    }
    
    // 튜토리얼 시작
    func trackTutorialBegin(tutorialId: String) {
        AdStageSDK.shared.track("TUTORIAL_BEGIN", params: [
            "tutorial_id": tutorialId
        ])
    }
    
    // 튜토리얼 완료
    func trackTutorialComplete(tutorialId: String, duration: TimeInterval) {
        AdStageSDK.shared.track("TUTORIAL_COMPLETE", params: [
            "tutorial_id": tutorialId,
            "duration_seconds": Int(duration)
        ])
    }
    
    // 게임 시작
    func trackGameStart(gameMode: String, difficulty: String) {
        AdStageSDK.shared.track("GAME_START", params: [
            "game_mode": gameMode,
            "difficulty": difficulty
        ])
    }
    
    // 게임 종료
    func trackGameEnd(gameMode: String, score: Int, duration: TimeInterval, result: String) {
        AdStageSDK.shared.track("GAME_END", params: [
            "game_mode": gameMode,
            "score": score,
            "duration_seconds": Int(duration),
            "result": result  // "win", "lose", "draw"
        ])
    }
}

6. 미디어 이벤트

class MediaTracker {
    
    // 비디오 시작
    func trackVideoStart(videoId: String, videoTitle: String, duration: TimeInterval) {
        AdStageSDK.shared.track("VIDEO_START", params: [
            "video_id": videoId,
            "video_title": videoTitle,
            "duration": Int(duration)
        ])
    }
    
    // 비디오 완료
    func trackVideoComplete(videoId: String, watchedDuration: TimeInterval, totalDuration: TimeInterval) {
        let completionRate = (watchedDuration / totalDuration) * 100
        
        AdStageSDK.shared.track("VIDEO_COMPLETE", params: [
            "video_id": videoId,
            "watched_seconds": Int(watchedDuration),
            "total_seconds": Int(totalDuration),
            "completion_rate": Int(completionRate)
        ])
    }
    
    // 오디오 재생
    func trackAudioPlay(audioId: String, audioTitle: String, artist: String) {
        AdStageSDK.shared.track("AUDIO_PLAY", params: [
            "audio_id": audioId,
            "audio_title": audioTitle,
            "artist": artist
        ])
    }
    
    // 플레이리스트 생성
    func trackPlaylistCreate(playlistId: String, playlistName: String, itemCount: Int) {
        AdStageSDK.shared.track("PLAYLIST_CREATE", params: [
            "playlist_id": playlistId,
            "playlist_name": playlistName,
            "item_count": itemCount
        ])
    }
}

7. 광고 이벤트

class AdEventTracker {
    
    // 광고 노출
    func trackAdImpression(adType: String, adId: String, adPlacement: String) {
        AdStageSDK.shared.track("AD_IMPRESSION", params: [
            "ad_type": adType,  // "banner", "interstitial", "rewarded", "native"
            "ad_id": adId,
            "ad_placement": adPlacement
        ])
    }
    
    // 광고 클릭
    func trackAdClick(adType: String, adId: String, adPlacement: String) {
        AdStageSDK.shared.track("AD_CLICK", params: [
            "ad_type": adType,
            "ad_id": adId,
            "ad_placement": adPlacement
        ])
    }
    
    // 리워드 광고 시작
    func trackAdRewardStart(adId: String, rewardType: String) {
        AdStageSDK.shared.track("AD_REWARD_START", params: [
            "ad_id": adId,
            "reward_type": rewardType
        ])
    }
    
    // 리워드 광고 완료
    func trackAdRewardEarned(adId: String, rewardType: String, rewardAmount: Int) {
        AdStageSDK.shared.track("AD_REWARD_EARNED", params: [
            "ad_id": adId,
            "reward_type": rewardType,
            "reward_amount": rewardAmount
        ])
    }
}

베스트 프랙티스

1. 이벤트 매니저 Singleton

import AdapterAdStage
 
class AnalyticsManager {
    static let shared = AnalyticsManager()
    
    private init() {}
    
    func trackEvent(_ eventName: String, params: [String: Any]? = nil) {
        var finalParams = params ?? [:]
        
        // 공통 파라미터 자동 추가
        finalParams["timestamp"] = Date().timeIntervalSince1970
        finalParams["app_version"] = Bundle.main.infoDictionary?["CFBundleShortVersionString"]
        
        AdStageSDK.shared.track(eventName, params: finalParams)
        
        #if DEBUG
        print("📊 Event: \(eventName)")
        if let params = finalParams as? [String: String] {
            params.forEach { print("  - \($0.key): \($0.value)") }
        }
        #endif
    }
}
 
// 사용
AnalyticsManager.shared.trackEvent("BUTTON_CLICK", params: [
    "button_id": "purchase"
])

2. Extension으로 간편화

import UIKit
import AdapterAdStage
 
extension UIViewController {
    func trackScreen() {
        let screenName = String(describing: type(of: self))
        AdStageSDK.shared.track("SCREEN_VIEW", params: [
            "screen_name": screenName
        ])
    }
}
 
// 사용
class ProfileViewController: UIViewController {
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        trackScreen()
    }
}

3. 네이밍 규칙

struct EventNames {
    // 앱 라이프사이클
    static let appLaunch = "APP_LAUNCH"
    static let appForeground = "APP_FOREGROUND"
    static let appBackground = "APP_BACKGROUND"
    
    // 사용자 행동
    static let userLogin = "USER_LOGIN"
    static let userSignup = "USER_SIGNUP"
    static let userLogout = "USER_LOGOUT"
    
    // 전자상거래
    static let viewItem = "VIEW_ITEM"
    static let addToCart = "ADD_TO_CART"
    static let purchase = "PURCHASE"
    
    // 게임
    static let levelStart = "LEVEL_START"
    static let levelComplete = "LEVEL_COMPLETE"
}
 
struct EventParams {
    static let screenName = "screen_name"
    static let itemId = "item_id"
    static let value = "value"
    static let currency = "currency"
}
 
// 사용
AdStageSDK.shared.track(EventNames.purchase, params: [
    EventParams.value: 10000,
    EventParams.currency: "KRW"
])

4. Throttling (과도한 호출 방지)

class ThrottledTracker {
    private var lastTrackTime: [String: Date] = [:]
    private let throttleInterval: TimeInterval = 1.0  // 1초
    
    func trackEvent(_ eventName: String, params: [String: Any]? = nil) {
        let now = Date()
        
        if let lastTime = lastTrackTime[eventName],
           now.timeIntervalSince(lastTime) < throttleInterval {
            // 너무 짧은 시간 내에 같은 이벤트 호출, 무시
            return
        }
        
        lastTrackTime[eventName] = now
        AdStageSDK.shared.track(eventName, params: params)
    }
}
 
// 사용 (스크롤 이벤트 등)
let throttledTracker = ThrottledTracker()
 
func scrollViewDidScroll(_ scrollView: UIScrollView) {
    throttledTracker.trackEvent("SCROLL", params: [
        "offset_y": scrollView.contentOffset.y
    ])
}

5. 에러 추적

extension AnalyticsManager {
    func trackError(_ error: Error, context: String) {
        AdStageSDK.shared.track("ERROR_OCCURRED", params: [
            "error_message": error.localizedDescription,
            "error_domain": (error as NSError).domain,
            "error_code": (error as NSError).code,
            "context": context,
            "timestamp": Date().timeIntervalSince1970
        ])
    }
}
 
// 사용
do {
    try someOperation()
} catch {
    AnalyticsManager.shared.trackError(error, context: "payment_processing")
}

트러블슈팅

1. 이벤트가 전송되지 않음

// 초기화 확인
func checkInitialization() {
    // SDK 초기화 여부 확인
    let userId = AdStageSDK.shared.getUserId()
    print("User ID: \(userId ?? "nil")")
    
    let sessionId = AdStageSDK.shared.getSessionId()
    print("Session ID: \(sessionId ?? "nil")")
    
    // 전체 상태 출력
    AdStageSDK.shared.printEventStatus()
}

2. 네트워크 문제

import Network
 
class NetworkMonitor {
    static let shared = NetworkMonitor()
    
    private let monitor = NWPathMonitor()
    private let queue = DispatchQueue(label: "NetworkMonitor")
    
    var isConnected: Bool = true
    
    private init() {
        monitor.pathUpdateHandler = { [weak self] path in
            self?.isConnected = path.status == .satisfied
            
            if path.status == .satisfied {
                print("✅ 네트워크 연결됨")
            } else {
                print("❌ 네트워크 연결 끊김")
            }
        }
        
        monitor.start(queue: queue)
    }
}
 
// 사용
if NetworkMonitor.shared.isConnected {
    AdStageSDK.shared.track("EVENT_NAME")
} else {
    print("⚠️ 네트워크 연결 없음, 이벤트는 나중에 전송됨")
    // SDK가 자동으로 재시도함
}

3. 디버그 로깅

#if DEBUG
extension AdStageSDK {
    func trackDebug(_ eventName: String, params: [String: Any]? = nil) {
        print("""
        ==========================================
        📊 Event Track
        ==========================================
        Event: \(eventName)
        User ID: \(getUserId() ?? "nil")
        Session ID: \(getSessionId() ?? "nil")
        Params:
        \(params?.map { "  \($0.key): \($0.value)" }.joined(separator: "\n") ?? "  none")
        ==========================================
        """)
        
        track(eventName, params: params)
    }
}
#endif
 
// 사용
#if DEBUG
AdStageSDK.shared.trackDebug("TEST_EVENT", params: ["test": true])
#else
AdStageSDK.shared.track("TEST_EVENT", params: ["test": true])
#endif

4. 메모리 누수 방지

class EventTrackerViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // ❌ BAD: Strong reference cycle
        // NotificationCenter.default.addObserver(self, ...)
        
        // ✅ GOOD: Weak self
        NotificationCenter.default.addObserver(
            forName: .dataUpdated,
            object: nil,
            queue: .main
        ) { [weak self] notification in
            self?.handleDataUpdate(notification)
        }
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
        print("✅ ViewController deinitialized")
    }
}

참고 자료


목차