이 페이지는 AI로 자동 번역되었으며 부정확한 내용이 포함될 수 있습니다. 번역 오류를 신고하려면 페이지 오른쪽 목차 아래에 있는 피드백 기능을 사용하세요.
Content Cards에서 배너로 마이그레이션
이 가이드는 배너 스타일 메시징 사용 사례를 위해 Content Cards에서 배너로 마이그레이션하는 데 도움을 줍니다. 배너는 애플리케이션의 특정 위치에 표시되는 인라인, 지속적인 인앱 및 웹 메시지에 이상적입니다.
배너로 마이그레이션해야 하는 이유
- 엔지니어링 팀이 커스텀 Content Cards를 구축하거나 유지 관리하고 있다면, 배너로 마이그레이션하면 지속적인 투자를 줄일 수 있습니다. 배너를 사용하면 마케터가 UI를 직접 제어할 수 있어 개발자가 다른 작업에 집중할 수 있습니다.
- 새로운 홈페이지 메시지, 온보딩 플로우 또는 지속적인 공지를 시작하려는 경우, Content Cards 위에 구축하는 대신 배너로 시작하세요. 실시간 개인화, 30일 만료 제한 없음, 크기 제한 없음, 네이티브 우선순위 지정 등의 이점을 처음부터 누릴 수 있습니다.
- 30일 만료 제한을 우회하거나, 복잡한 재자격 로직을 관리하거나, 오래된 개인화에 불만이 있다면, 배너가 이러한 문제를 기본적으로 해결합니다.
배너는 배너 스타일 메시징에서 Content Cards보다 여러 가지 장점을 제공합니다.
가속화된 제작
- 지속적인 엔지니어링 지원 필요성 감소: 마케터가 드래그 앤 드롭 편집기와 커스텀 HTML을 사용하여 커스텀 메시지를 작성할 수 있으며, 커스터마이징을 위해 개발자의 도움이 필요하지 않습니다
- 유연한 커스터마이징 옵션: 편집기에서 직접 디자인하거나, HTML을 사용하거나, 커스텀 속성으로 기존 데이터 모델을 활용할 수 있습니다
더 나은 UX
- 동적 콘텐츠 업데이트: 배너는 새로고침할 때마다 Liquid 로직과 자격 조건을 갱신하여 사용자가 항상 가장 관련성 높은 콘텐츠를 볼 수 있도록 합니다
- 네이티브 배치 지원: 메시지가 피드가 아닌 특정 상황에 표시되어 더 나은 상황별 관련성을 제공합니다
- 네이티브 우선순위 지정: 커스텀 로직 없이 표시 순서를 제어할 수 있어 메시지 계층 구조를 더 쉽게 관리할 수 있습니다
지속성
- 만료 제한 없음: 배너 Campaigns에는 Content Cards와 같은 30일 만료 제한이 없어 메시지를 진정으로 지속적으로 유지할 수 있습니다
마이그레이션 시기
다음과 같은 용도로 Content Cards를 사용하고 있다면 배너로의 마이그레이션을 고려해 보세요:
- 홈페이지 히어로, 제품 페이지 프로모션, 결제 오퍼
- 지속적인 내비게이션 공지 또는 사이드바 메시지
- 30일 이상 실행되는 상시 메시지
- 실시간 개인화 및 자격 조건을 적용하려는 메시지
Content Cards를 계속 사용해야 하는 경우
다음이 필요한 경우 Content Cards를 계속 사용하세요:
- 피드 경험: 여러 개의 스크롤 가능한 메시지 또는 카드 기반 “받은편지함”이 포함된 모든 사용 사례.
- 특정 기능: 프로모션 코드가 필요한 메시지. 배너는 이를 기본적으로 지원하지 않습니다. 배너는 얼리 액세스로 연결된 콘텐츠를 지원합니다.
- 트리거 전달: API 트리거 또는 실행 기반 전달이 반드시 필요한 사용 사례. 배너는 API 트리거 또는 실행 기반 전달을 지원하지 않지만, 실시간 자격 평가를 통해 사용자는 새로고침할 때마다 Segment 멤버십에 따라 즉시 자격을 얻거나 잃게 됩니다.
마이그레이션 가이드
사전 요구 사항
마이그레이션하기 전에 Braze SDK가 최소 버전 요구 사항을 충족하는지 확인합니다:
해제 및 재적격성에는 다음 최소 SDK 버전이 필요합니다:
업데이트 구독
Content Cards 접근 방식
1
2
3
4
5
6
7
8
| import * as braze from "@braze/web-sdk";
braze.subscribeToContentCardsUpdates((cards) => {
// Handle array of cards
cards.forEach(card => {
console.log("Card:", card.id);
});
});
|
1
2
3
4
5
6
| Braze.getInstance(context).subscribeToContentCardsUpdates { cards ->
// Handle array of cards
cards.forEach { card ->
Log.d(TAG, "Card: ${card.id}")
}
}
|
1
2
3
4
5
6
| braze.contentCards.subscribeToUpdates { cards in
// Handle array of cards
for card in cards {
print("Card: \(card.id)")
}
}
|
1
2
3
4
5
6
7
| Braze.addListener(Braze.Events.CONTENT_CARDS_UPDATED, (update) => {
const cards = update.cards;
// Handle array of cards
cards.forEach(card => {
console.log("Card:", card.id);
});
});
|
1
2
3
4
5
6
| StreamSubscription contentCardsStreamSubscription = braze.subscribeToContentCards((List<BrazeContentCard> contentCards) {
// Handle array of cards
for (final card in contentCards) {
print("Card: ${card.id}");
}
});
|
배너 접근 방식
1
2
3
4
5
6
7
8
9
| import * as braze from "@braze/web-sdk";
braze.subscribeToBannersUpdates((banners) => {
// Get banner for specific placement
const banner = braze.getBanner("sample_placement_id");
if (banner) {
console.log("Banner received for placement:", banner.placementId);
}
});
|
1
2
3
4
5
6
7
| Braze.getInstance(context).subscribeToBannersUpdates { update ->
// Get banner for specific placement
val banner = Braze.getInstance(context).getBanner("sample_placement_id")
if (banner != null) {
Log.d(TAG, "Banner received for placement: ${banner.placementId}")
}
}
|
1
2
3
4
5
6
7
8
| braze.banners.subscribeToUpdates { banners in
// Get banner for specific placement
braze.banners.getBanner(for: "sample_placement_id") { banner in
guard let banner = banner else { return }
print("Banner received for placement: \(banner.placementId)")
}
}
|
1
2
3
4
5
6
7
8
9
| Braze.addListener(Braze.Events.BANNER_CARDS_UPDATED, (data) => {
const banners = data.banners;
// Get banner for specific placement
Braze.getBanner("sample_placement_id").then(banner => {
if (banner) {
console.log("Banner received for placement:", banner.placementId);
}
});
});
|
1
2
3
4
5
6
7
8
| StreamSubscription bannerStreamSubscription = braze.subscribeToBanners((List<BrazeBanner> banners) {
// Get banner for specific placement
braze.getBanner("sample_placement_id").then((banner) {
if (banner != null) {
print("Banner received for placement: ${banner.placementId}");
}
});
});
|
콘텐츠 표시

