이 페이지에서는 Datadog Feature Flags SDK를 사용하여 Android 또는 Android TV 애플리케이션을 계측하는 방법을 설명합니다. Datadog Feature Flags는 앱의 기능 가용성을 원격으로 제어하고, 안전하게 실험하며, 새로운 경험을 안심하고 제공할 수 있는 통합된 방법을 제공합니다.
Android용 Datadog Feature Flags SDK는 Feature Flag 관리를 위한 개방형 표준인 OpenFeature를 기반으로 구축되었습니다. 이 가이드에서는 SDK를 설치하고, Datadog 공급자를 구성하며, 애플리케이션에서 Feature Flags를 평가하는 방법을 설명합니다.
대부분의 애플리케이션에는 OpenFeature API를 사용하는 것이 권장됩니다. 동일한 애플리케이션에서 여러 개의 독립적인 평가 컨텍스트가 필요한 경우 Direct FlagsClient 통합을 참조하세요.
시작
Android 앱에서 Feature Flags를 작동시키기 위한 최소한의 예시는 다음과 같습니다.
// 1. Add dependencies (see Installation section)
// 2. Initialize the Datadog Android SDK (in Application.onCreate)
valconfiguration=Configuration.Builder(clientToken="<CLIENT_TOKEN>",env="<ENV_NAME>",variant="<APP_VARIANT_NAME>").useSite(DatadogSite.).build()Datadog.initialize(this,configuration,TrackingConsent.GRANTED)// 3. Enable Feature Flags with a bounded initialization timeout
Flags.enable(FlagsConfiguration.Builder().initializationTimeout(2_000L).build())// 4. Create and set up the OpenFeature provider
valprovider=FlagsClient.Builder().build().asOpenFeatureProvider()OpenFeatureAPI.setProviderAndWait(provider)// 5. Set evaluation context (who is the user)
OpenFeatureAPI.setEvaluationContext(ImmutableContext(targetingKey="user-123",attributes=mapOf("tier"toValue.String("premium"))))// 6. Evaluate flags anywhere in your app
valclient=OpenFeatureAPI.getClient()valisEnabled=client.getBooleanValue("my-feature",false)
dependencies{implementation"com.datadoghq:dd-sdk-android-flags:<latest-version>"implementation"com.datadoghq:dd-sdk-android-flags-openfeature:<latest-version>"// Recommended: RUM integration drives analysis and enriches RUM session data
implementation"com.datadoghq:dd-sdk-android-rum:<latest-version>"}
SDK 초기화
앱 수명 주기에서 가능한 한 빨리, 일반적으로 Application 클래스의 onCreate() 메서드에서 Datadog을 초기화합니다. 이렇게 하면 모든 Feature Flag 평가 및 텔레메트리가 올바르게 캡처되도록 할 수 있습니다. 클라이언트 토큰을 생성하려면 클라이언트 토큰을 참조하세요.
FlagsClient를 생성한 다음 asOpenFeatureProvider() 확장 프로그램을 사용하여 OpenFeature 공급자로 변환하세요. 이 작업은 앱 시작 중에 한 번 수행하면 됩니다.
importcom.datadog.android.flags.FlagsClientimportcom.datadog.android.flags.openfeature.asOpenFeatureProviderimportdev.openfeature.kotlin.sdk.OpenFeatureAPI// Create and configure the provider
valprovider=FlagsClient.Builder().build().asOpenFeatureProvider()// Set it as the OpenFeature provider
OpenFeatureAPI.setProviderAndWait(provider)
OpenFeature 공급자는 Datadog을 내부적으로 FlagsClient 래핑합니다. 이는 구현 세부 사항입니다. 일단 설정되면 표준 OpenFeature API를 통해서만 상호 작용하게 됩니다.
OpenFeature Kotlin SDK는 단일 전역 공급자와 평가 컨텍스트를 사용합니다. 동일한 앱 내에서 여러 개의 독립적인 평가 컨텍스트가 필요한 경우(예: 다중 사용자 앱에 있는 서로 다른 사용자의 경우) Direct FlagsClient 통합을 참조하세요.
평가 컨텍스트 설정
ImmutableContext를 사용하여 플래그 평가가 적용되는 사람 및 대상을 정의하세요. 평가 컨텍스트에는 반환할 플래그 변형을 결정하는 데 사용되는 사용자 또는 세션 정보가 포함됩니다. 이러한 정보는 적절한 타겟팅을 보장하기 위해 플래그 평가 전에 설정합니다.
Datadog Feature Flags는 평가 컨텍스트 속성이 문자열, 숫자, 불리언과 같은 단일한 기본값이어야 합니다. 중첩된 객체나 배열은 전달하지 마세요. 지원되지 않으며 노출 데이터가 삭제될 수 있습니다.
OpenFeature 속성은 다음과 같은 Value 기본값만 사용해야 합니다. Value.String(), Value.Integer(), Value.Double(), 또는 Value.Boolean(). 타겟팅 키는 세션 간에 일관된 플래그 평가를 보장할 수 있도록 동일한 사용자에 대해 일관되어야 합니다. 익명 사용자의 경우, 예를 들어, SharedPreferences에 저장된 영구 UUID를 사용합니다.
플래그 평가
공급자와 평가 컨텍스트를 설정한 후에는 앱 전체에서 플래그 값을 읽을 수 있습니다. 플래그 평가는 로컬에서 즉시 수행됩니다. SDK는 로컬에 캐시된 데이터를 사용하므로 플래그를 평가할 때 네트워크 요청이 발생하지 않습니다. 이를 통해 메인 스레드에서 안전하게 평가를 수행할 수 있습니다.
각 플래그는 키(고유 문자열)로 식별되며 예상되는 유형의 값을 반환하는 유형화된 메서드로 평가할 수 있습니다. 각 Feature Flag가 존재하지 않거나 평가할 수 없는 경우, SDK는 제공된 기본값을 반환합니다.
플래그 값 이외의 추가 정보가 필요한 경우 평가된 값, 변형 이름, 이유 및 오류 코드를 포함한 자세한 평가 메타데이터를 얻을 수 있습니다.
valdetails=client.getStringDetails(key="paywall.layout",defaultValue="control")print(details.value)// Evaluated value (for example: "A", "B", or "control")
print(details.variant)// Variant name, if applicable
print(details.reason)// Reason for this value (for example: "TARGETING_MATCH" or "DEFAULT")
print(details.errorCode)// Error code, if any
다음과 같은 다른 유형에 대해서도 유사한 세부 정보 메서드가 존재합니다. getBooleanDetails(), getIntegerDetails(), getDoubleDetails() 및 getObjectDetails().
Feature Flag 세부 정보는 평가 동작을 디버깅하고 사용자가 특정 값을 받은 이유를 이해하는 데 도움이 됩니다.
공급자 이벤트 관찰
공급자 이벤트 관찰은 dd-sdk-android-flags-openfeature 3.6.0 이상에서 사용할 수 있습니다. 또한 dd-sdk-android-flags에 저장된 영구 UUID를 사용합니다.
OpenFeatureAPI.observe()를 사용하여 공급자 상태 변경에 대응하세요. Datadog OpenFeature 공급자는 기본 FlagsClient 상태에 따라 ProviderReady, ProviderStale 및 ProviderError를 전송합니다.
importdev.openfeature.kotlin.sdk.OpenFeatureAPIimportdev.openfeature.kotlin.sdk.events.OpenFeatureProviderEventsimportkotlinx.coroutines.flow.catchimportkotlinx.coroutines.launchvalstateJob=lifecycleScope.launch{OpenFeatureAPI.observe<OpenFeatureProviderEvents>().catch{// Handle Flow collection errors.
}.collect{event->when(event){isOpenFeatureProviderEvents.ProviderReady->{// The provider is ready to evaluate flags.
}isOpenFeatureProviderEvents.ProviderStale->{// Cached assignments are available, but they may be out of date.
}isOpenFeatureProviderEvents.ProviderError->{// The provider cannot evaluate flags.
}isOpenFeatureProviderEvents.ProviderConfigurationChanged->{// The provider configuration changed.
}else->{// Handle other OpenFeature provider events as needed.
}}}}
관찰 구성 요소가 중지되면 수집 작업을 취소합니다. 예를 들어, Android Activity 또는 Fragment에서 lifecycleScope을 수집하거나 ViewModel에서 viewModelScope을 수집합니다.
고급 구성
전역 구성
Flags.enable() API는 아래 나열된 옵션을 포함하는 선택적 구성을 허용합니다. 이러한 설정은 모든 공급자에 전역적으로 적용됩니다.
valconfig=FlagsConfiguration.Builder().initializationTimeout(2_000L)// configure additional options here
.build()Flags.enable(config)
initializationTimeout(timeoutMs)
첫 번째 평가 컨텍스트가 준비될 때까지 기다리는 최대 시간(밀리초)입니다. 시간 초과는 캐시된 데이터 로드, 할당 가져오기, 응답 읽기 및 디코딩, 할당 저장, 준비 상태 게시 과정에 적용됩니다. HTTP 클라이언트의 시간 초과에는 영향을 주지 않습니다. 할당 작업은 시간 초과 후에도 계속되며, 완료되면 클라이언트를 Ready로 이동시킬 수 있습니다.
시간 초과는 첫 번째 setEvaluationContext 호출에만 적용됩니다. 해당 호출은 작업이 실패하거나 시작되지 않더라도 시간 초과 시간이 소진됩니다. 이후 호출에는 초기화 타이머가 없습니다. 기본값은 5_000밀리초입니다. 시간 초과를 비활성화하려면 0 또는 음수 값으로 설정하세요. 일치하는 캐시된 할당을 사용할 수 있는 경우, 시간 초과 시 클라이언트는 Stale 상태가 됩니다. 그렇지 않으면 Error 상태가 됩니다.
initializationTimeout 를 사용하는 직접 클라이언트 상태 관찰은 dd-sdk-android-flags 3.14.0 이상에서 사용할 수 있습니다.
trackExposures()
true(기본값)인 경우 SDK는 플래그가 평가될 때 자동으로 _노출 이벤트_를 기록합니다. 이러한 이벤트에는 어떤 플래그에 액세스했는지, 어떤 변형이 제공되었는지, 그리고 어떤 컨텍스트에서 제공되었는지에 대한 메타데이터가 포함되어 있습니다. 이벤트는 Datadog으로 전송되므로 나중에 기능 채택을 분석할 수 있습니다. 텔레메트리 없이 로컬 평가만 필요한 경우 : trackExposures(false)를 사용하여 비활성화할 수 있습니다.
rumIntegrationEnabled()
true(기본값)인 경우 플래그 평가가 RUM에서 추적되므로 사용자 세션과 상관관계를 분석할 수 있습니다. 이를 통해 _“변형 B에서 사용자가 더 많은 오류를 경험하나요?”_와 같은 분석이 가능합니다. 앱에서 RUM을 사용하지 않는다면 이 플래그는 아무런 영향을 미치지 않으며 기본값으로 두어도 안전합니다. RUM 통합을 비활성화하려면 rumIntegrationEnabled(false)를 사용하세요.
gracefulModeEnabled()Flags.enable()을 호출하기 전에 클라이언트를 생성하거나, 동일한 이름으로 중복 클라이언트를 생성하거나, 아직 생성되지 않은 클라이언트를 검색하는 경우 등 SDK가 API의 잘못된 사용을 처리하는 방법을 제어합니다.
그레이스풀 모드의 정확한 동작은 빌드 구성에 따라 달라집니다.
릴리스 빌드:SDK가 항상 그레이스풀 모드 적용: Datadog.setVerbosity()가 구성된 경우에만 오용이 내부적으로 기록됩니다.
gracefulModeEnabled = true(기본값)인 * 디버그 빌드: SDK가 항상 콘솔에 경고를 기록합니다.
gracefulModeEnabled = false인 * 디버그 빌드: SDK가 잘못된 API 사용에 대해 IllegalStateException을 발생시켜 구성 실수를 조기에 탐지할 수 있도록 하는 fail-fast 접근 방식을 적용합니다.
개발 또는 QA 단계에 따라 gracefulModeEnabled()를 조정할 수 있습니다.
대부분의 애플리케이션에서는 위에서 설명한 OpenFeature API를 사용하는 것이 권장됩니다. 하지만 OpenFeature 추상화가 지원하지 않는 특정 요구 사항이 있는 경우 Datadog FlagsClient를 직접 사용할 수 있습니다.
FlagsClient는 다음과 같은 경우에만 직접 사용하세요.
동일한 앱 내에서 여러 개의 독립적인 평가 컨텍스트가 필요한 경우(예: 다중 사용자 앱에 있는 서로 다른 사용자의 경우)
네이티브 Kotlin 유형으로 직접 작업하려는 경우(Value.Structure 대신 JSONObject)
인스턴스별로 클라이언트 수명 주기 및 구성에 대한 세밀한 제어가 필요한 경우
설치(FlagsClient)
직접 API만 필요한 경우 OpenFeature 종속성을 생략할 수 있습니다.
build.gradle
dependencies{implementation"com.datadoghq:dd-sdk-android-flags:<latest-version>"// Recommended: RUM integration drives analysis and enriches RUM session data
implementation"com.datadoghq:dd-sdk-android-rum:<latest-version>"}
클라이언트(FlagsClient) 생성 및 검색
일반적으로 클라이언트는 앱 시작 중에 한 번 생성합니다.
FlagsClient.Builder().build()// Creates the default client
동일한 클라이언트는 앱 내 어디에서나 검색할 수 있습니다.
valflagsClient=FlagsClient.get()// Retrieves the "default" client
이 메서드는 백그라운드에서 서버로부터 플래그 할당을 비동기적으로 가져옵니다. 이 작업은 비차단 방식이며 스레드로부터 안전합니다. 플래그 업데이트는 백그라운드 작업이 완료된 후 후속 평가에 사용할 수 있습니다.
직접 클라이언트 상태 변경 관찰
다음 flagsClient.state 를 사용하는 직접 클라이언트 상태 관찰은 dd-sdk-android-flags 3.4.0 이상에서 사용 가능합니다.
flagsClient.state를 사용하여 현재 직접 클라이언트 상태를 검사하거나 상태 변경에 대한 리스너를 등록하세요.
importcom.datadog.android.flags.FlagsStateListenerimportcom.datadog.android.flags.model.FlagsClientStatevallistener=object: FlagsStateListener{overridefunonStateChanged(newState:FlagsClientState){when(newState){FlagsClientState.NotReady->{// The client has not loaded assignments yet.
}FlagsClientState.Reconciling->{// The client is fetching assignments for a context change.
}FlagsClientState.Ready->{// Assignments are loaded and available for evaluation.
}FlagsClientState.Stale->{// Cached assignments are available, but the latest fetch failed.
}isFlagsClientState.Error->{// No assignments are available for evaluation.
}}}}flagsClient.state.addListener(listener)valcurrentState=flagsClient.state.getCurrentState()
리스너는 등록 시 현재 상태를 수신한 다음 이후 상태 변경 사항을 수신합니다. 콜백을 빠르게 유지하고 오래 걸리는 작업은 다른 스레드로 전달합니다. 관찰 중인 구성 요소가 중지될 때 flagsClient.state.removeListener(listener)를 호출합니다.
valdetails=flagsClient.resolve(flagKey="paywall.layout",defaultValue="control")print(details.value)// Evaluated value (for example: "A", "B", or "control")
print(details.variant)// Variant name, if applicable
print(details.reason)// Description of why this value was chosen
print(details.errorCode)// The error that occurred during evaluation, if any
API 비교
이 표는 OpenFeature와 FlagsClient API 간의 주요 차이점을 강조해서 보여주고 요구 사항에 맞는 통합을 선택하는 데 도움을 줍니다.
기능
OpenFeature
FlagsClient
API 표준
OpenFeature(벤더 중립적)
Datadog 전용
평가 컨텍스트
전역/정적
클라이언트 인스턴스당
구조화된 플래그
Value.Structure
JSONObject
유형 안전성
OpenFeature Value 유형
Kotlin 네이티브 유형
벤더 종속
낮음(벤더 중립적)
높음(Datadog 전용)
상태 관리
흐름 기반 관찰
수동 리스너 등록
테스트
실제 Datadog 공급자를 사용하여 전용 Datadog 테스트 환경에서 테스트하거나, 인메모리 FeatureProvider로 교체하여 테스트 코드에서 직접 플래그 값을 제어할 수 있습니다. 이 섹션에서는 테스트를 독립적이고 오프라인 상태로 유지하는 인메모리 방식을 보여줍니다. 업스트림 OpenFeature Kotlin SDK는 InMemoryProvider를 제공하지 않으므로, 테스트에서는 소규모로 사용자 지정 FeatureProvider를 사용합니다. 아래 예시는 OpenFeatureAPI의 공급자를 대체합니다. 프로덕션 코드에서 Datadog FlagsClient 래퍼를 직접 사용하는 경우, 테스트는 FlagsClient가 아니라 래퍼가 사용하는 동일한 OpenFeatureAPI 클라이언트를 통해 어설션해야 합니다.
테스트 구성에 kotlinx-coroutines-test를 추가합니다(SDK의 initialize는 suspend 함수임).
importdev.openfeature.kotlin.sdk.*importdev.openfeature.kotlin.sdk.events.OpenFeatureProviderEventsimportkotlinx.coroutines.flow.Flowimportkotlinx.coroutines.flow.MutableSharedFlowimportkotlinx.coroutines.test.runTestimportorg.junit.Beforeimportorg.junit.Testimportkotlin.test.assertTrueclassFakeProvider(privatevalflags:Map<String,Any>):FeatureProvider{overridevalhooks=emptyList<Hook<*>>()overridevalmetadata=object: ProviderMetadata{overridevalname="fake"}privatevalevents=MutableSharedFlow<OpenFeatureProviderEvents>(replay=1)overridesuspendfuninitialize(initialContext:EvaluationContext?){// No-op. The SDK emits ProviderReady after initialize returns.
}overridefunshutdown(){}overridesuspendfunonContextSet(old:EvaluationContext?,new:EvaluationContext){}overridefungetBooleanEvaluation(key:String,defaultValue:Boolean,context:EvaluationContext?)=ProviderEvaluation(value=(flags[key]as?Boolean)?:defaultValue)overridefungetStringEvaluation(key:String,defaultValue:String,context:EvaluationContext?)=ProviderEvaluation(value=(flags[key]as?String)?:defaultValue)overridefungetIntegerEvaluation(key:String,defaultValue:Int,context:EvaluationContext?)=ProviderEvaluation(value=(flags[key]as?Int)?:defaultValue)overridefungetDoubleEvaluation(key:String,defaultValue:Double,context:EvaluationContext?)=ProviderEvaluation(value=(flags[key]as?Double)?:defaultValue)overridefungetObjectEvaluation(key:String,defaultValue:Value,context:EvaluationContext?)=ProviderEvaluation(value=(flags[key]as?Value)?:defaultValue)overridefunobserve():Flow<OpenFeatureProviderEvents>=events}classCheckoutFlagsTest{privatelateinitvarclient:Client@BeforefunsetUp()=runTest{OpenFeatureAPI.setProviderAndWait(FakeProvider(mapOf("new-checkout-flow"totrue)))client=OpenFeatureAPI.getClient()}@TestfunnewCheckoutEnabled(){assertTrue(client.getBooleanValue("new-checkout-flow",false))}}
OpenFeatureAPI는 프로세스 전체 싱글톤이므로, 테스트가 JVM을 공유하는 경우 테스트 클래스 간에 이를 재설정합니다. setProviderAndWait를 runTest { ... }에서 래핑합니다. suspend이므로 일반 @Before 메서드에서 호출할 수 없습니다.