UI 설정
전체 테마
Buzzvil SDK에서 제공하는 기본 UI를 사용해 광고 지면이나 CTA의 색상, 아이콘 이미지 등 테마를 변경할 수 있습니다.

주요 색상
Buzzvil SDK에서 제공하는 UI 중 다이얼로그의 버튼 색상, 바텀 시트 UI의 확인 버튼 등 앱에 연동한 모든 지면 내 주요 UI의 색상(Primary color)을 변경할 수 있습니다.

Primary 색상 변경을 위해 아래와 같이 색상 리소스를 추가하세요.
import BuzzvilSDK
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
let config = ...
// BuzzBenefit.initialize()를 호출하기 전에 GlobalTheme을 설정합니다.
let theme = BuzzBenefitTheme { builder in
builder.primaryColor = YOUR_PRIMARY_COLOR
builder.primaryLightColor = YOUR_PRIMARY_LIGHT_COLOR
}
BuzzBenefit.shared.setGlobalTheme(theme)
BuzzBenefit.shared.initialize(with: config)
return true
}
}
@import BuzzvilSDK;
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
BuzzBenefitConfig *config = ...
// BuzzBenefit.initialize()를 호출하기 전에 GlobalTheme을 설정합니다.
BuzzBenefitTheme *theme = [BuzzBenefitTheme themeWithBlock:^(BuzzBenefitThemeBuilder * _Nonnull builder) {
builder.primaryColor = YOUR_PRIMARY_COLOR;
builder.primaryLightColor = YOUR_PRIMARY_LIGHT_COLOR;
}];
[[BuzzBenefit sharedInstance] setGlobalTheme:theme];
[[BuzzBenefit sharedInstance] initializeWithConfig:config];
return YES;
}
@end
리워드 아이콘
Buzzvil SDK에서 제공하는 UI 중 CTA 버튼 등의 아이콘을 변경할 수 있습니다.
다음은 BuzzBenefitTheme에 rewardIcon를 추가해 리워드 아이콘을 변경하는 예시입니다. 각 속성 값에서 설정할 수 있는 UI에 대해서는 아래의 표를 참고하세요.
| 속성 값 | 설정 가능한 UI |
|---|---|
rewardIcon | 모든 지면: CTA 뷰 리워드 아이콘, 베네핏허브: 유저 프로필 입력 배너 리워드 아이콘, 베네핏허브: 팝 옵트인 버튼 아이콘 |
participatedIcon | 모든 지면: CTA 뷰 |
import BuzzvilSDK
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
let config = ...
// BuzzBenefit.initialize()를 호출하기 전에 GlobalTheme을 설정합니다.
let theme = BuzzBenefitTheme { builder in
builder.rewardIcon = YOUR_REWARD_ICON
builder.participatedIcon = YOUR_PARTICIPATED_ICON
}
BuzzBenefit.shared.setGlobalTheme(theme)
BuzzBenefit.shared.initialize(with: config)
return true
}
}
@import BuzzvilSDK;
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
BuzzBenefitConfig *config = ...
// BuzzBenefit.initialize()를 호출하기 전에 GlobalTheme을 설정합니다.
BuzzBenefitTheme *theme = [BuzzBenefitTheme themeWithBlock:^(BuzzBenefitThemeBuilder * _Nonnull builder) {
builder.rewardIcon = YOUR_REWARD_ICON;
builder.participatedIcon = YOUR_PARTICIPATED_ICON;
}];
[[BuzzBenefit sharedInstance] setGlobalTheme:theme];
[[BuzzBenefit sharedInstance] initializeWithConfig:config];
return YES;
}
@end
CTA 버튼 자체 구현하기
Buzzvil SDK에서 기본으로 제공하는 UI를 사용하지 않고 CTA 버튼을 자체적으로 구현할 수 있습니다. 자체적으로 구현한 CTA 버튼을 베네핏허브와 네이티브에서 사용할 수 있습니다.

