딥링킹 지원 추가
딥링크는사용자를 앱 내의 특정 콘텐츠로 연결합니다. 사용자가 앱이 설치된 기기에서 딥링크를 탭하면 앱이 제품 페이지나 특정 경험 등 의도한 콘텐츠로 바로 연결됩니다.
Singular 추적 링크는 표준 딥링킹(설치된 앱의 경우)과 디퍼드 딥링킹(신규 설치의 경우)을 모두 지원합니다. 자세한 내용은 딥링킹 FAQ및 Singular 링크 FAQ를 참조하세요.
요구 사항
전제 조건
iOS 및 Android 플랫폼에서 딥링킹을 사용하려면 Singular 링크 사전 요구 사항을완료하세요.
참고:
- 이 가이드에서는 2019년에 출시된 Singular의 추적 링크 기술인 Singular 링크를사용하고 있다고 가정합니다. 이전 고객은 레거시 트래킹 링크를 사용하고 있을 수 있습니다.
- 앱의 딥링크 대상은 Singular의 앱 페이지에서 구성해야 합니다( 딥링크 URL 구성하기 참조).
SDK 구성
Singular 링크 핸들러 구현
Singular SDK는 앱이 열릴 때 Singular 추적 링크에서 딥링크, 디퍼드 딥링크 및 패스스루 파라미터를 검색하는 델리게이트 기반 핸들러 메커니즘을 제공합니다.
사용 가능한 파라미터
- 딥링크: 링크를 클릭하는 사용자의 앱 내 대상 URL(링크 관리 페이지에서 구성)입니다.
- 패스스루: 추가 컨텍스트 또는 캠페인 메타데이터를 위해 추적 링크를 통해 전달되는 사용자 지정 데이터입니다.
- IsDeferred: 링크가 디퍼드 딥링크인지 여부를 나타냅니다(신규 인스톨의 경우 참, 기존 인스톨의 경우 거짓).
앱에 딥링크 핸들러 추가
앱이 실행될 때 수신되는 딥링크 데이터를 처리하도록 딥링크 핸들러를 구성합니다. 이 작업은 Singular SDK를 초기화하기 전에 수행해야 합니다.
구현 단계
- 클래스 헤더 파일에 필요한 Singular 포함을 추가합니다.
- OnSingularLinksResolved 콜백을 처리하는 메서드를 구현합니다.
- SDK를 초기화하기 전에 USingularDelegates 객체를 생성하고 등록합니다.
-
OnSingularLinksResolved.AddDynamic()를 사용하여 핸들러 메서드를 등록합니다. - 핸들러 메서드에서 딥링크 파라미터를 처리합니다.
// 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() 을 호출하기 전에 델리게이트를 등록해야 합니다. 초기화 후에 핸들러를 등록하면 딥링크 콜백을 받지 못합니다.
iOS SceneDelegate 구성
Unreal Engine이 iOS 애플리케이션 델리게이트를 소유하며 Singular 플러그인이 자동으로 연결되므로, Unreal의 기본 라이프사이클을 사용하는 앱에는 추가 코드가 필요하지 않습니다. 앱이 UIKit 씬을 사용하도록 설정한 경우에는 프로젝트가 SceneDelegate를 소유하게 되며, 플러그인은 SceneDelegate가 수신하는 링크를 인식할 수 없습니다. SingularLinkHandler 함수를 사용하여 링크를 명시적으로 전달하세요.
사용 가능 버전: Unreal Engine SDK 버전 2.7.0 이상. SDK가 초기화되기 전에 전달된 링크는 보관되었다가 Initialize가 실행되면 다시 처리되므로, 콜드 스타트 링크가 유실되지 않습니다.
씬 라이프사이클 링크 전달
Objective-C++ SceneDelegate 구현에 다음 세 가지 전달 호출을 추가하세요.
#import "IOS/SingularLinkHandler.h"
- (void)scene:(UIScene *)scene
willConnectToSession:(UISceneSession *)session
options:(UISceneConnectionOptions *)connectionOptions
{
// Cold-start custom-scheme URLs and universal links.
SingularLinkHandler::HandleSceneConnectionOptions(connectionOptions);
}
- (void)scene:(UIScene *)scene
openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts
{
// Warm custom-scheme URLs.
UIOpenURLContext *context = URLContexts.anyObject;
if (context.URL) {
SingularLinkHandler::HandleOpenURL(context.URL);
}
}
- (void)scene:(UIScene *)scene
continueUserActivity:(NSUserActivity *)userActivity
{
// Warm universal links.
SingularLinkHandler::HandleUniversalLink(userActivity);
}SingularLinkHandler 함수
| 함수 | 세부 정보 |
|---|---|
HandleSceneConnectionOptions |
파라미터:
|
HandleOpenURL |
파라미터: 이미 실행 중인 앱에 전달된 커스텀 스킴 URL의 경우 |
HandleUniversalLink |
파라미터: 이미 실행 중인 앱에 전달된 유니버설 링크의 경우 |
핸들러 동작
딥링크 해상도 이해
딥링크 핸들러는 사용자가 Singular 링크를 탭할 때 앱이 새로 설치되었는지 또는 이미 설치되었는지에 따라 다르게 동작합니다.
새로 설치(디퍼드 딥링크)
새로 설치한 경우, 앱이 실행될 때 오픈 URL이 존재하지 않습니다. Singular는 추적 링크에 딥링크 값이 포함되어 있는지 확인하기 위해 어트리뷰션을 완료합니다.
디퍼드 딥링크 흐름:
- 사용자가 딥링크 값으로 구성된 Singular 트래킹 링크를 클릭합니다.
- 사용자가 앱을 설치하고 처음으로 앱을 엽니다.
- Singular SDK가 첫 번째 세션을 Singular 서버로 전송합니다.
- 어트리뷰션이 완료되고 트래킹 링크에서 딥링크를 식별합니다.
-
딥링크 값은
IsDeferred = true로 핸들러에 반환됩니다.
디퍼드 딥링크 테스트:
- 테스트 기기에서 앱을 제거합니다(현재 설치되어 있는 경우).
- iOS: IDFA를 재설정합니다. 안드로이드:구글 광고 ID(GAID)를 재설정합니다.
- 기기에서 Singular 추적 링크를 클릭합니다(딥링크 값으로 구성되었는지 확인).
- 앱을 설치하고 엽니다.
어트리뷰션이 성공적으로 완료되고 디퍼드 딥링크 값이 핸들러에 전달됩니다.
개발 테스트 팁: 다른 패키지 이름이나 번들 ID를 사용하는 개발 빌드로 딥링크를 테스트하는 경우, 개발 앱의 식별자에 맞게 추적 링크를 구성하세요. 테스트 링크를 클릭한 후에는 앱 스토어에서 프로덕션 앱을 다운로드하지 말고 언리얼 엔진 또는 플랫폼별 툴을 통해 개발 빌드를 디바이스에 직접 설치하세요.
이미 설치됨(즉시 딥 링크)
앱이 이미 설치되어 있는 경우, Singular 링크를 클릭하면 유니버설 링크(iOS) 또는 Android 앱 링크 기술을 사용하여 앱이 즉시 열립니다.
즉시 딥링크 흐름:
- 사용자가 Singular 추적 링크를 클릭합니다.
- 운영 체제는 전체 Singular 추적 링크가 포함된 오픈 URL을 제공합니다.
- SDK 초기화 중에 Singular는 URL을 파싱합니다.
-
Singular는
deeplink및passthrough값을 추출합니다. -
값은 핸들러를 통해
IsDeferred = false로 반환됩니다.
패스스루 매개변수
캠페인 메타데이터, 사용자 세분화 또는 사용자 지정 정보에 대한 패스스루 매개변수를 사용하여 추적 링크 클릭에서 추가 데이터를 캡처합니다.
추적 링크에 passthrough (_p) 파라미터가 포함된 경우 핸들러의 Passthrough 파라미터에 해당 데이터가 포함됩니다.
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 을 반환합니다. |
메서드 서명
// 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 구조를 기반으로 사용자를 특정 콘텐츠로 안내합니다.
// 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) 즉시 및 지연 흐름 모두 성공적으로 테스트되었는지 확인합니다.