본문으로 건너뛰기

Jetpack Compose

본 가이드에서는 Jetpack Compose 기반으로 작성된 UI 에서의 GFP Android SDK 연동 방식을 설명합니다.

warning

androidx.compose:compose-bom:2024.09.03 미만의 버전에서는 Compose 버그로 인해 impression 또는 click 등의 이벤트가 잘 발화되지 않을 수 있습니다. 정상적인 광고 연동을 위해 2024.09.03 이상의 버전을 사용해주세요.

Getting Started

Jetpack Compose 기반 UI에서의 GFP SDK 광고 렌더링은 기본적으로 AndroidView 를 사용하여 렌더링합니다. 이 때 리컴포지션으로 인한 불필요한 광고 로딩이나 중복 로딩을 방지하게 위해, 광고 로드에 필요한 AdParam 객체를 remember로 관리하거나 광고를 렌더링 할 컴포저블의 매개 변수로 넘깁니다. 광고 로딩 시에는 GfpAdLoader 가 중복 생성되거나 동일한 AdParam 으로 중복 요청을 하지 않도록 AdParam 을 key 로 갖는 LaunchedEffect 또는 DisposiableEffect 안에서 GfpAdLoader 를 통해 광고를 로드합니다. 광고 로딩이 완료되면 광고 유형에 따라 AdView 또는 AdManager 객체를 이용해 광고를 렌더링합니다. 광고 표시를 종료하거나 화면을 벗어나는 경우에는 AndroidView.onRelease 또는 DisposableEffect 를 통해 자원을 해제합니다.


Banner 는 로드 방식에 따라 GfpAdLoader 를 사용하는 방식과 사용하지 않는 방식을 지원합니다. GfpAdLoader 를 사용해 로딩할 경우, 로딩 완료 시에 GfpBannerAdView 를 전달받아 State 로 관리하여 광고를 표시합니다.

With GfpAdLoader

@Composable
fun BannerAdView(
adParam: AdParam,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
var adLoader by remember { mutableStateOf<GfpAdLoader?>(null) }
var bannerAdView by remember { mutableStateOf<GfpBannerAdView?>(null) }

LaunchedEffect(adParam) {
adLoader = GfpAdLoader.Builder(context, adParam)
.withAdListener(/* . . . */)
.withBannerAd {
bannerAdView = it
}
.build()
.also { loadAd() }
}

bannerAdView?.let { adView ->
AndroidView(
modifier = modifier,
factory = { adView },
onRelease = { view ->
view.destroy()
adLoader?.cancel()
}
)
}
}

Without GfpAdLoader

@Composable
fun BannerAdView(
adParam: AdParam,
modifier: Modifier = Modifier
) {
AndroidView(
modifier = modifier,
factory = { context ->
GfpBannerAdView(context, adParam).apply {
setAdListener(/* . . . */)
loadAd()
}
},
onRelease = { view ->
view.destroy()
}
)
}

Native Normal

GFP SDK 의 Native Normal 광고는 아직 Android View 렌더링만을 지원하며, Compose 환경에서는 AndroidViewBinding 을 이용해 렌더링하게 됩니다. 연동 시에 광고를 렌더링 할 xml 형식의 layout 파일과 compose.ui:ui-viewbinding 의존성 및 gradle 설정이 필요합니다.