참고
Content Cards는 커스텀 UI 로직으로 수동 렌더링할 수 있지만, 배너는 기본 제공 SDK 메서드로만 렌더링할 수 있습니다.
Content Cards 접근 방식
1
2
3
4
5
6
7
8
9
10
11
| // Show default feed UI
braze.showContentCards(document.getElementById("feed"));
// Or manually render cards
const cards = braze.getCachedContentCards();
cards.forEach(card => {
// Custom rendering logic
if (card instanceof braze.ClassicCard) {
// Render classic card
}
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // Using default fragment
val fragment = ContentCardsFragment()
supportFragmentManager.beginTransaction()
.replace(R.id.content_cards_container, fragment)
.commit()
// Or manually render cards
val cards = Braze.getInstance(context).getCachedContentCards()
cards.forEach { card ->
when (card) {
is ClassicCard -> {
// Render classic card
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // Using default view controller
let contentCardsController = BrazeContentCardUI.ViewController(braze: braze)
navigationController?.pushViewController(contentCardsController, animated: true)
// Or manually render cards
let cards = braze.contentCards.cards
for card in cards {
switch card {
case let card as Braze.ContentCard.Classic:
// Render classic card
default:
break
}
}
|
1
2
3
4
5
6
7
8
9
10
| // Launch default feed
Braze.launchContentCards();
// Or manually render cards
const cards = await Braze.getCachedContentCards();
cards.forEach(card => {
if (card.type === 'CLASSIC') {
// Render classic card
}
});
|
1
2
3
4
5
6
7
8
9
10
| // Launch default feed
braze.launchContentCards();
// Or manually render cards
final cards = await braze.getContentCards();
for (final card in cards) {
if (card.type == 'CLASSIC') {
// Render classic card
}
}
|
배너 접근 방식
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| braze.subscribeToBannersUpdates((banners) => {
const banner = braze.getBanner("sample_placement_id");
if (!banner) {
return;
}
const container = document.getElementById("global-banner-container");
braze.insertBanner(banner, container);
if (banner.isControl) {
container.style.display = "none";
}
});
braze.requestBannersRefresh(["sample_placement_id"]);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // Using BannerView in XML
// <com.braze.ui.banners.BannerView
// android:id="@+id/banner_view"
// android:layout_width="match_parent"
// android:layout_height="wrap_content"
// app:placementId="sample_placement_id" />
// Or programmatically
val bannerView = BannerView(context).apply {
placementId = "sample_placement_id"
}
container.addView(bannerView)
Braze.getInstance(context).requestBannersRefresh(listOf("sample_placement_id"))
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // Using BannerUIView
let bannerView = BrazeBannerUI.BannerUIView(
placementId: "sample_placement_id",
braze: braze,
processContentUpdates: { result in
switch result {
case .success(let updates):
if let height = updates.height {
// Update height constraint
}
case .failure:
break
}
}
)
view.addSubview(bannerView)
braze.banners.requestBannersRefresh(placementIds: ["sample_placement_id"])
|
1
2
3
4
5
6
7
8
9
10
11
12
| // Using BrazeBannerView component
<Braze.BrazeBannerView
placementId='sample_placement_id'
/>
// Or get banner data
const banner = await Braze.getBanner("sample_placement_id");
if (banner) {
// Render custom banner UI
}
Braze.requestBannersRefresh(["sample_placement_id"]);
|
1
2
3
4
5
6
7
8
9
10
11
12
| // Using BrazeBannerView widget
BrazeBannerView(
placementId: "sample_placement_id",
)
// Or get banner data
final banner = await braze.getBanner("sample_placement_id");
if (banner != null) {
// Render custom banner UI
}
braze.requestBannersRefresh(["sample_placement_id"]);
|
분석 로깅 (커스텀 구현)

참고
Content Cards와 배너 모두 기본 UI 컴포넌트를 사용할 때 분석을 자동으로 추적합니다. 다음 예시는 자체 UI를 구축하는 커스텀 구현을 위한 것입니다.
Content Cards 접근 방식
1
2
3
4
5
6
7
| // Manual impression logging required for custom implementations
cards.forEach(card => {
braze.logContentCardImpressions([card]);
});
// Manual click logging required for custom implementations
card.logClick();
|
1
2
3
4
5
6
7
| // Manual impression logging required for custom implementations
cards.forEach { card ->
card.logImpression()
}
// Manual click logging required for custom implementations
card.logClick()
|
1
2
3
4
5
6
7
| // Manual impression logging required for custom implementations
for card in cards {
card.context?.logImpression()
}
// Manual click logging required for custom implementations
card.context?.logClick()
|
1
2
3
4
5
6
7
| // Manual impression logging required for custom implementations
cards.forEach(card => {
Braze.logContentCardImpression(card.id);
});
// Manual click logging required for custom implementations
Braze.logContentCardClicked(card.id);
|
1
2
3
4
5
6
7
| // Manual impression logging required for custom implementations
for (final card in cards) {
braze.logContentCardImpression(card);
}
// Manual click logging required for custom implementations
braze.logContentCardClicked(card);
|
배너 접근 방식

중요
insertBanner()를 사용할 때 분석이 자동으로 추적됩니다. insertBanner()를 사용하는 경우 수동 로깅을 사용하면 안 됩니다.
1
2
3
4
5
6
7
8
9
| // Analytics are automatically tracked when using insertBanner()
// Manual logging should not be used when using insertBanner()
// For custom implementations, use manual logging methods:
// Log impression
braze.logBannerImpressions([banner]);
// Log click (with optional buttonId)
braze.logBannerClick("sample_placement_id", buttonId);
|

중요
BannerView를 사용할 때 분석이 자동으로 추적됩니다. BannerView를 사용하는 경우 수동 로깅을 사용하면 안 됩니다.
1
2
3
4
5
6
7
8
9
| // Analytics are automatically tracked when using BannerView
// Manual logging should not be used for default BannerView
// For custom implementations, use manual logging methods:
// Log impression
Braze.getInstance(context).logBannerImpression("sample_placement_id");
// Log click (with optional buttonId)
Braze.getInstance(context).logBannerClick("sample_placement_id", buttonId);
|

중요
BannerUIView를 사용할 때 분석이 자동으로 추적됩니다. 기본 BannerUIView에서는 수동 로깅을 사용하면 안 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // Analytics are automatically tracked when using BannerUIView
// Manual logging should not be used for default BannerUIView
// For custom implementations, use manual logging methods:
// Get banner for specific placement
braze.banners.getBanner(for: "sample_placement_id") { banner in
guard let banner = banner else { return }
// Log impression
banner.context?.logImpression()
// Log click (with optional buttonId)
banner.context?.logClick(buttonId: buttonId)
}
// Control groups are automatically handled by BannerUIView
|

중요
BrazeBannerView를 사용할 때 분석이 자동으로 추적됩니다. 수동 로깅이 필요하지 않습니다.
1
2
3
4
5
| // Analytics are automatically tracked when using BrazeBannerView
// No manual logging required
// Note: Manual logging methods for Banners are not yet supported in React Native
// Control groups are automatically handled by BrazeBannerView
|

중요
BrazeBannerView를 사용할 때 분석이 자동으로 추적됩니다. 수동 로깅이 필요하지 않습니다.
1
2
3
4
5
| // Analytics are automatically tracked when using BrazeBannerView
// No manual logging required
// Note: Manual logging methods for Banners are not yet supported in Flutter
// Control groups are automatically handled by BrazeBannerView
|
속성 가져오기
Content Cards 접근 방식
1
2
3
| cards.forEach(card => {
console.log("Card id:", card.id, "Extras:", card.extras);
});
|
1
2
3
| cards.forEach { card ->
Log.d(TAG, "Card id: ${card.id} Extras: ${card.extras}")
}
|
1
2
3
| for card in cards {
print("Card id: \(card.id) Extras: \(card.extras)")
}
|
1
2
3
| cards.forEach(card => {
console.log("Card id:", card.id, "Extras:", card.extras);
});
|
1
2
3
| for (final card in cards) {
print("Card id: ${card.id} Extras: ${card.extras}");
}
|
배너 접근 방식
1
2
3
4
5
6
| const banner = braze.getBanner("sample_placement_id");
if (!banner) {
return;
}
console.log("Banner placement:", banner.placementId, "Properties:", banner.properties);
|
1
2
3
4
| val banner = Braze.getInstance(context).getBanner("sample_placement_id")
if (banner != null) {
Log.d(TAG, "Banner placement: ${banner.placementId} Properties: ${banner.properties}")
}
|
1
2
3
4
5
| braze.banners.getBanner(for: "sample_placement_id") { banner in
guard let banner = banner else { return }
print("Banner placement: \(banner.placementId) Properties: \(banner.properties)")
}
|
1
2
3
4
| const banner = await Braze.getBanner("sample_placement_id");
if (banner) {
console.log("Banner placement:", banner.placementId, "Properties:", banner.properties);
}
|
1
2
3
4
| final banner = await braze.getBanner("sample_placement_id");
if (banner != null) {
print("Banner placement: ${banner.placementId} Properties: ${banner.properties}");
}
|
대조군 처리
Content Cards 접근 방식
1
2
3
4
5
6
7
| cards.forEach(card => {
if (card.isControl) {
// Logic for control cards ie. don't display but log analytics
} else {
// Logic for cards ie. render card
}
});
|
1
2
3
4
5
6
7
| cards.forEach { card ->
if (card.isControl) {
// Logic for control cards ie. don't display but log analytics
} else {
// Logic for cards ie. render card
}
}
|
1
2
3
4
5
6
7
| for card in cards {
if card.isControl {
// Logic for control cards ie. don't display but log analytics
} else {
// Logic for cards ie. render card
}
}
|
1
2
3
4
5
6
7
| cards.forEach(card => {
if (card.isControl) {
// Logic for control cards ie. don't display but log analytics
} else {
// Logic for cards ie. render card
}
});
|
1
2
3
4
5
6
7
| for (final card in cards) {
if (card.isControl) {
// Logic for control cards ie. don't display but log analytics
} else {
// Logic for cards ie. render card
}
}
|
배너 접근 방식
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| braze.subscribeToBannersUpdates((banners) => {
const banner = braze.getBanner("sample_placement_id");
if (!banner) {
return;
}
const container = document.getElementById("global-banner-container");
// Always call insertBanner to track impression (including control)
braze.insertBanner(banner, container);
// Hide if control group
if (banner.isControl) {
container.style.display = "none";
}
});
|
1
2
3
4
5
| // BannerView automatically handles control groups
// No additional code needed
val bannerView = BannerView(context).apply {
placementId = "sample_placement_id"
}
|
1
2
3
4
5
6
| // BannerUIView automatically handles control groups
// No additional code needed
let bannerView = BrazeBannerUI.BannerUIView(
placementId: "sample_placement_id",
braze: braze
)
|
1
2
3
4
5
| // BrazeBannerView automatically handles control groups
// No additional code needed
<Braze.BrazeBannerView
placementId='sample_placement_id'
/>
|
1
2
3
4
5
| // BrazeBannerView automatically handles control groups
// No additional code needed
BrazeBannerView(
placementId: "sample_placement_id",
)
|
제한 사항
Content Cards에서 배너로 마이그레이션할 때 다음 제한 사항에 유의하세요:
트리거된 메시지 마이그레이션
배너는 예약 전달 Campaign만 지원합니다. 이전에 API 트리거 또는 실행 기반으로 전달되던 메시지를 마이그레이션하려면 Segment 기반 타겟팅으로 변환하세요:
- 예시: API로 “프로필 완성” 카드를 트리거하는 대신, 최근 7일 이내에 가입했지만 프로필을 완성하지 않은 사용자를 위한 Segment를 생성하세요.
- 실시간 자격: 사용자는 새로고침할 때마다 Segment 멤버십에 따라 즉시 배너 자격을 얻거나 잃게 됩니다.
기능 차이
| 기능 |
Content Cards |
배너 |
| 콘텐츠 구조 |
|
|
| 피드 내 여러 카드 |
✅ 지원됨 |
✅ 여러 배치를 생성하여 캐러셀과 유사한 구현이 가능합니다. 배치당 하나의 배너만 반환됩니다. |
| 여러 배치 |
해당 없음 |
✅ 여러 배치 지원 |
| 카드 유형 (클래식, 캡션, 이미지 전용) |
✅ 여러 사전 정의 유형 |
✅ 단일 HTML 기반 배너 (더 유연함) |
| 콘텐츠 관리 |
|
|
| 드래그 앤 드롭 편집기 |
❌ 커스터마이징에 개발자 필요 |
✅ 마케터가 엔지니어링 없이 생성/업데이트 가능 |
| 커스텀 HTML/CSS |
❌ 카드 구조로 제한됨 |
✅ 전체 HTML/CSS 지원 |
| 커스터마이징을 위한 키-값 페어 |
✅ 고급 커스터마이징에 필요 |
✅ 고급 커스터마이징을 위한 “속성”이라 불리는 강력한 타입의 키-값 페어 |
| 메시지 추가 정보 |
✅ 지원됨 |
❌ 현재 지원되지 않음 |
| 지속성 및 만료 |
|
|
| 카드 만료 |
✅ 지원됨 (30일 제한) |
✅ 지원됨 (만료 제한 없음) |
| 완전한 지속성 |
❌ 최대 30일 |
✅ 무제한 지속성 |
| 표시 및 타겟팅 |
|
|
| 피드 UI |
✅ 기본 피드 사용 가능 |
❌ 배치 기반만 지원 |
| 상황별 배치 |
❌ 피드 기반 |
✅ 네이티브 배치 지원 |
| 우선순위 지정 |
❌ 커스텀 로직 필요 |
✅ 네이티브 우선순위 지정 |
| 사용자 상호작용 |
|
|
| 수동 해제 |
✅ 지원됨 |
✅ 지원됨 |
| 해제 후 재자격 |
❌ 커스텀 필터 또는 Campaign 로직 필요 |
✅ 기본 대기 기간 |
| 고정된 카드 |
✅ 지원됨 |
해당 없음 |
| 분석 |
|
|
| 자동 분석 (기본 UI) |
✅ 지원됨 |
✅ 지원됨 |
| 우선순위 정렬 |
❌ 지원되지 않음 |
✅ 지원됨 |
| 콘텐츠 업데이트 |
|
|
| Liquid 템플릿 새로고침 |
❌ 전송/실행 시 카드당 한 번 |
✅ 매 새로고침마다 갱신 |
| 자격 새로고침 |
❌ 전송/실행 시 카드당 한 번 |
✅ 매 세션마다 갱신 |
제품 제한 사항
- 배치당 최대 25개의 활성 메시지.
- 새로고침 요청당 최대 10개의 배치 ID. 이를 초과하는 요청은 잘립니다.
SDK 제한 사항
- 배너는 현재 .NET MAUI (Xamarin), Cordova, Unity, Vega 또는 TV 플랫폼에서 지원되지 않습니다.
- 사전 요구 사항에 나열된 최소 SDK 버전을 사용하고 있는지 확인하세요.
관련 문서