CTA 버튼을 직접 구현하려면 CtaViewProtocol 프로토콜을 준수하는 UIView 클래스의 서브클래스를 생성하세요.
다음은 UIStackView를 활용하여 CTA View 클래스를 생성한 예시입니다.
import UIKit
import BuzzvilSDK
final class CustomCtaView: UIStackView, BZVCtaViewProtocol {
let rewardImageView = UIImageView(frame: .zero)
let rewardLabel = UILabel(frame: .zero)
let ctaLabel = UILabel(frame: .zero)
required init(coder: NSCoder) {
super.init(coder: coder)
setUpView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}
private func setUpView() {
addArrangedSubview(rewardImageView)
addArrangedSubview(rewardLabel)
addArrangedSubview(ctaLabel)
}
// MARK: BZVCtaViewProtocol
func renderRewardNotAvailableViewState(withCtaText ctaText: String) {
// 리워드가 없는 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
rewardImageView.isHidden = true
rewardLabel.isHidden = true
ctaLabel.text = ctaText
}
func renderRewardAvailableViewState(withCtaText ctaText: String, reward: Int) {
// 리워드가 있는 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
rewardImageView.isHidden = false
rewardLabel.isHidden = false
rewardImageView.image = UIImage(named: "YOUR_REWARD_IMAGE")
rewardLabel.text = "+\(reward)"
ctaLabel.text = ctaText
}
func renderParticipatingViewState(withCtaText ctaText: String) {
// 참여 확인 중인 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
rewardImageView.isHidden = true
rewardLabel.isHidden = true
ctaLabel.text = "YOUR_PARTICIPATING_TEXT"
}
func renderParticipatedViewState(withCtaText ctaText: String) {
// 참여 완료한 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
rewardImageView.isHidden = false
rewardLabel.isHidden = true
rewardImageView.image = UIImage(named: "YOUR_PARTICIPATED_IMAGE")
ctaLabel.text = "YOUR_PARTICIPATED_TEXT"
}
}
@import UIKit;
@import BuzzvilSDK;
@interface CustomCtaView : UIStackView <BZVCtaViewProtocol>
@end
@interface CustomCtaView ()
@property (nonatomic, strong, readonly) UIImageView *rewardImageView;
@property (nonatomic, strong, readonly) UILabel *rewardLabel;
@property (nonatomic, strong, readonly) UILabel *ctaLabel;
@end
@implementation CustomCtaView
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
_rewardImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
[self addArrangedSubview:_rewardImageView];
_rewardLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self addArrangedSubview:_rewardLabel];
_ctaLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self addArrangedSubview:_ctaLabel];
}
#pragma mark - BZVCtaViewProtocol
- (void)renderRewardNotAvailableViewStateWithCtaText:(NSString *)ctaText {
// 리워드가 없는 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
[_rewardImageView setHidden:YES];
[_rewardLabel setHidden:YES];
[_ctaLabel setText:ctaText];
}
- (void)renderRewardAvailableViewStateWithCtaText:(NSString *)ctaText reward:(NSInteger)reward {
// 리워드가 있는 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
[_rewardImageView setHidden:NO];
[_rewardLabel setHidden:NO];
[_rewardImageView setImage:[UIImage imageNamed:@"YOUR_REWARD_IMAGE"]];
[_rewardLabel setText:[NSString stringWithFormat:@"+%ld", reward]];
[_ctaLabel setText:ctaText];
}
- (void)renderParticipatingViewStateWithCtaText:(NSString *)ctaText {
// 참여 확인 중인 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
[_rewardImageView setHidden:YES];
[_rewardLabel setHidden:YES];
[_ctaLabel setText:@"YOUR_PARTICIPATING_TEXT"];
}
- (void)renderParticipatedViewStateWithCtaText:(NSString *)ctaText {
// 참여 완료한 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
[_rewardImageView setHidden:NO];
[_rewardLabel setHidden:YES];
[_rewardImageView setImage:[UIImage imageNamed:@"YOUR_PARTICIPATED_IMAGE"]];
[_ctaLabel setText:@"YOUR_PARTICIPATED_TEXT"];
}
@end
자체 구현한 CTA 버튼을 GlobalTheme에 적용하기
BuzzBenefitTheme에 ctaViewClass를 추가해 자체 구현한 CTA 버튼을 적용하세요.
import BuzzvilSDK
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
let config = ...
// BuzzBenefit.initialize()를 호출하기 전에 GlobalTheme을 설정합니다.
let theme = BuzzBenefitTheme { builder in
// ...
builder.ctaViewClass = CustomCtaView.self
}
BuzzBenefit.shared.setGlobalTheme(theme)
// ...
return true
}
}
@import BuzzvilSDK;
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
BuzzBenefitConfig *config = ...
// BuzzBenefit.initialize()를 호출하기 전에 GlobalTheme을 설정합니다.
BuzzBenefitTheme *theme = [BuzzBenefitTheme themeWithBlock:^(BuzzBenefitThemeBuilder * _Nonnull builder) {
// ...
builder.ctaViewClass = [CustomCtaView class];
}];
[[BuzzBenefit sharedInstance] setGlobalTheme:theme];
// ...
return YES;
}
@end
네이티브
네이티브 오버레이 UI
버즈빌 대시 어드민을 통해 오버레이 UI를 구성하는 이미지와 텍스트를 설정할 수 있습니다.
네이티브 오버레이 UI를 설정하려면 다음의 절차를 따르세요.
- 버즈빌 대시 어드민(https://dashboard.buzzvil.com/) 에 접속하세요.
- Inventory Manager에서 앱을 검색하고 선택한 후 왼쪽 메뉴 패널에서 Entry Point Contents 메뉴로 이동하세요.
- 진입점 유형 드롭다운 목록에서 ‘네이티브 오버레이’를 선택하세요.
- 오버레이 유형별로 커스터마이징 토글을 켜세요.
- 원하는 이미지와 UI 스트링을 등록하세요.
- 설정한 UI를 실시간으로 미리보기할 수 있습니다.
- 오버레이 UI를 설정하지 않으면 버즈빌이 제공하는 UI가 적용됩니다.
- 이미지와 UI 스트링이 지원하는 규격은 다음과 같습니다.
- 이미지 포맷: PNG, JPG, JSON(Lottie)
- 파일 크기: 최대 2 MB
- 이미지 비율: 312 x 212 (가로 x 세로)
- UI 스트링 글자 수 & 줄 수: 최대 20 자 (공백 포함) & 두 줄
- 오른쪽 상단의 저장하기 버튼을 클릭하세요.

베네핏허브
Buzzvil SDK에서 제공하는 UI의 디자인을 변경하기 위한 방법을 안내합니다.
럭키박스
SDK 패치 없이 UI 스트링 및 이미지 소재를 설정하여 럭키박스의 스타일을 원하는 대로 설정할 수 있습니다. 설정하고 싶은 항목과 내용을 버즈빌 담당자(help@buzzvil.com)에게 전달해 주세요.

| 항목 | 설명 |
|---|---|
| 프로모션 명 | 럭키박스 상세 페이지 상단에 노출되는 페이지 타이틀 문구, 공백 포함 최대 15자까지 입력할 수 있습니다., 기본값 : 오늘의 선물상자 |
| 참여 전후 진입점 | 베네핏허브에 노출되는 진입점 아이콘과 문구, 기본값 : 오늘의 선물상자 (참여 전)/ 내일 다시 만나요!(참여 후) |
| 참여도 영역 아이콘 | Day 1~Day 5 참여 일자 UI의 참여 전 아이콘, 참여 완료 아이콘, 부활 아이콘, 연속 참여 성공 보너스 리워드, 24 x 24 (가로 x 세로), PNG, JPG, JSON(Lottie), 최대 2MB |
| 럭키박스 참여 전후 CTA | 럭키박스 참여 시 클릭하는 CTA UI, 기본값 : 선물 상자를 열어보세요 (참여 전) / 참여 완료! 내일 다시 만나요 (참여 후) |
| 럭키박스 참여 전후 이미지 | 럭키박스 참여 CTA 위의 이미지, 100 x 100 (가로 x 세로), PNG, JPG, JSON(Lottie), 최대 2MB |
| 추가보상 배너 | 럭키박스 참여 후 럭키박스 배너 하단에 나타나는 CPC 상품 전용 배너, 24 x 24 (가로 x 세로), PNG, JPG, JSON(Lottie), 최대 2MB, 배너 문구 기본값 : 버튼 누르고 보너스 받기 |
| 일일 리워드 금액 | 하루 한 번 유저에게 지급하는 럭키박스 일별 리워드 금액, 기본값 : 1 |
| 연속 참여 성공 리워드 금액 | 5일 연속 참여 달성 시 유저에게 5일 째에 추가로 지급하는 리워드 금액, 기본값 : 2 |
| 연속 참여 성공 문구 | 연속 참여 성공 리워드 지급을 알리는 UI의 문구, 기본값 : 연속 참여 성공 했어요! |
| 연속 참여 성공 문구 색상 | 연속 참여 성공 문구 색상, 단위: Hex, 기본값 : FFFFFF |
| 연속 참여 성공 아이콘 | 연속 참여 성공 리워드 지급을 알리는 UI의 아이콘, 80 x 80 (가로 x 세로), PNG, JPG, JSON(Lottie), 최대 2MB |
| 연속 참여 성공 주변 효과 | 연속 참여 성공 아이콘 주변의 효과 이미지, 375 x 375 (가로 x 세로), PNG, JPG, JSON(Lottie), 최대 2MB |
| 유의 사항 | 럭키박스 운영 및 리워드 지급 정책에 대한 안내 및 면책 조항, 리워드 금액 크기에 대한 안내를 수정할 수 있습니다. |
미션팩
SDK 패치 없이 UI 스트링 및 이미지 소재를 설정하여 미션팩의 스타일을 원하는 대로 설정할 수 있습니다. 설정하고 싶은 항목과 내용을 버즈빌 담당자(help@buzzvil.com)에게 전달해 주세요.

| 항목 | 세부항목 | 설명 |
|---|---|---|
| 베네핏허브 진입점 | 타이틀 | 베네핏허브 진입점(타이틀, 아이콘) 이미지 참고 |
| 베네핏허브 진입점 | 아이콘 | 베네핏허브 진입점(타이틀, 아이콘) 이미지 참고 |
| 배경 | 색상 | BuzzBenefitTheme에 primaryColor을 통해 각 요소별 색상을 적용하세요. |
| 배경 | 그라데이션 효과 | BuzzBenefitTheme에 backgroundGradientColors를 배경 색상을 적용하세요. (미션팩 배경 Gradation 설정하기 참고) |
| 프로모션 타이틀 | 텍스트 | - |
| 미션 보너스 | 지급 비율 | 유저가 완료한 미션의 리워드 중 n%를 미션 보너스로 지급하는 구조, 미션 보너스에서 소수점 발생 시 반올림 처리 |
| 미션 보너스 CTA | 텍스트 | - |
| 도전 중 미션 | 도전 중 타이틀 | - |
| 도전 중 미션 | 도전 중 아이콘 | PNG, JPG, JSON(Lottie) 지원 |
| 도전 중 미션 | 도전 중 미션 설명 | "UI & 미션 설정 영역" 이미지 참고, 미션 보너스 비율은 지급 비율을 따름 |
| 도전 중 미션 | 도전 중 미션 CTA | 최대 6글자 |
| 도전 중 미션 | 미션 완료 CTA | 최대 6글자 |
| 참여 완료 | 참여 완료 타이틀 | - |
| 미션 구성 | 미션 타이틀 | - |
| 미션 구성 | 설정 기준 | revenue type: CPI, CPE, CPQ, CPQLITE, CPCQUIZ, CPYoutube, CPInsta, CPK, CPTiktok, CPL, CPYLike, CPNstore, ad type (허브 광고 필터): 클릭적립, 참여적립, SNS적립, 콘텐츠 적립, 쇼핑적립, 영상적립, event type + resource type 조합: 이벤트 기능 (예: 럭키박스) |
| 미션 구성 | 미션별 진행도 | 미션 구성하는 광고 갯수 |
| 럭키박스 진입점 | CTA | 최대 6글자 |
| 유의 사항 | - | 디폴트 문구에서 리워드 지급 비율은 미션 보너스 지금 비율 값을 자동으로 가져옴 |
| 성과 기록 유닛 | - |
|
| 광고 인정 유닛 | - | 미션팩에 구성한 미션으로 참여 인정을 하는 유닛, ✅ 중요 각 미션팩은 1개 또는 여러 개의 광고 인정 유닛을 가질 수 있습니다. |
미션팩 배경 Gradation 설정하기
import BuzzvilSDK
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
let config = ...
// BuzzBenefit.initialize()를 호출하기 전에 GlobalTheme을 설정합니다.
let theme = BuzzBenefitTheme { builder in
// ...
builder.backgroundGradientColors = [YOUR_GRADIENT_UICOLOR, ...]
}
BuzzBenefit.shared.setGlobalTheme(theme)
// ...
return true
}
}
@import BuzzvilSDK;
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
BuzzBenefitConfig *config = ...
// BuzzBenefit.initialize()를 호출하기 전에 GlobalTheme을 설정합니다.
BuzzBenefitTheme *theme = [BuzzBenefitTheme themeWithBlock:^(BuzzBenefitThemeBuilder * _Nonnull builder) {
// ...
builder.backgroundGradientColors = @[YOUR_GRADIENT_UICOLOR, ...];
}];
[[BuzzBenefit sharedInstance] setGlobalTheme:theme];
// ...
return YES;
}
@end
내비게이션 바
베네핏허브 내비게이션 바의 UI 스트링을 수정하거나 내비게이션 바 영역을 자체 구현할 수 있습니다.

