언리얼 엔진 SDK - 딥 링크 지원

문서

딥링킹 지원 추가

딥링크는사용자를 앱 내의 특정 콘텐츠로 연결합니다. 사용자가 앱이 설치된 기기에서 딥링크를 탭하면 앱이 제품 페이지나 특정 경험 등 의도한 콘텐츠로 바로 연결됩니다.

Singular 추적 링크는 표준 딥링킹(설치된 앱의 경우)과 디퍼드 딥링킹(신규 설치의 경우)을 모두 지원합니다. 자세한 내용은 딥링킹 FAQSingular 링크 FAQ를 참조하세요.


요구 사항

전제 조건

iOS 및 Android 플랫폼에서 딥링킹을 사용하려면 Singular 링크 사전 요구 사항을완료하세요.

참고:

  • 이 가이드에서는 2019년에 출시된 Singular의 추적 링크 기술인 Singular 링크를사용하고 있다고 가정합니다. 이전 고객은 레거시 트래킹 링크를 사용하고 있을 수 있습니다.
  • 앱의 딥링크 대상은 Singular의 페이지에서 구성해야 합니다( 딥링크 URL 구성하기 참조).

SDK 구성

Singular 링크 핸들러 구현

Singular SDK는 앱이 열릴 때 Singular 추적 링크에서 딥링크, 디퍼드 딥링크 및 패스스루 파라미터를 검색하는 델리게이트 기반 핸들러 메커니즘을 제공합니다.

사용 가능한 파라미터

  • 딥링크: 링크를 클릭하는 사용자의 앱 내 대상 URL(링크 관리 페이지에서 구성)입니다.
  • 패스스루: 추가 컨텍스트 또는 캠페인 메타데이터를 위해 추적 링크를 통해 전달되는 사용자 지정 데이터입니다.
  • IsDeferred: 링크가 디퍼드 딥링크인지 여부를 나타냅니다(신규 인스톨의 경우 참, 기존 인스톨의 경우 거짓).

앱에 딥링크 핸들러 추가

앱이 실행될 때 수신되는 딥링크 데이터를 처리하도록 딥링크 핸들러를 구성합니다. 이 작업은 Singular SDK를 초기화하기 전에 수행해야 합니다.

구현 단계

  1. 클래스 헤더 파일에 필요한 Singular 포함을 추가합니다.
  2. OnSingularLinksResolved 콜백을 처리하는 메서드를 구현합니다.
  3. SDK를 초기화하기 전에 USingularDelegates 객체를 생성하고 등록합니다.
  4. OnSingularLinksResolved.AddDynamic() 를 사용하여 핸들러 메서드를 등록합니다.
  5. 핸들러 메서드에서 딥링크 파라미터를 처리합니다.
C++
// Add to the include section of your class header
#include "SingularLinkParams.h"
#include "SingularDelegates.h"

// In your class header, declare the handler method
UFUNCTION()
void OnSingularLinksResolved(const FSingularLinkParams& LinkParams);

// In your class implementation file, implement the handler method
void UYourGameInstance::OnSingularLinksResolved(const FSingularLinkParams& LinkParams)
{
    // Extract parameters from the tracking link
    const FString Deeplink = LinkParams.SingularLinksParams["deeplink"];
    const FString Passthrough = LinkParams.SingularLinksParams["passthrough"];
    const bool IsDeferred = LinkParams.SingularLinksParams["isDeferred"] == "true";

    // Log the parameters
    UE_LOG(LogTemp, Log, TEXT("Deep Link Resolved"));
    UE_LOG(LogTemp, Log, TEXT("Deeplink: %s"), *Deeplink);
    UE_LOG(LogTemp, Log, TEXT("Passthrough: %s"), *Passthrough);
    UE_LOG(LogTemp, Log, TEXT("Is Deferred: %s"), IsDeferred ? TEXT("true") : TEXT("false"));

    // Handle deep link routing
    if (!Deeplink.IsEmpty())
    {
        HandleDeepLinkNavigation(Deeplink, IsDeferred);
    }
}

void UYourGameInstance::HandleDeepLinkNavigation(const FString& Url, bool IsDeferred)
{
    // Your deep link routing logic
    UE_LOG(LogTemp, Log, TEXT("Routing to: %s (Deferred: %s)"), 
           *Url, IsDeferred ? TEXT("true") : TEXT("false"));

    // Example: Parse the URL and navigate to appropriate content
    if (Url.Contains(TEXT("product")))
    {
        NavigateToProduct(Url);
    }
    else if (Url.Contains(TEXT("promo")))
    {
        NavigateToPromotion(Url);
    }
    else
    {
        NavigateToHome();
    }
}