buildFeatures {
compose = true
viewBinding = true
}
dependencies {
// . . .
implementation("androidx.compose.ui:ui-viewbinding:<compose-version>")
}
@Composable
fun NativeAdView(
adParam: AdParam,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
var adLoader by remember { mutableStateOf<GfpAdLoader?>(null) }
var nativeAd by remember { mutableStateOf<GfpNativeAd?>(null) }

LaunchedEffect(adParam) {
adLoader = GfpAdLoader.Builder(context, adParam)
.withAdListener(/* . . . */)
.withNativeAd {
nativeAd = it
}
.build()
.also { loadAd() }
}

nativeAd?.let { ad ->
AndroidViewBinding(
factory = LayoutNativeAdBinding::inflate,
modifier = modifier,
onRelease = { adLoader?.cancel() }
) {
with(nativeAdView) {
assetsContainer = assetsContainerLayout
adChoicesView = adChoices
mediaView = media
titleView = title.apply { text = ad.title }
bodyView = body.apply { text = ad.body }
callToActionView = callToAction.apply { text = ad.callToAction }
advertiserView = advertiser.apply { text = ad.advertiserName }
iconView = icon.apply {
ad.icon?.let { setImageDrawable(it.drawable) } ?: run { visibility = GONE }
}
socialContextView = socialContext.apply {
ad.socialContext?.let { text = it } ?: run { visibility = GONE }
}
noticeView = notice.apply {
ad.notice?.let { text = it } ?: run { visibility = GONE }
}
setNativeAd(ad)
}
}
}
}
정보

layout_native_ad.xml 코드는 생략합니다.


Native Simple

GfpAdLoader 를 통해 로드가 완료되면 전달받은 GfpNativeSimpleAd 객체를 State 로 관리하고, AndroidViewfactory 또는 update 를 통해 GfpNativeSimpleAdView 에 설정합니다.

@Composable
fun NativeSimpleAdView(
adParam: AdParam,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
var adLoader by remember { mutableStateOf<GfpAdLoader?>(null) }
var nativeSimpleAd by remember { mutableStateOf<GfpNativeSimpleAd?>(null) }

LaunchedEffect(adParam) {
adLoader = GfpAdLoader.Builder(context, adParam)
.withAdListener(/* . . . */)
.withNativeSimpleAd {
nativeSimpleAd = it
}
.build()
.also { loadAd() }
}

nativeSimpleAd?.let { ad ->
AndroidView(
modifier = modifier,
factory = { context -> GfpNativeSimpleAdView(context) },
update = { view -> view.setNativeSimpleAd(ad) },
onRelease = { adLoader?.cancel() }
)
}
}

Rewarded & Interstitial

보상형과 전면형의 경우, AdManager 를 통해 광고를 로드하고 AdEventListener 로부터 전달받은 광고 로드 완료 여부를 State 로 관리하여 로드 완료 시 광고 표시 버튼을 나타나게 합니다. 이 때 showAd 함수에 전달되는 contextActivity 로 캐스팅이 필요하며, 자원 해제 시에는 AndroidView 를 사용하지 않으므로 DisposableEffectonDispose 를 통해 destroy() 를 호출해야 합니다.

Rewarded

@Composable
fun RewardedAdView(
adParam: AdParam,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
var adManager by remember { mutableStateOf<GfpRewardedAdManager?>(null) }
var isAdLoaded by remember { mutableStateOf(false) }

DisposableEffect(adParam) {
adManager = GfpRewardedAdManager(context, adParam).apply {
setAdListener(object : RewardedAdListener() {
override fun onAdLoaded(ad: GfpRewardedAd) {
isAdLoaded = true
}
})
loadAd()
}

onDispose {
adManager?.destroy()
}
}

if (isAdLoaded) {
Button(
onClick = { adManager?.showAd(context as Activity) },
modifier = modifier
) {
Text(text = "Show Rewarded AD")
}
}
}

Interstitial

@Composable
fun InterstitialAdView(
adParam: AdParam,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
var adManager by remember { mutableStateOf<GfpInterstitialAdManager?>(null) }
var isAdLoaded by remember { mutableStateOf(false) }

DisposableEffect(adParam) {
adManager = GfpInterstitialAdManager(context, adParam).apply {
setAdListener(object : InterstitialAdListener() {
override fun onAdLoaded(ad: GfpInterstitialAd) {
isAdLoaded = true
}
})
loadAd()
}

onDispose {
adManager?.destroy()
}
}

if (isAdLoaded) {
Button(
onClick = { adManager?.showAd(context as Activity) },
modifier = modifier
) {
Text(text = "Show Interstitial AD")
}
}
}

정보

전체 코드 샘플은 샘플앱 저장소에서 확인하실 수 있습니다.