내비게이션 바 UI 스트링 변경하기
내비게이션 바의 UI 스트링을 변경할 수 있습니다.
let feedTheme = BZVFeedTheme { builder in
builder.navigationBarTitle = "YOUR_TITLE"
}
BZVBuzzAdFeed.setDefaultTheme(feedTheme)
BZVFeedTheme *feedTheme = [BZVFeedTheme themeWithBlock:^(BZVFeedThemeBuilder * _Nonnull builder) {
builder.navigationBarTitle = @"YOUR_TITLE";
}];
[BZVBuzzAdFeed setDefaultTheme:feedTheme];
내비게이션 바 자체 구현하기
하위 뷰 컨트롤러로 베네핏허브 연동하기 항목을 참고하여 내비게이션 바를 자체 구현할 수 있습니다.
헤더
베네핏허브 상단의 헤더를 원하는 목적에 맞게 자유롭게 활용할 수 있습니다. 예를 들어, 베네핏허브 영역을 설명하는 공간으로 활용하거나 적립 가능한 금액을 표시할 수도 있습니다.

다음은 헤더 영역을 변경하는 예시입니다.
BZVFeedHeaderView를 상속하는 클래스를 생성하고 Custom View를 헤더 영역에 구현합니다. 그리고 BZVFeedConfig의 headerViewClass 속성에 새로 구현한 클래스를 추가합니다.
import UIKit
import BuzzvilSDK
final class CustomFeedHeaderView: BZVFeedHeaderView {
let headerLabel = UILabel(frame: .zero)
override class func desiredHeight(_ width: CGFloat) -> CGFloat {
return 60
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
func setupView() {
addSubview(headerLabel)
// LayoutConstraint 설정
// ...
}
override func availableRewardDidUpdate(_ reward: Int) {
headerLabel.text = "리워드 \(reward)원"
}
}
@import UIKit;
@import BuzzvilSDK;
@interface CustomFeedHeaderView : BZVFeedHeaderView
@end
@interface CustomFeedHeaderView ()
@property (nonatomic, strong, readonly) UILabel *headerLabel;
@end
@implementation CustomFeedHeaderView
+ (CGFloat)desiredHeight:(CGFloat)width {
return 60;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
_headerLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self addSubview:_headerLabel];
// LayoutConstraint 설정
// ...
}
- (void)availableRewardDidUpdate:(NSInteger)reward {
_headerLabel.text = [NSString stringWithFormat:@"리워드 %ld원", reward];
}
@end
let feedConfig = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.headerViewClass = CustomFeedHeaderView.self
}
BZVFeedConfig *feedConfig = [BZVFeedConfig configWithBlock:^(BZVFeedConfigBuilder * _Nonnull builder) {
builder.unitId = @"YOUR_FEED_UNIT_ID";
builder.headerViewClass = [CustomFeedHeaderView class];
}];
광고 분류 필터
광고 분류 필터의 기본 색상은 monochrome이며, primary color를 따라가도록 변경할 수 있습니다.