// Register the delegate BEFORE initializing the Singular SDK
void UYourGameInstance::InitializeSingular()
{
    // Create the delegates object
    USingularDelegates* SingularDelegates = 
        CreateDefaultSubobject<USingularDelegates>(TEXT("SingularLinksHandler"));

    // Register the deep link handler
    SingularDelegates->OnSingularLinksResolved.AddDynamic(
        this, 
        &UYourGameInstance::OnSingularLinksResolved
    );

    UE_LOG(LogTemp, Log, TEXT("Singular Link Handler registered"));

    // Now initialize the SDK
    bool Success = USingularSDKBPLibrary::Initialize(
        TEXT("YOUR_SDK_KEY"),
        TEXT("YOUR_SDK_SECRET"),
        60,    // Session timeout
        TEXT(""),    // Custom user ID
        true,  // Enable SKAdNetwork
        false, // Manual SKAN management
        0,     // Wait for tracking authorization
        false, // OAID collection
        true,  // Enable logging
        3,     // Log level
        false, // Clipboard attribution
        TEXT(""),    // Facebook App ID
        TEXT("")     // Custom SDID
    );

    if (Success)
    {
        UE_LOG(LogTemp, Log, TEXT("Singular SDK initialized successfully"));
    }
}

중요: USingularSDKBPLibrary::Initialize() 을 호출하기 전에 델리게이트를 등록해야 합니다. 초기화 후에 핸들러를 등록하면 딥링크 콜백을 받지 못합니다.


핸들러 동작

딥링크 해상도 이해

딥링크 핸들러는 사용자가 Singular 링크를 탭할 때 앱이 새로 설치되었는지 또는 이미 설치되었는지에 따라 다르게 동작합니다.

새로 설치(디퍼드 딥링크)

새로 설치한 경우, 앱이 실행될 때 오픈 URL이 존재하지 않습니다. Singular는 추적 링크에 딥링크 값이 포함되어 있는지 확인하기 위해 어트리뷰션을 완료합니다.

디퍼드 딥링크 흐름:

  1. 사용자가 딥링크 값으로 구성된 Singular 트래킹 링크를 클릭합니다.
  2. 사용자가 앱을 설치하고 처음으로 앱을 엽니다.
  3. Singular SDK가 첫 번째 세션을 Singular 서버로 전송합니다.
  4. 어트리뷰션이 완료되고 트래킹 링크에서 딥링크를 식별합니다.
  5. 딥링크 값은 IsDeferred = true 로 핸들러에 반환됩니다.

디퍼드 딥링크 테스트:

  1. 테스트 기기에서 앱을 제거합니다(현재 설치되어 있는 경우).
  2. iOS: IDFA를 재설정합니다. 안드로이드:구글 광고 ID(GAID)를 재설정합니다.
  3. 기기에서 Singular 추적 링크를 클릭합니다(딥링크 값으로 구성되었는지 확인).
  4. 앱을 설치하고 엽니다.

어트리뷰션이 성공적으로 완료되고 디퍼드 딥링크 값이 핸들러에 전달됩니다.

개발 테스트 팁: 다른 패키지 이름이나 번들 ID를 사용하는 개발 빌드로 딥링크를 테스트하는 경우, 개발 앱의 식별자에 맞게 추적 링크를 구성하세요. 테스트 링크를 클릭한 후에는 앱 스토어에서 프로덕션 앱을 다운로드하지 말고 언리얼 엔진 또는 플랫폼별 툴을 통해 개발 빌드를 디바이스에 직접 설치하세요.


이미 설치됨(즉시 딥 링크)

앱이 이미 설치되어 있는 경우, Singular 링크를 클릭하면 유니버설 링크(iOS) 또는 Android 앱 링크 기술을 사용하여 앱이 즉시 열립니다.

즉시 딥링크 흐름:

  1. 사용자가 Singular 추적 링크를 클릭합니다.
  2. 운영 체제는 전체 Singular 추적 링크가 포함된 오픈 URL을 제공합니다.
  3. SDK 초기화 중에 Singular는 URL을 파싱합니다.
  4. Singular는 deeplinkpassthrough값을 추출합니다.
  5. 값은 핸들러를 통해 IsDeferred = false 로 반환됩니다.

패스스루 매개변수

캠페인 메타데이터, 사용자 세분화 또는 사용자 지정 정보에 대한 패스스루 매개변수를 사용하여 추적 링크 클릭에서 추가 데이터를 캡처합니다.

추적 링크에 passthrough (_p) 파라미터가 포함된 경우 핸들러의 Passthrough 파라미터에 해당 데이터가 포함됩니다.

C++
void UYourGameInstance::OnSingularLinksResolved(const FSingularLinkParams& LinkParams)
{
    // Extract passthrough data
    const FString PassthroughData = LinkParams.SingularLinksParams["passthrough"];

    if (!PassthroughData.IsEmpty())
    {
        // Parse JSON passthrough data
        TSharedPtr<FJsonObject> JsonObject;
        TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(PassthroughData);

        if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid())
        {
            // Extract campaign metadata
            FString CampaignId;
            if (JsonObject->TryGetStringField(TEXT("campaign_id"), CampaignId))
            {
                UE_LOG(LogTemp, Log, TEXT("Campaign ID: %s"), *CampaignId);
            }

            FString UserSegment;
            if (JsonObject->TryGetStringField(TEXT("segment"), UserSegment))
            {
                UE_LOG(LogTemp, Log, TEXT("User Segment: %s"), *UserSegment);
            }

            FString PromoCode;
            if (JsonObject->TryGetStringField(TEXT("promo_code"), PromoCode))
            {
                UE_LOG(LogTemp, Log, TEXT("Promo Code: %s"), *PromoCode);
                ApplyPromoCode(PromoCode);
            }

            // Use the data in your app
            ApplyCampaignSettings(CampaignId, UserSegment);
        }
        else
        {
            UE_LOG(LogTemp, Warning, TEXT("Failed to parse passthrough data"));
        }
    }
}

void UYourGameInstance::ApplyCampaignSettings(const FString& CampaignId, const FString& Segment)
{
    // Your campaign-specific logic
    UE_LOG(LogTemp, Log, TEXT("Applying campaign settings for: %s (Segment: %s)"), 
           *CampaignId, *Segment);
}

패스스루 예시 URL:
https://yourapp.sng.link/A1b2c/abc123?_dl=myapp://product/123&_p={"campaign_id":"summer2025","segment":"premium","promo_code":"SAVE20"}

핸들러는 캠페인 메타데이터가 포함된 JSON 문자열이 포함된 passthrough 파라미터를 수신합니다.


FSingularLinkParams 참조

파라미터 세부 정보

FSingularLinkParams 객체에는 딥링크 핸들러에 전달된 추적 링크 세부 정보가 포함되어 있습니다.

파라미터 유형 설명
deeplink FString 링크 관리 페이지에서 Singular로 구성된 딥링크 대상 URL입니다. 이 값은 사용자를 특정 인앱 콘텐츠로 리디렉션합니다.
passthrough FString 일반적으로 앱에서 처리해야 하는 캠페인 메타데이터, 사용자 세분화 또는 사용자 지정 데이터에 사용되는 추적 링크에 추가되는 패스스루 매개변수입니다.
isDeferred bool 링크가 디퍼드 딥링크로 구성되었는지 여부를 나타냅니다. 신규 인스톨(지연)의 경우 true, 기존 인스톨(즉시)의 경우 false 을 반환합니다.

메서드 서명

C++
// Callback method signature for Singular Links
void OnSingularLinksResolved(const FSingularLinkParams& linkParams)

// Access parameters using the SingularLinksParams map:
const FString Deeplink = linkParams.SingularLinksParams["deeplink"];
const FString Passthrough = linkParams.SingularLinksParams["passthrough"];
const bool IsDeferred = linkParams.SingularLinksParams["isDeferred"] == "true";

딥링크 라우팅 구현

딥링크 라우터 구축

중앙 집중식 딥링크 라우팅 시스템을 구현하여 딥링크 URL 구조를 기반으로 사용자를 특정 콘텐츠로 안내합니다.

C++
// Deep Link Router class implementation
class UDeepLinkRouter : public UObject
{
public:
    void RouteDeepLink(const FString& DeepLinkUrl, bool IsDeferred)
    {
        UE_LOG(LogTemp, Log, TEXT("Routing deep link: %s (Deferred: %s)"), 
               *DeepLinkUrl, IsDeferred ? TEXT("true") : TEXT("false"));

        // Parse the URL scheme
        if (DeepLinkUrl.StartsWith(TEXT("myapp://product/")))
        {
            FString ProductId = ExtractParameter(DeepLinkUrl, TEXT("myapp://product/"));
            NavigateToProduct(ProductId);
        }
        else if (DeepLinkUrl.StartsWith(TEXT("myapp://category/")))
        {
            FString CategoryName = ExtractParameter(DeepLinkUrl, TEXT("myapp://category/"));
            NavigateToCategory(CategoryName);
        }
        else if (DeepLinkUrl.StartsWith(TEXT("myapp://promo")))
        {
            NavigateToPromotions();
        }
        else if (DeepLinkUrl.StartsWith(TEXT("myapp://profile")))
        {
            NavigateToUserProfile();
        }
        else
        {
            // Default fallback
            UE_LOG(LogTemp, Warning, TEXT("Unknown deep link format, navigating to home"));
            NavigateToHome();
        }

        // Track deep link usage
        TrackDeepLinkEvent(DeepLinkUrl, IsDeferred);
    }

private:
    FString ExtractParameter(const FString& Url, const FString& Prefix)
    {
        FString Param;
        if (Url.Split(Prefix, nullptr, &Param))
        {
            // Remove query parameters if present
            FString CleanParam;
            if (Param.Split(TEXT("?"), &CleanParam, nullptr))
            {
                return CleanParam;
            }
            return Param;
        }
        return FString();
    }