let feedTheme = BZVFeedTheme { builder in
builder.usePrimaryColorInFilter = true
}
BZVBuzzAdFeed.setDefaultTheme(feedTheme)
BZVFeedTheme *feedTheme = [BZVFeedTheme themeWithBlock:^(BZVFeedThemeBuilder * _Nonnull builder) {
builder.usePrimaryColorInFilter = YES;
}];
[BZVBuzzAdFeed setDefaultTheme:feedTheme];
광고 디자인
Buzzvil SDK에서 제공하는 광고는 일반 광고와 쇼핑 광고가 있습니다.
- 일반 광고: 쇼핑 광고를 제외한 나머지 유형의 광고입니다.
- 쇼핑 광고: 유저가 광고를 통해 물건 구매를 달성하는 경우 유저에게 구매 금액의 일정 비율을 포인트로 돌려주는 광고입니다.

두 유형 모두 광고를 표현할 수 있는 다양한 정보로 구성됩니다. 일반 광고와 쇼핑 광고를 구성하는 요소와 UI에 대해서는 아래의 표를 참고하세요.
| 광고 유형 | 구성 요소 | 설명 |
|---|---|---|
| 공통(일반, 쇼핑) | 광고 제목 (필수) | 광고의 제목입니다. 최대 10자까지 권장하며, 필요에 따라 글자 수에 상관 없이 일정 부분은 생략 부호로 대체할 수 있습니다. |
| 광고 소재 (필수) |
| |
| 광고 설명 (필수) | 광고에 대한 상세 설명입니다. 최대 40자까지 권장하며, 필요에 따라 글자 수에 상관 없이 일정 부분은 생략 부호로 대체할 수 있습니다. | |
| 광고주 아이콘 (필수) |
| |
| CTA 버튼 (필수) |
✏️ 참고 CTA 버튼의 디자인을 변경하려면 CTA 버튼 자체 구현하기 토픽을 참고하세요. | |
| 클릭 가능한 영역 설정 (필수) | 광고의 클릭 가능한 영역을 설정하는 기능합니다. | |
| 광고 알림 문구 (권장) | 광고임을 명시하는 문구입니다. (예: 광고, ad, 스폰서, Sponsored) | |
| 쇼핑 적립 | OriginalPrice View (권장) | 상품의 원가를 표시합니다. |
| Price View (권장) | 상품의 할인된 가격을 표시합니다. | |
| DiscountRate View (권장) | 상품 가격의 할인율을 표시합니다. 할인율은 원가와 할인가로 비교하여 산출해서 표시해야 합니다. |
일반 광고 디자인 자체 구현하기
일반 광고의 디자인을 변경하려면 다음의 절차를 따르세요.
- 일반 광고용
BZVFeedAdView의 상속 클래스를 구현하세요.
- 하위 뷰로
BZVNativeAdView,BZVMediaView,BZVDefaultCtaView클래스를 추가하세요. renderAd에서BZVNativeAdViewBinder를 이용하여 광고 데이터(NativeAd)를 앞서 생성한CustomFeedAdView에 바인딩하세요.
import UIKit
import BuzzvilSDK
final class CustomFeedAdView: BZVFeedAdView {
let nativeAdView = BZVNativeAdView(frame: .zero)
let mediaView = BZVMediaView(frame: .zero)
let iconImageView = UIImageView(frame: .zero)
let titleLabel = UILabel(frame: .zero)
let descriptionLabel = UILabel(frame: .zero)
let ctaView = BZVDefaultCtaView(frame: .zero)
lazy var viewBinder = BZVNativeAdViewBinder { builder in
builder.nativeAdView = self.nativeAdView
builder.mediaView = self.mediaView
builder.iconImageView = self.iconImageView
builder.titleLabel = self.titleLabel
builder.descriptionLabel = self.descriptionLabel
builder.ctaView = self.ctaView
builder.clickableViews = [
self.mediaView,
self.ctaView
]
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
override class func desiredHeight(_ width: CGFloat) -> CGFloat {
return width * 0.8
}
func setupView() {
addSubview(nativeAdView)
nativeAdView.addSubview(mediaView)
nativeAdView.addSubview(iconImageView)
nativeAdView.addSubview(titleLabel)
nativeAdView.addSubview(descriptionLabel)
nativeAdView.addSubview(ctaView)
// LayoutConstraint 설정
// ...
}
override func renderAd(_ ad: BZVNativeAd) {
viewBinder.bind(with: ad)
}
}
@import UIKit;
@import BuzzvilSDK;
@interface CustomFeedAdView : BZVFeedAdView
@end
@interface CustomFeedAdView ()
@property (nonatomic, strong, readonly) BZVNativeAdView *nativeAdView;
@property (nonatomic, strong, readonly) BZVMediaView *mediaView;
@property (nonatomic, strong, readonly) UIImageView *iconImageView;
@property (nonatomic, strong, readonly) UILabel *titleLabel;
@property (nonatomic, strong, readonly) UILabel *descriptionLabel;
@property (nonatomic, strong, readonly) BZVDefaultCtaView *ctaView;
@property (nonatomic, strong, readonly) BZVNativeAdViewBinder *viewBinder;
@end
@implementation CustomFeedAdView
+ (CGFloat)desiredHeight:(CGFloat)width {
return width * 0.8;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
_nativeAdView = [[BZVNativeAdView alloc] initWithFrame:CGRectZero];
[self addSubview:_nativeAdView];
_mediaView = [[BZVMediaView alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_mediaView];
_iconImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_iconImageView];
_titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_titleLabel];
_descriptionLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_descriptionLabel];
_ctaView = [[BZVDefaultCtaView alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_ctaView];
_viewBinder = [BZVNativeAdViewBinder viewBinderWithBlock:^(BZVNativeAdViewBinderBuilder * _Nonnull builder) {
builder.nativeAdView = self.nativeAdView;
builder.mediaView = self.mediaView;
builder.iconImageView = self.iconImageView;
builder.titleLabel = self.titleLabel;
builder.descriptionLabel = self.descriptionLabel;
builder.ctaView = self.ctaView;
builder.clickableViews = @[
self.mediaView,
self.ctaView
];
}];
// LayoutConstraint 설정
// ...
}
- (void)renderAd:(BZVNativeAd *)ad {
[_viewBinder bindWithNativeAd:ad];
}
@end
BZVNativeAdEventDelegate을 통해 광고 콜백 이벤트 수신 기능을 부가적으로 추가할 수도 있습니다.
import UIKit
import BuzzvilSDK
final class CustomFeedAdView: BZVFeedAdView, BZVNativeAdEventDelegate {
override func renderAd(_ ad: BZVNativeAd) {
viewBinder.bind(with: ad)
ad.delegate = self
}
// MARK: BZVNativeAdEventDelegate
func didImpress(_ nativeAd: BZVNativeAd) {
}
func didClick(_ nativeAd: BZVNativeAd) {
}
func didRequestReward(for nativeAd: BZVNativeAd) {
}
func didReward(for nativeAd: BZVNativeAd, with result: BZVRewardResult) {
}
func didParticipateAd(_ nativeAd: BZVNativeAd) {
}
}
@import UIKit;
@import BuzzvilSDK;
// 부가 기능: 광고 콜백 이벤트 수신 기능을 추가합니다.
@interface CustomFeedAdView () <BZVNativeAdEventDelegate>
@end
@implementation CustomFeedAdView
- (void)renderAd:(BZVNativeAd *)ad {
[_viewBinder bindWithNativeAd:ad];
ad.delegate = self;
}
#pragma mark - BZVNativeAdEventDelegate
- (void)didImpressAd:(BZVNativeAd *)nativeAd {
}
- (void)didClickAd:(BZVNativeAd *)nativeAd {
}
- (void)didRequestRewardForAd:(BZVNativeAd *)nativeAd {
}
- (void)didRewardForAd:(BZVNativeAd *)nativeAd withResult:(BZVRewardResult)result {
}
- (void)didParticipateAd:(BZVNativeAd *)nativeAd {
}
@end
BZVFeedConfig에 위에서 작성한CustomFeedAdView를 광고 뷰 홀더로 설정하세요.
let feedConfig = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.adViewClass = CustomFeedAdView.self
}
BZVFeedConfig *config = [BZVFeedConfig configWithBlock:^(BZVFeedConfigBuilder * _Nonnull builder) {
builder.unitId = @"YOUR_FEED_UNIT_ID";
builder.adViewClass = [CustomFeedAdView class];
}];
쇼핑 적립 광고 디자인 자체 구현하기
쇼핑 적립 광고의 디자인을 변경하려면 다음의 절차를 따르세요.
- 일반 광고 디자인 자체 구현하기 토픽을 참고해
BZVFeedAdView의 상속 클래스를 구현하세요.
- 일반 광고용 레이아웃에서
priceLabel,originalPriceLabel,discountPercentageLabel을 추가해야 합니다.
import UIKit
import BuzzvilSDK
final class CustomFeedCpsAdView: BZVFeedAdView {
let nativeAdView = BZVNativeAdView(frame: .zero)
let mediaView = BZVMediaView(frame: .zero)
let iconImageView = UIImageView(frame: .zero)
let titleLabel = UILabel(frame: .zero)
let descriptionLabel = UILabel(frame: .zero)
let ctaView = BZVDefaultCtaView(frame: .zero)
let priceLabel = UILabel(frame: .zero)
let originalPriceLabel = UILabel(frame: .zero)
let discountRateLabel = UILabel(frame: .zero)
lazy var viewBinder = BZVNativeAdViewBinder { builder in
builder.nativeAdView = self.nativeAdView
builder.mediaView = self.mediaView
builder.iconImageView = self.iconImageView
builder.titleLabel = self.titleLabel
builder.descriptionLabel = self.descriptionLabel
builder.ctaView = self.ctaView
builder.clickableViews = [
self.mediaView,
self.ctaView,
self.priceLabel,
self.originalPriceLabel,
self.discountRateLabel,
]
}
override class func desiredHeight(_ width: CGFloat) -> CGFloat {
return 350
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setUpView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}
private func setUpView() {
// 광고 레이아웃 컴포넌트를 생성합니다.
// ...생략...
nativeAdView.addSubview(priceLabel)
nativeAdView.addSubview(originalPriceLabel)
nativeAdView.addSubview(discountRateLabel)
// LayoutConstraint 설정
// ...생략...
}
override func renderAd(_ ad: BZVNativeAd) {
viewBinder.bind(with: ad)
let product = ad.product
if product.discountedPrice != 0 {
// 할인이 있는 쇼핑 광고
priceLabel.text = NSNumber(value: product.discountedPrice).formattedString()
originalPriceLabel.text = NSNumber(value: product.price).formattedString()
let discountRate = (1 - product.discountedPrice / product.price) * 100
discountRateLabel.text = String(format: "%d%%", Int(discountRate))
} else {
// 할인이 없는 쇼핑 광고
priceLabel.text = NSNumber(value: product.discountedPrice).formattedString()
originalPriceLabel.text = NSNumber(value: product.price).formattedString()
discountRateLabel.text = "0%%"
}
}
}
extension NSNumber {
func formattedString() -> String? {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
return numberFormatter.string(from: self)
}
}
@import UIKit;
@import BuzzvilSDK;
@interface CustomFeedCpsAdView : BZVFeedAdView
@end
@interface CustomFeedCpsAdView ()
@property (nonatomic, strong, readonly) BZVNativeAdView *nativeAdView;
@property (nonatomic, strong, readonly) BZVMediaView *mediaView;
@property (nonatomic, strong, readonly) UIImageView *iconImageView;
@property (nonatomic, strong, readonly) UILabel *titleLabel;
@property (nonatomic, strong, readonly) UILabel *descriptionLabel;
@property (nonatomic, strong, readonly) BZVDefaultCtaView *ctaView;
@property (nonatomic, strong, readonly) UILabel *priceLabel;
@property (nonatomic, strong, readonly) UILabel *originalPriceLabel;
@property (nonatomic, strong, readonly) UILabel *discountRateLabel;
@property (nonatomic, strong, readonly) BZVNativeAdViewBinder *viewBinder;
@end
@implementation CustomFeedCpsAdView
+ (CGFloat)desiredHeight:(CGFloat)width {
return 350;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
// 광고 레이아웃 컴포넌트를 생성합니다.
// ...생략...
_priceLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_priceLabel];
_originalPriceLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_originalPriceLabel];
_discountRateLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_discountRateLabel];
_viewBinder = [BZVNativeAdViewBinder viewBinderWithBlock:^(BZVNativeAdViewBinderBuilder * _Nonnull builder) {
builder.nativeAdView = self.nativeAdView;
builder.mediaView = self.mediaView;
builder.titleLabel = self.titleLabel;
builder.ctaView = self.ctaView;
builder.clickableViews = @[
self.mediaView,
self.priceLabel,
self.originalPriceLabel,
self.discountRateLabel,
self.ctaView,
];
}];
// LayoutConstraint 설정
// ...생략...
}
- (void)renderAd:(BZVNativeAd *)ad {
[_viewBinder bindWithNativeAd:ad];
BZVNativeAdProduct *product = ad.product;
if (product.discountedPrice != 0) {
// 할인이 있는 쇼핑 광고
_priceLabel.text = [self formattedStringFromNumber:@(product.discountedPrice)];
_originalPriceLabel.text = [self formattedStringFromNumber:@(product.price)];
CGFloat discountRate = (1 - product.discountedPrice / product.price) * 100;
_discountRateLabel.text = [NSString stringWithFormat:@"%.f%%", discountRate];
} else {
// 할인이 없는 쇼핑 광고
_priceLabel.text = [self formattedStringFromNumber:@(product.price)];
_originalPriceLabel.text = [self formattedStringFromNumber:@(product.price)];
_discountRateLabel.hidden = YES;
}
}
- (NSString *)formattedStringFromNumber:(NSNumber *)number {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
return [formatter stringFromNumber:number];
}
@end
- 이전 단계에서 작성한
CustomFeedAdView를BZVFeedConfig에 쇼핑 광고 뷰 홀더로 설정하세요.
let config = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.cpsAdViewClass = CustomFeedCpsAdView.self
}
BZVFeedConfig *config = [BZVFeedConfig configWithBlock:^(BZVFeedConfigBuilder * _Nonnull builder) {
builder.unitId = @"YOUR_FEED_UNIT_ID";
builder.cpsAdViewClass = [CustomFeedCpsAdView class];
}];
광고 미할당 안내 UI
광고 미할당 시 이탈을 막기 위해 유저는 3번까지 재시도하는 UX로 설계되어 있습니다. 광고 미할당 안내 UI에서는 각 재시도 회차별 UI 스트링과 이미지를 변경할 수 있습니다.

| 재시도 회차 | 제목 | 내용 | 버튼 |
|---|---|---|---|
| 0회차 | 미션을 준비 중이에요 | 다시 미션을 불러와서 포인트를 모아보시겠어요? | 미션 불러오기 |
| 1회차 | 미션을 준비 중이에요 | 미션을 로딩 중이에요. 잠시 후 다시 시도해주세요. | 다시 불러오기 |
| 2회차 | 미션을 준비 중이에요 | 미션 준비에 시간이 걸리고 있습니다. 잠시 후 다시 시도해주세요. | 한번 더 불러오기 |
| 3회차(마지막) | 미션을 준비 중이에요 | 전체 필터: 지금은 참여 가능한 미션이 없습니다. 잠시 후 다시 방문해주세요.그 외 필터: 지금은 참여 가능한 미션이 없습니다. 다른 미션에 참여해서 포인트를 모아보세요! | 전체 필터: 나중에 다시 올게요그 외 필터: 다른 미션 참여하기 |
let feedTheme = BZVFeedTheme { builder in
builder.noFillErrorImage = ...
builder.noFillErrorTitle = ...
builder.noFillErrorDescription1st = ...
builder.noFillErrorDescription2nd = ...
builder.noFillErrorDescription3rd = ...
builder.noFillErrorDescriptionFinalAllFilter = ...
builder.noFillErrorDescriptionFinalOtherFilters = ...
builder.noFillErrorButton1st = ...
builder.noFillErrorButton2nd = ...
builder.noFillErrorButton3rd = ...
builder.noFillErrorButtonFinalAllFilter = ...
builder.noFillErrorButtonFinalOtherFilters = ...
}
BZVBuzzAdFeed.setDefaultTheme(feedTheme)
BZVFeedTheme *feedTheme = [BZVFeedTheme themeWithBlock:^(BZVFeedThemeBuilder * _Nonnull builder) {
builder.noFillErrorImage = ...;
builder.noFillErrorTitle = ...;
builder.noFillErrorDescription1st = ...;
builder.noFillErrorDescription2nd = ...;
builder.noFillErrorDescription3rd = ...;
builder.noFillErrorDescriptionFinalAllFilter = ...;
builder.noFillErrorDescriptionFinalOtherFilters = ...;
builder.noFillErrorButton1st = ...;
builder.noFillErrorButton2nd = ...;
builder.noFillErrorButton3rd = ...;
builder.noFillErrorButtonFinalAllFilter = ...;
builder.noFillErrorButtonFinalOtherFilters = ...;
}];
[BZVBuzzAdFeed setDefaultTheme:feedTheme];
유저 프로필 오류 안내 UI
유저 프로필이 설정되지 않았을 때 유저에게 노출되는 화면의 UI 스트링과 이미지를 변경할 수 있습니다.