    void NavigateToProduct(const FString& ProductId)
    {
        UE_LOG(LogTemp, Log, TEXT("Navigating to product: %s"), *ProductId);
        // Your product screen navigation logic
    }

    void NavigateToCategory(const FString& CategoryName)
    {
        UE_LOG(LogTemp, Log, TEXT("Navigating to category: %s"), *CategoryName);
        // Your category screen navigation logic
    }

    void NavigateToPromotions()
    {
        UE_LOG(LogTemp, Log, TEXT("Navigating to promotions"));
        // Your promotions screen navigation logic
    }

    void NavigateToUserProfile()
    {
        UE_LOG(LogTemp, Log, TEXT("Navigating to user profile"));
        // Your profile screen navigation logic
    }

    void NavigateToHome()
    {
        UE_LOG(LogTemp, Log, TEXT("Navigating to home screen"));
        // Your home screen navigation logic
    }

    void TrackDeepLinkEvent(const FString& DeepLink, bool IsDeferred)
    {
        // Track deep link usage with Singular
        TMap<FString, FString> EventAttributes;
        EventAttributes.Add(TEXT("deep_link_url"), DeepLink);
        EventAttributes.Add(TEXT("is_deferred"), IsDeferred ? TEXT("true") : TEXT("false"));

        USingularSDKBPLibrary::EventWithAttributes(
            TEXT("deep_link_opened"), 
            EventAttributes
        );
    }
};

모범 사례: Singular에서 사용자 지정 이벤트로 딥링크 오픈을 추적하여 딥링킹 캠페인의 효과를 측정하고 어떤 콘텐츠가 가장 많은 참여를 유도하는지 파악하세요.


중요 고려 사항

구현 참고 사항

  • 등록 시기: 항상 Singular SDK를 초기화하기 전에 딥링크 핸들러를 등록하세요. SDK가 추적 링크를 처리하기 전에 핸들러를 등록해야 합니다.
  • 스레드 안전: 핸들러 콜백은 백그라운드 스레드에서 실행될 수 있습니다. 언리얼 엔진의 스레드 안전 메커니즘을 사용하여 UI 업데이트 또는 내비게이션 로직이 게임 스레드로 전송되도록 하세요.
  • URL 스키마: 딥링크(예: myapp://product/, myapp://category/)에 일관된 URL 체계를 설계하여 라우팅 로직과 유지보수를 간소화하세요.
  • 폴백 처리: 인식되지 않거나 잘못된 딥링크에 대해 항상 폴백 탐색(일반적으로 홈 화면으로 이동)을 구현하여 앱 충돌이나 사용자 경험 저하를 방지하세요.
  • 두 흐름 모두 테스트하기: 즉시 딥링크(설치된 앱)와 디퍼드 딥링크(새로 설치)를 모두 테스트하여 모든 시나리오에서 적절하게 처리되는지 확인합니다.
  • 파라미터 유효성 검사: 딥링크에서 추출한 모든 파라미터를 사용하기 전에 유효성을 검사하여 악성 또는 잘못된 URL로 인한 보안 문제나 충돌을 방지합니다.
  • 플랫폼 구성: Singular 링크 전제 조건 가이드에 설명된 대로 플랫폼별 구성(iOS 유니버설 링크, Android 앱 링크)을 완료합니다.

프로덕션 체크리스트: 딥링킹 캠페인을 시작하기 전에 (1) SDK 초기화 전에 핸들러 등록, (2) 플랫폼별 구성 완료, (3) Singular 대시보드에서 딥링크 구성, (4) 라우팅 로직이 모든 예상 URL 패턴 처리, (5) 즉시 및 지연 흐름 모두 성공적으로 테스트되었는지 확인합니다.


추가 리소스