let feedTheme = BZVFeedTheme { builder in
builder.userProfileErrorImage = ...
builder.userProfileErrorTitle = ...
builder.userProfileErrorDescription = ...
}
BZVBuzzAdFeed.setDefaultTheme(feedTheme)
BZVFeedTheme *feedTheme = [BZVFeedTheme themeWithBlock:^(BZVFeedThemeBuilder * _Nonnull builder) {
builder.userProfileErrorImage = ...;
builder.userProfileErrorTitle = ...;
builder.userProfileErrorDescription = ...;
}];
[BZVBuzzAdFeed setDefaultTheme:feedTheme];
기타 오류 화면 UI
기타 오류 화면별 이미지를 변경할 수 있습니다.

| 속성 | 설명 |
|---|---|
privacyPolicyErrorIcon | 개인정보 제3자 제공 동의를 받아야 하는 경우 노출되는 이미지를 변경합니다. |
agePolicyErrorIcon | 네트워크 에러 발생 시 노출되는 이미지를 변경합니다. |
networkErrorIcon | 네트워크 에러 발생 시 노출되는 이미지를 변경합니다. |
unknownErrorIcon | 알 수 없는 오류 발생 시 노출되는 이미지를 변경합니다. |
let feedTheme = BZVFeedTheme { builder in
builder.privacyPolicyErrorImage = ...
builder.agePolicyErrorImage = ...
builder.networkErrorImage = ...
builder.unknownErrorImage = ...
}
BZVBuzzAdFeed.setDefaultTheme(feedTheme)
BZVFeedTheme *feedTheme = [BZVFeedTheme themeWithBlock:^(BZVFeedThemeBuilder * _Nonnull builder) {
builder.privacyPolicyErrorImage = ...;
builder.agePolicyErrorImage = ...;
builder.networkErrorImage = ...;
builder.unknownErrorImage = ...;
}];
[BZVBuzzAdFeed setDefaultTheme:feedTheme];
개인정보 제3자 제공 동의 UI 자체 구현
개인정보 처리방침 UI를 자체 구현하는 경우, SDK에 동의 여부를 전달해야 합니다. 아래 표를 참고하여 적절한 함수를 호출 해 주세요.
| Class | API | 설명 |
|---|---|---|
BuzzAdBenefit | privacyPolicyManager (정적 변수) | BZVPrivacyPolicyManager 인스턴스를 반환합니다. |
BZVPrivacyPolicyManager | grantConsent() | 개인정보 수집에 동의합니다. |
revokeConsent() | 개인정보 수집 동의를 철회합니다. | |
isConsentGranted() | 개인정보 수집 동의 여부를 확인합니다. |
사용 예시
let privacyPolicyManager = BuzzAdBenefit.privacyPolicyManager
// 직접 구현한 개인정보 제3자 제공 동의 UI에서 동의를 받았을 경우 아래 코드를 실행하면, 피드 화면에서 개인정보 제3자 제공 동의 UI가 노출되지 않으면서 광고 할당이 정상적으로 진행 됩니다.
// 아래의 코드를 실행하지 않을 경우 피드 진입 시 개인정보 제3자 제공 동의 UI가 노출되기 때문에 필수적으로 실행이 필요합니다.
privacyPolicyManager.grantConsent()
// 아래 코드를 통해서 수집 동의를 철회할 수 있습니다. 이 코드가 실행되면 피드에서 광고 할당이 이루어지지 않습니다.
privacyPolicyManager.revokeConsent()
BZVPrivacyPolicyManager *privacyPolicyManager = [BuzzAdBenefit privacyPolicyManager];
// 직접 구현한 개인정보 제3자 제공 동의 UI 에서 동의를 받았을 경우 아래 코드를 실행하면, 피드 화면에서 개인정보 제3자 제공 동의 UI가 노출되지 않으면서 광고 할당이 정상적으로 진행 됩니다.
// 아래의 코드를 실행하지 않을 경우 피드 진입 시 개인정보 제3자 제공 동의 UI가 노출되기 때문에 필수적으로 실행이 필요합니다.
[privacyPolicyManager grantConsent];
// 아래 코드를 통해서 수집 동의를 철회할 수 있습니다. 이 코드가 실행되면 피드에서 광고 할당이 이루어지지 않습니다.
[privacyPolicyManager revokeConsent];
ATT 허용 유도 모달
iOS 14 이상에서 앱 활동 추적 권한을 허용하지 않은 유저에게 권한 허용을 유도하는 모달의 이미지를 변경할 수 있습니다.

let feedTheme = BZVFeedTheme { builder in
builder.appTrackingTransparencyGuideModalImage = ...
}
BZVBuzzAdFeed.setDefaultTheme(feedTheme)
BZVFeedTheme *feedTheme = [BZVFeedTheme themeWithBlock:^(BZVFeedThemeBuilder * _Nonnull builder) {
builder.appTrackingTransparencyGuideModalImage = ...;
}];
[BZVBuzzAdFeed setDefaultTheme:feedTheme];
인터스티셜
주요 UI의 색상(Primary color)을 변경할 수 있는 GlobalTheme을 사용해 진입 경로 레이아웃의 베네핏허브 진입 유도 문구 색상, CTA 버튼 배경 색상과 리워드 아이콘이 함께 변경됩니다. 자세한 내용은 전체 테마 토픽을 참고하세요.