Braze SDK와 Google Tag 매니저
Braze SDK와 Google Tag 매니저(GTM)를 사용하여 코드 변경이나 새 앱 릴리스 없이 Braze 이벤트 추적 및 사용자 속성 업데이트를 원격으로 제어하는 방법을 알아보세요.
웹용 Google Tag 매니저 정보
Google Tag 매니저(GTM)를 사용하면 프로덕션 코드 릴리스나 엔지니어링 리소스 없이도 웹사이트에 원격으로 태그를 추가, 제거, 편집할 수 있습니다. Braze는 웹 SDK를 위해 다음과 같은 템플릿을 제공합니다:
| 태그 유형 | 사용 사례 |
|---|---|
| 초기화 태그 | 이 태그를 사용하면 사이트의 코드를 수정할 필요 없이 웹 Braze SDK를 통합할 수 있습니다. |
| 동작 태그 | 이 태그를 사용하면 Content Cards를 생성하고, 사용자 속성을 설정하고, 이커머스 이벤트를 기록하고, 데이터 수집을 관리할 수 있습니다. |
Braze 동작 태그의 태그 시퀀싱
Braze 초기화 태그는 Braze SDK 메서드를 호출하는 모든 태그(예: braze.getUser(), braze.logCustomEvent(), braze.logPurchase())보다 먼저 실행되어야 합니다. SDK가 초기화되기 전에 이러한 메서드가 실행되면 Uncaught TypeError: Cannot read properties of undefined (reading 'getUser')와 같은 오류가 발생할 수 있습니다.
Google Tag 매니저에서 태그 시퀀싱을 구성하려면 다음을 수행합니다:
- Braze SDK 메서드를 호출하는 태그(예: 커스텀 HTML 태그 또는 Braze 동작 태그)를 엽니다.
- Advanced Settings > Tag Sequencing으로 이동합니다.
- A tag that fires before [this tag] is fired를 선택합니다.
- Braze Initialization 태그를 선택합니다.
이렇게 하면 다른 태그가 Braze 메서드를 호출하기 전에 SDK가 완전히 로드됩니다.
자세한 내용은 커스텀 이벤트의 태그 시퀀싱 확인을 참조하세요.
GTM으로 구매 기록하기
Braze 동작 태그 및 커스텀 HTML 태그에서 braze.logPurchase()를 호출하여 매출을 기록합니다. 레거시 appboy.logPurchase() 네임스페이스는 현재 웹 SDK 통합에서 지원되지 않습니다.
GTM으로 이커머스 이벤트 기록하기
Braze 동작 태그의 Log eCommerce Event (New) 태그 유형을 사용하여 GTM 데이터 레이어에서 Braze로 이커머스 추천 이벤트를 전송합니다. 이 태그는 braze.logEcommerceEvent()를 호출하며 웹 SDK 6.8.0 이상이 필요합니다. Braze 초기화 태그가 호환되는 SDK 버전을 로드하는지 확인하세요.
이 태그는 다음 이벤트를 지원합니다:
| 태그 이벤트 유형 | Braze 이벤트 |
|---|---|
| Product Viewed | ecommerce.product_viewed |
| Cart Updated | ecommerce.cart_updated |
| Checkout started | ecommerce.checkout_started |
| Order placed | ecommerce.order_placed |
ecommerce.order_cancelled 또는 ecommerce.order_refunded를 기록하려면 braze.logCustomEvent()를 호출하는 커스텀 HTML 태그를 사용하세요. 예시는 이커머스 이벤트 기록을 참조하세요.

Log Purchase (Legacy) 및 E-Commerce Purchase (Legacy) 태그 유형은 계속 사용할 수 있습니다. 새 통합에는 Log eCommerce Event (New)를 사용하세요.
1단계: 데이터 레이어에 이벤트 푸시
사이트의 코드에서 이벤트와 ecommerce 객체를 데이터 레이어에 푸시합니다. 기본적으로 이 태그는 표준 GA4 이커머스 데이터 레이어 형식을 읽습니다. 예를 들어, 주문을 기록하려면 다음과 같이 합니다:
dataLayer.push({
event: "purchase",
ecommerce: {
transaction_id: "T_12345",
value: 29.97,
currency: "USD",
items: [
{
item_id: "SKU_12345",
item_name: "Stan and Friends Tee",
item_variant: "green",
price: 9.99,
quantity: 3
}
]
}
});
Braze는 item_id 및 transaction_id와 같은 ID를 문자열로 요구합니다. ID가 숫자인 경우에도 문자열로 푸시하세요.
2단계: GTM에서 트리거 만들기
- GTM 컨테이너에서 Triggers로 이동하여 새 트리거를 만듭니다.
- Trigger Type을 Custom Event로 설정합니다.
- Event Name을 데이터 레이어에 푸시한 이벤트와 동일하게 설정합니다(예:
purchase).
3단계: Braze 동작 태그 만들기
- GTM에서 Tags로 이동하여 Braze Actions Tag 유형으로 새 태그를 만듭니다.
- Tag Type을 Log eCommerce Event (New)로 설정합니다.
- eCommerce Event Type을 선택합니다.
- 다음 표에 설명된 대로 해당 이벤트 유형의 필드를 입력합니다.
- (선택 사항) 메타데이터를 추가합니다.
- Triggering에서 2단계에서 만든 트리거를 선택합니다.
- Braze 초기화 태그가 먼저 실행되도록 태그 시퀀싱을 구성합니다.
- 컨테이너를 저장하고 게시합니다.
| 이커머스 이벤트 유형 | 태그 필드 | 필수 데이터 레이어 값 |
|---|---|---|
| Product Viewed | 없음. | ecommerce.items 및 ecommerce.currency. 첫 번째 항목이 조회된 제품입니다. |
| Cart Updated | Cart ID는 필수입니다. Cart Update Action은 Replace, Add 또는 Remove이며, 기본값은 Replace입니다. | ecommerce.items 및 ecommerce.currency. 동작이 Replace인 경우 ecommerce.value가 필수입니다. |
| Checkout started | Checkout ID는 필수입니다. Cart ID는 선택 사항입니다. | ecommerce.items, ecommerce.currency 및 ecommerce.value. |
| Order placed | Cart ID는 선택 사항입니다. | ecommerce.items, ecommerce.currency, ecommerce.value 및 ecommerce.transaction_id. |
ecommerce.currency가 설정되지 않은 경우, 태그는 첫 번째 항목의 currency 필드를 사용합니다. 태그는 이벤트 source를 google_tag_manager_web으로 설정합니다.
데이터 레이어 매핑
Automatically parse e-commerce event from the data layer가 선택된 경우(기본값), 태그는 데이터 레이어 값을 다음과 같이 Braze 이벤트 속성정보에 매핑합니다:
| Braze 속성정보 | 데이터 레이어 값 |
|---|---|
currency |
ecommerce.currency |
total_value |
ecommerce.value |
order_id |
ecommerce.transaction_id |
products[].product_id |
ecommerce.items[].item_id |
products[].product_name |
ecommerce.items[].item_name |
products[].variant_id |
ecommerce.items[].item_variant. 기본값은 item_id입니다. |
products[].price |
ecommerce.items[].price |
products[].quantity |
ecommerce.items[].quantity. 기본값은 1입니다. |
products[].image_url |
ecommerce.items[].image_url |
products[].product_url |
ecommerce.items[].product_url |
이벤트 값 수동 입력
사이트에서 커스텀 이커머스 데이터 레이어 형식을 사용하는 경우, Automatically parse e-commerce event from the data layer를 선택 해제하세요. 그런 다음 태그에 이벤트 값을 입력합니다. 이 필드에는 GTM 변수를 사용할 수 있습니다.
| 필드 | 설명 |
|---|---|
| Currency | 필수. USD와 같은 ISO 4217 통화 코드입니다. |
| Total Value | Replace 동작이 있는 Cart Updated, Checkout started 및 Order placed에 필수입니다. |
| Order ID | Order placed에 필수입니다. |
| Products | 필수. 각 제품에 대해 행을 추가합니다. Product ID, Product Name 및 Price는 필수입니다. Variant ID는 기본적으로 제품 ID이며, Quantity는 기본값이 1입니다. Image URL, Product URL 및 Product Metadata (JSON)는 선택 사항입니다. Product Viewed의 경우 태그는 첫 번째 행을 사용합니다. |
이커머스 이벤트 메타데이터
이벤트와 함께 추가 키-값 페어를 전송하려면 메타데이터를 추가합니다. Braze는 이벤트 메타데이터를 이벤트의 metadata 객체에, 제품 메타데이터를 각 제품의 metadata 객체에 전송합니다.
이벤트 메타데이터
eCommerce Event Metadata 테이블을 사용하여 이벤트 수준 메타데이터를 추가합니다. 이 테이블은 자동 파싱 사용 여부와 관계없이 사용할 수 있습니다. 각 행에 Metadata property key와 Metadata property value를 입력합니다. 값으로 GTM 변수를 사용할 수 있습니다. 태그는 키 또는 값이 비어 있는 행을 건너뜁니다.
일부 이벤트 유형은 인식된 메타데이터 키를 지원합니다. 예를 들어, Checkout started에는 checkout_url, Order placed에는 order_status_url을 사용합니다. 전체 목록은 이벤트 스키마를 참조하세요.
제품 메타데이터
Products 테이블의 Product Metadata (JSON) 열을 사용하여 개별 제품에 메타데이터를 추가합니다. {"color": "green", "size": "M"}과 같은 JSON 객체를 입력합니다. 제품 메타데이터에는 다음과 같은 제한 사항이 있습니다:
- 제품 메타데이터는 Automatically parse e-commerce event from the data layer를 선택 해제한 경우에만 사용할 수 있습니다.
- 태그는 Cart Updated, Checkout started 및 Order placed 이벤트에 대해 제품 메타데이터를 전송합니다. Product Viewed에는 제품 배열이 없으므로 제품 메타데이터를 전송하지 않습니다. Product Viewed에는 eCommerce Event Metadata 테이블을 대신 사용하세요.
- JSON이 유효한 객체가 아닌 경우 태그는 이를 무시합니다.
이커머스 이벤트 확인
Braze 웹 SDK는 모든 이커머스 이벤트를 검증합니다. 값이 유효하지 않으면 SDK는 이벤트를 기록하지 않고 브라우저 콘솔에 오류를 기록합니다. GTM에서는 태그가 실행된 것으로 표시됩니다. 누락된 이벤트를 문제 해결하려면 다음을 수행합니다:
- 태그에서 Enable GTM Tag Debugging을 선택합니다. 태그가 데이터 레이어 값을 읽을 수 없거나, 제품을 찾을 수 없거나, 유효하지 않은 제품 메타데이터를 무시하는 경우 브라우저 콘솔에 메시지를 기록합니다.
- 브라우저의 개발자 콘솔을 열고 Braze 웹 SDK의 오류를 확인합니다.
- 해당 이벤트 유형의 필수 필드를 확인합니다. ID가 문자열인지,
price와total_value가 유효한 숫자인지,quantity가 정수인지 확인합니다.
이벤트 스키마 및 수집 검증에 대해서는 이커머스 이벤트 기록 및 이벤트 검증 및 문제 해결을 참조하세요.
GTM으로 커스텀 이벤트 기록하기
GTM의 커스텀 HTML 태그를 사용하여 커스텀 이벤트를 기록할 수 있습니다. 이 방법은 GTM 데이터 레이어를 사용하여 사이트에서 Braze 웹 SDK를 호출하는 GTM 태그로 이벤트 데이터를 전달합니다.
1단계: 데이터 레이어에 이벤트 푸시
사이트의 코드에서 커스텀 이벤트를 트리거하려는 곳에 이벤트를 데이터 레이어에 푸시합니다. 예를 들어, 버튼을 클릭할 때 커스텀 이벤트를 기록하려면 다음과 같이 합니다:
<button onclick="dataLayer.push({'event': 'my_custom_event'});">Track Event</button>
2단계: GTM에서 트리거 만들기
- GTM 컨테이너에서 Triggers로 이동하여 새 트리거를 만듭니다.
- Trigger Type을 Custom Event로 설정합니다.
- Event Name을 데이터 레이어에 푸시한 값과 동일하게 설정합니다(예:
my_custom_event). - 트리거가 실행되는 시점을 선택합니다(예: All Custom Events).
3단계: 커스텀 HTML 태그 만들기
- GTM에서 Tags로 이동하여 새 태그를 만듭니다.
- Tag Type을 Custom HTML로 설정합니다.
-
HTML 필드에 다음을 추가합니다:
<script> window.braze.logCustomEvent("my_custom_event"); </script>
- Triggering에서 2단계에서 만든 트리거를 선택합니다.
- 컨테이너를 저장하고 게시합니다.
이벤트 속성정보를 포함하려면 두 번째 인수로 전달합니다:
<script>
window.braze.logCustomEvent("my_custom_event", {"property_key": "property_value"});
</script>
엄격한 CSP(nonce 기반) 환경
Braze 초기화 태그는 현재 braze.initialize()에 contentSecurityNonce 전달을 지원하지 않습니다. 사이트에서 엄격한 nonce 기반 CSP를 적용하는 경우, Braze 초기화 태그 대신 커스텀 HTML 태그를 사용하세요.
- 이미 설정한 경우 GTM 컨테이너에서 Braze 초기화 태그를 일시 중지하거나 제거합니다.
-
사이트 HTML에서 GTM 스니펫 앞에 서버 생성 nonce를
dataLayer에 푸시합니다:<script nonce="SERVER_GENERATED_NONCE"> dataLayer = window.dataLayer || []; dataLayer.push({ cspNonce: 'SERVER_GENERATED_NONCE' }); </script>
- GTM에서
cspNonce에 매핑된 Data Layer Variable을 만듭니다(예:DLV - cspNonce). -
트리거가 Initialization - All Pages로 설정된 커스텀 HTML 태그를 만듭니다:
환경에 맞게 다음 템플릿을 커스터마이즈합니다.
<script nonce="{{DLV - cspNonce}}"> var s = document.createElement('script'); s.src = 'https://js.appboycdn.com/web-sdk/YOUR_SDK_VERSION/braze.min.js'; s.setAttribute('nonce', '{{DLV - cspNonce}}'); s.onload = function() { braze.initialize('YOUR_API_KEY', { baseUrl: 'YOUR_SDK_ENDPOINT', contentSecurityNonce: '{{DLV - cspNonce}}' }); braze.openSession(); }; document.head.appendChild(s); </script>
- Braze 동작 태그 또는 Braze 메서드를 호출하는 커스텀 HTML 태그의 경우, 이 커스텀 HTML 태그 이후에 실행되도록 태그 시퀀싱을 구성합니다.
Google의 EU 사용자 동의 정책

Google은 2024년 3월 6일부터 시행되는 디지털 시장법(DMA)의 변경 사항에 대응하여 EU 사용자 동의 정책을 업데이트하고 있습니다. 이 새로운 변경 사항은 광고주가 EEA 및 영국 최종사용자에게 특정 정보를 공개하고 필요한 동의를 받을 것을 요구합니다. 자세한 내용은 다음 설명서를 참조하세요.
Google의 EU 사용자 동의 정책의 일환으로, 다음 부울 커스텀 속성을 고객 프로필에 기록해야 합니다:
$google_ad_user_data$google_ad_personalization
GTM 통합을 통해 이를 설정하는 경우, 커스텀 속성을 위해 커스텀 HTML 태그를 생성해야 합니다. 다음은 이러한 값을 부울 데이터 유형(문자열이 아님)으로 기록하는 방법의 예시입니다:
<script>
window.braze.getUser().setCustomUserAttribute("$google_ad_personalization", true);
</script>
자세한 내용은 Google 오디언스 싱크를 참조하세요.
필수 조건
이 기능을 사용하려면 먼저 Android Braze SDK를 통합해야 합니다.
Android용 Google 태그 관리자 사용
다음 예시에서는 음악 스트리밍 앱에서 사용자가 노래를 들을 때 다양한 이벤트를 기록하려고 합니다. Android용 Google Tag 매니저를 사용하여 어떤 서드파티 업체가 이 이벤트를 수신할지 제어하고, Braze 전용 태그를 생성할 수 있습니다.
1단계: 커스텀 이벤트 트리거 만들기
커스텀 이벤트는 actionType이 logEvent로 설정되어 기록됩니다. 이 예제의 Braze 커스텀 태그 제공업체는 eventName 을 사용하여 커스텀 이벤트 이름을 설정할 것으로 예상합니다.
시작하려면 played song과 동일한 ‘이벤트 이름’을 찾는 트리거를 만듭니다.

그런 다음 새 태그(“함수 호출”이라고도 함)를 만들고 이 글의 뒷부분에 설명된 커스텀 태그 제공업체의 클래스 경로를 입력합니다. 이 태그는 played song 이벤트를 기록할 때 트리거됩니다.
태그의 커스텀 파라미터(키-값 페어라고도 함)에서 eventName 을 played song 으로 설정합니다. Braze에 기록된 커스텀 이벤트 이름이 됩니다.


커스텀 이벤트를 보낼 때 actionType 을 logEvent 으로 설정하고 eventName 에 값을 설정하여 Braze가 올바른 이벤트 이름과 조치를 수신할 수 있도록 하세요.
태그에 추가적인 키-값 페어 인수를 포함할 수 있습니다. 그러면 Braze에 커스텀 이벤트 속성정보로 전송됩니다. eventName 및 actionType은 커스텀 이벤트 속성정보에 대해 무시되지 않습니다. 다음 예제 태그에서 genre 은 앱에 기록된 커스텀 이벤트에서 소싱된 Google Tag 매니저의 태그 변수를 사용하여 전달되고 정의됩니다.
Android용 Google 태그 관리자는 데이터 계층으로 Firebase를 사용하므로 genre 이벤트 속성정보는 “Firebase - 이벤트 파라미터” 변수로 Google 태그 관리자에게 전송됩니다.

사용자가 앱에서 노래를 재생하면 태그의 트리거 이름( played song)과 일치하는 Firebase 분석 이벤트 이름을 사용하여 Firebase 및 Google Tag 매니저를 통해 이벤트가 기록됩니다:
Bundle params = new Bundle();
params.putString("genre", "pop");
params.putInt("number of times listened", 42);
mFirebaseAnalytics.logEvent("played song", params);
val params = Bundle()
params.putString("genre", "pop")
params.putInt("number of times listened", 42);
mFirebaseAnalytics.logEvent("played song", params)
2단계: 커스텀 속성 기록하기
actionType을 customAttribute로 설정하여 커스텀 속성은 설정됩니다. Braze 커스텀 태그 공급자는 customAttributeKey 및 customAttributeValue를 통해 커스텀 속성 키-값을 설정한다고 예상합니다.
Bundle params = new Bundle();
params.putString("customAttributeKey", "favorite song");
params.putString("customAttributeValue", "Private Eyes");
mFirebaseAnalytics.logEvent("customAttribute", params);
val params = Bundle()
params.putString("customAttributeKey", "favorite song")
params.putString("customAttributeValue", "Private Eyes")
mFirebaseAnalytics.logEvent("customAttribute", params)
3단계: 통화 changeUser()
actionType을 changeUser로 설정하고 changeUser()에 대한 호출이 수행됩니다. Braze 커스텀 태그 공급자는 태그 내 externalUserId 키-값 페어를 통해 Braze 사용자 ID를 설정한다고 예상합니다.
Bundle params = new Bundle();
params.putString("externalUserId", userId);
mFirebaseAnalytics.logEvent("changeUser", params);
val params = Bundle()
params.putString("externalUserId", userId)
mFirebaseAnalytics.logEvent("changeUser", params)
4단계: 커스텀 태그 공급자 추가하기
태그 및 트리거를 설정한 상태에서 Google Tag 매니저를 Android 앱에서도 구현해야 합니다(Google 설명서 참조).
앱에 Google 태그 매니저를 설치한 후 커스텀 태그 공급자를 추가하여 Google 태그 매니저 내에서 구성한 태그를 기반으로 Braze 소프트웨어 개발 키트 메서드를 호출하세요.
“클래스 경로”를 파일에 기록해 두십시오. 이는 Google Tag 매니저 콘솔에서 태그를 설정할 때 입력할 내용입니다.
이 예는 커스텀 태그 공급자를 구성할 수 있는 여러 방법 중 하나를 강조합니다. 특히 GTM 태그에서 전송된 actionType 키-값 페어에 따라 호출할 Braze SDK 메서드를 결정하는 방법을 보여줍니다.
이 예제에 표시된 actionType 은 logEvent, customAttribute, changeUser 이지만 태그 제공업체가 Google Tag 매니저에서 데이터를 처리하는 방식을 변경할 수 있습니다.
public class BrazeGtmTagProvider implements CustomTagProvider {
private static final String TAG = BrazeLogger.getBrazeLogTag(BrazeGtmTagProvider.class);
private static final String ACTION_TYPE_KEY = "actionType";
// Custom Events
private static final String LOG_EVENT_ACTION_TYPE = "logEvent";
private static final String EVENT_NAME_VARIABLE = "eventName";
// Custom Attributes
private static final String CUSTOM_ATTRIBUTE_ACTION_TYPE = "customAttribute";
private static final String CUSTOM_ATTRIBUTE_KEY = "customAttributeKey";
private static final String CUSTOM_ATTRIBUTE_VALUE_KEY = "customAttributeValue";
// Change User
private static final String CHANGE_USER_ACTION_TYPE = "changeUser";
private static final String CHANGE_USER_ID_VARIABLE = "externalUserId";
private static Context sApplicationContext;
/**
* Must be set before calling any of the following methods
* so that the proper application context is available when needed.
*
* Recommended to be called in your {@link Application#onCreate()}.
*/
public static void setApplicationContext(Context applicationContext) {
if (applicationContext != null) {
sApplicationContext = applicationContext.getApplicationContext();
}
}
@Override
public void execute(Map<String, Object> map) {
BrazeLogger.i(TAG, "Got google tag manager parameters map: " + map);
if (sApplicationContext == null) {
BrazeLogger.w(TAG, "No application context provided to this tag provider.");
return;
}
if (!map.containsKey(ACTION_TYPE_KEY)) {
BrazeLogger.w(TAG, "Map does not contain the Braze action type key: " + ACTION_TYPE_KEY);
return;
}
String actionType = String.valueOf(map.remove(ACTION_TYPE_KEY));
switch (actionType) {
case LOG_EVENT_ACTION_TYPE:
logEvent(map);
break;
case CUSTOM_ATTRIBUTE_ACTION_TYPE:
setCustomAttribute(map);
break;
case CHANGE_USER_ACTION_TYPE:
changeUser(map);
break;
default:
BrazeLogger.w(TAG, "Got unknown action type: " + actionType);
break;
}
}
private void logEvent(Map<String, Object> tagParameterMap) {
String eventName = String.valueOf(tagParameterMap.remove(EVENT_NAME_VARIABLE));
Braze.getInstance(sApplicationContext).logCustomEvent(eventName, parseMapIntoProperties(tagParameterMap));
}
private BrazeProperties parseMapIntoProperties(Map<String, Object> map) {
BrazeProperties brazeProperties = new BrazeProperties();
for (Map.Entry<String, Object> entry : map.entrySet()) {
final Object value = entry.getValue();
final String key = entry.getKey();
if (value instanceof Boolean) {
brazeProperties.addProperty(key, (Boolean) value);
} else if (value instanceof Integer) {
brazeProperties.addProperty(key, (Integer) value);
} else if (value instanceof Date) {
brazeProperties.addProperty(key, (Date) value);
} else if (value instanceof Long) {
brazeProperties.addProperty(key, (Long) value);
} else if (value instanceof String) {
brazeProperties.addProperty(key, (String) value);
} else if (value instanceof Double) {
brazeProperties.addProperty(key, (Double) value);
} else {
BrazeLogger.w(TAG, "Failed to parse value into an BrazeProperties "
+ "accepted type. Key: '" + key + "' Value: '" + value + "'");
}
}
return brazeProperties;
}
private void setCustomAttribute(Map<String, Object> tagParameterMap) {
String key = String.valueOf(tagParameterMap.get(CUSTOM_ATTRIBUTE_KEY));
Object value = tagParameterMap.get(CUSTOM_ATTRIBUTE_VALUE_KEY);
Braze.getInstance(sApplicationContext).getCurrentUser(new IValueCallback<BrazeUser>() {
@Override
public void onSuccess(BrazeUser brazeUser) {
if (value instanceof Boolean) {
brazeUser.setCustomUserAttribute(key, (Boolean) value);
} else if (value instanceof Integer) {
brazeUser.setCustomUserAttribute(key, (Integer) value);
} else if (value instanceof Long) {
brazeUser.setCustomUserAttribute(key, (Long) value);
} else if (value instanceof String) {
brazeUser.setCustomUserAttribute(key, (String) value);
} else if (value instanceof Double) {
brazeUser.setCustomUserAttribute(key, (Double) value);
} else if (value instanceof Float) {
brazeUser.setCustomUserAttribute(key, (Float) value);
} else {
BrazeLogger.w(TAG, "Failed to parse value into a custom "
+ "attribute accepted type. Key: '" + key + "' Value: '" + value + "'");
}
}
});
}
private void changeUser(Map<String, Object> tagParameterMap) {
String userId = String.valueOf(tagParameterMap.get(CHANGE_USER_ID_VARIABLE));
Braze.getInstance(sApplicationContext).changeUser(userId);
}
}
class BrazeGtmTagProvider : CustomTagProvider {
override fun execute(map: MutableMap<String, Any>) {
BrazeLogger.i(TAG, "Got google tag manager parameters map: $map")
if (sApplicationContext == null) {
BrazeLogger.w(TAG, "No application context provided to this tag provider.")
return
}
if (!map.containsKey(ACTION_TYPE_KEY)) {
BrazeLogger.w(TAG, "Map does not contain the Braze action type key: $ACTION_TYPE_KEY")
return
}
val actionType = map.remove(ACTION_TYPE_KEY).toString()
when (actionType) {
LOG_EVENT_ACTION_TYPE -> logEvent(map)
CUSTOM_ATTRIBUTE_ACTION_TYPE -> setCustomAttribute(map)
CHANGE_USER_ACTION_TYPE -> changeUser(map)
else -> BrazeLogger.w(TAG, "Got unknown action type: $actionType")
}
}
private fun logEvent(tagParameterMap: MutableMap<String, Any>) {
val eventName = tagParameterMap.remove(EVENT_NAME_VARIABLE).toString()
Braze.getInstance(sApplicationContext).logCustomEvent(eventName, parseMapIntoProperties(tagParameterMap))
}
private fun parseMapIntoProperties(map: Map<String, Any>): BrazeProperties {
val brazeProperties = BrazeProperties()
map.forEach { param ->
val key = param.key
val value = param.value
when (value) {
is Boolean -> brazeProperties.addProperty(key, value)
is Int -> brazeProperties.addProperty(key, value)
is Date -> brazeProperties.addProperty(key, value)
is Long -> brazeProperties.addProperty(key, value)
is String -> brazeProperties.addProperty(key, value)
is Double -> brazeProperties.addProperty(key, value)
else -> BrazeLogger.w(TAG, "Failed to parse value into an BrazeProperties "
+ "accepted type. Key: '" + key + "' Value: '" + value + "'")
}
}
return brazeProperties
}
private fun setCustomAttribute(tagParameterMap: Map<String, Any>) {
val key = tagParameterMap[CUSTOM_ATTRIBUTE_KEY].toString()
val value = tagParameterMap[CUSTOM_ATTRIBUTE_VALUE_KEY]
Braze.getInstance(sApplicationContext).getCurrentUser { brazeUser ->
when (value) {
is Boolean -> brazeUser.setCustomUserAttribute(key, value)
is Int -> brazeUser.setCustomUserAttribute(key, value)
is Long -> brazeUser.setCustomUserAttribute(key, value)
is String -> brazeUser.setCustomUserAttribute(key, value)
is Double -> brazeUser.setCustomUserAttribute(key, value)
is Float -> brazeUser.setCustomUserAttribute(key, value)
else -> BrazeLogger.w(
TAG, "Failed to parse value into a custom "
+ "attribute accepted type. Key: '" + key + "' Value: '" + value + "'"
)
}
}
}
private fun changeUser(tagParameterMap: Map<String, Any>) {
val userId = tagParameterMap[CHANGE_USER_ID_VARIABLE].toString()
Braze.getInstance(sApplicationContext).changeUser(userId)
}
companion object {
private val TAG = BrazeLogger.getBrazeLogTag(BrazeGtmTagProvider::class.java)
private val ACTION_TYPE_KEY = "actionType"
// Custom Events
private val LOG_EVENT_ACTION_TYPE = "logEvent"
private val EVENT_NAME_VARIABLE = "eventName"
// Custom Attributes
private val CUSTOM_ATTRIBUTE_ACTION_TYPE = "customAttribute"
private val CUSTOM_ATTRIBUTE_KEY = "customAttributeKey"
private val CUSTOM_ATTRIBUTE_VALUE_KEY = "customAttributeValue"
// Change User
private val CHANGE_USER_ACTION_TYPE = "changeUser"
private val CHANGE_USER_ID_VARIABLE = "externalUserId"
private var sApplicationContext: Context? = null
/**
* Must be set before calling any of the following methods so
* that the proper application context is available when needed.
*
* Recommended to be called in your [Application.onCreate].
*/
fun setApplicationContext(applicationContext: Context?) {
if (applicationContext != null) {
sApplicationContext = applicationContext.applicationContext
}
}
}
}
Application.onCreate()에서 이전 스니펫에 대해 다음 초기화를 추가합니다.
BrazeGtmTagProvider.setApplicationContext(this.getApplicationContext());
BrazeGtmTagProvider.setApplicationContext(this.applicationContext)
필수 조건
이 기능을 사용하려면 먼저 Swift Braze SDK를 통합해야 합니다.
Swift용 Google Tag 매니저 사용하기
다음 예시에서는 음악 스트리밍 앱에서 사용자가 노래를 들을 때 다양한 이벤트를 기록하려고 합니다. iOS용 Google Tag 매니저를 사용하여 이 이벤트를 수신할 Braze 서드파티 업체를 제어하고 Braze 전용 태그를 생성할 수 있습니다.
1단계: 커스텀 이벤트 트리거 만들기
커스텀 이벤트는 actionType이 logEvent로 설정되어 기록됩니다. 이 예제에서 Braze 커스텀 태그 제공업체는 eventName 을 사용하여 커스텀 이벤트 이름을 설정할 것으로 예상됩니다.
먼저 played song 과 동일한 eventName 을 찾는 트리거를 만듭니다.

그런 다음 새 태그(“함수 호출”이라고도 함)를 만들고 이 글의 뒷부분에 설명된 커스텀 태그 제공업체의 클래스 경로를 입력합니다. 이 태그는 played song 이벤트를 기록할 때 트리거됩니다. eventName 이 played song 으로 설정되어 있으므로 Braze에 기록되는 커스텀 이벤트 이름으로 사용됩니다.

커스텀 이벤트를 보낼 때 actionType 을 logEvent 으로 설정하고 eventName 에 값을 설정하여 Braze가 올바른 이벤트 이름과 수행할 작업을 수신하도록 합니다.

태그에 추가적인 키-값 페어 인수를 포함할 수 있습니다. 그러면 Braze에 커스텀 이벤트 속성정보로 전송됩니다. eventName 및 actionType은 커스텀 이벤트 속성정보에 대해 무시되지 않습니다. 다음 예제 태그에서는 Google Tag 매니저에서 태그 변수를 사용하여 정의하고 앱에 기록된 커스텀 이벤트에서 소싱한 genre 을 전달합니다.
genre 이벤트 속성정보는 iOS용 Google Tag 매니저가 데이터 레이어로 Firebase를 사용하기 때문에 ‘Firebase - 이벤트 매개변수’ 변수로 Google Tag 매니저에 전송됩니다.

사용자가 앱에서 노래를 재생하면 태그의 트리거 이름과 일치하는 Firebase 분석 이벤트 이름( played song)을 사용하여 Firebase 및 Google Tag 매니저를 통해 이벤트를 로그합니다:
let parameters: [String: Any] = ["genre": "pop",
"number of times listened": 42]
Analytics.logEvent("played song", parameters: parameters)
NSDictionary *parameters = @{@"genre" : @"pop",
@"number of times listened" : @42};
[FIRAnalytics logEventWithName:@"played song" parameters:parameters];
2단계: 커스텀 속성 기록하기
actionType을 customAttribute로 설정하여 커스텀 속성은 설정됩니다. Braze 커스텀 태그 공급자는 customAttributeKey 및 customAttributeValue를 통해 커스텀 속성 키-값을 설정한다고 예상합니다.
let parameters: [String: Any] = ["customAttributeKey": "favoriteSong",
"customAttributeValue": "Private Eyes"]
FIRAnalytics.logEvent(withName:"customAttribute", parameters: parameters)
NSDictionary *parameters = @{@"customAttributeKey" : @"favoriteSong",
@"customAttributeValue" : @"Private Eyes"};
[FIRAnalytics logEventWithName:@"customAttribute" parameters:parameters];
3단계: 통화 changeUser()
actionType을 changeUser로 설정하고 changeUser()에 대한 호출이 수행됩니다. Braze 커스텀 태그 공급자는 태그 내 externalUserId 키-값 페어를 통해 Braze 사용자 ID를 설정한다고 예상합니다.
let parameters: [String: Any] = ["externalUserId": "favorite userId"]
Analytics.logEvent(withName:"changeUser", parameters: parameters)
NSDictionary *parameters = @{@"externalUserId" : userId};
[FIRAnalytics logEventWithName:@"changeUser" parameters:parameters];
4단계: 커스텀 태그 공급자 추가하기
태그 및 트리거를 설정한 상태에서 Google Tag 매니저를 iOS 앱에서도 구현해야 합니다(Google 설명서 참조).
앱에 Google 태그 매니저를 설치한 후 커스텀 태그 공급자를 추가하여 Google 태그 매니저 내에서 구성한 태그를 기반으로 Braze SDK 메서드를 호출하세요.
파일의 ‘클래스 경로’는 Google 태그 관리자 콘솔에서 태그를 설정할 때 입력하는 정보입니다.
이 예는 커스텀 태그 공급자를 구성할 수 있는 여러 방법 중 하나를 강조합니다. 특히 GTM 태그에서 전송된 actionType 키-값 페어에 따라 호출할 Braze SDK 메서드를 결정하는 방법을 보여줍니다. 이 예제에서는 AppDelegate에서 Braze 인스턴스를 변수로 할당했다고 가정합니다.
이 예제에서 지원되는 actionType 은 logEvent, customAttribute, changeUser 이지만 태그 제공업체가 Google Tag 매니저에서 데이터를 처리하는 방식을 변경할 수 있습니다.
BrazeGTMTagManager.swift 파일에 다음 코드를 추가합니다.
import FirebaseAnalytics
import GoogleTagManager
import BrazeKit
let ActionTypeKey: String = "actionType"
// Custom Events
let LogEventAction: String = "logEvent"
let LogEventName: String = "eventName"
// Custom Attributes
let CustomAttributeAction: String = "customAttribute"
let CustomAttributeKey: String = "customAttributeKey"
let CustomAttributeValueKey: String = "customAttributeValue"
// Change User
let ChangeUserAction: String = "changeUser"
let ChangeUserExternalUserId: String = "externalUserId"
@objc(BrazeGTMTagManager)
final class BrazeGTMTagManager : NSObject, TAGCustomFunction {
@objc func execute(withParameters parameters: [AnyHashable : Any]!) -> NSObject! {
var parameters: [String : Any] = parameters as! [String : Any]
guard let actionType: String = parameters[ActionTypeKey] as? String else {
print("There is no Braze action type key in this call. Doing nothing.")
return nil
}
parameters.removeValue(forKey: ActionTypeKey)
if actionType == LogEventAction {
logEvent(parameters: parameters)
} else if actionType == CustomAttributeAction {
logCustomAttribute(parameters: parameters)
} else if actionType == ChangeUserAction {
changeUser(parameters: parameters)
}
return nil
}
func logEvent(parameters: [String : Any]) {
var parameters: [String : Any] = parameters
guard let eventName: String = parameters[LogEventName] as? String else { return }
parameters.removeValue(forKey: LogEventName)
AppDelegate.braze?.logCustomEvent(name: eventName, properties: parameters)
}
func logCustomAttribute(parameters: [String: Any]) {
guard let customAttributeKey = parameters[CustomAttributeKey] as? String else { return }
let customAttributeValue = parameters[CustomAttributeValueKey]
if let customAttributeValue = customAttributeValue as? String {
AppDelegate.braze?.user.setCustomAttribute(key: customAttributeKey, value: customAttributeValue)
} else if let customAttributeValue = customAttributeValue as? Date {
AppDelegate.braze?.user.setCustomAttribute(key: customAttributeKey, value: customAttributeValue)
} else if let customAttributeValue = customAttributeValue as? Double {
AppDelegate.braze?.user.setCustomAttribute(key: customAttributeKey, value: customAttributeValue)
} else if let customAttributeValue = customAttributeValue as? Bool {
AppDelegate.braze?.user.setCustomAttribute(key: customAttributeKey, value: customAttributeValue)
} else if let customAttributeValue = customAttributeValue as? Int {
AppDelegate.braze?.user.setCustomAttribute(key: customAttributeKey, value: customAttributeValue)
} else if let customAttibuteValue = customAttributeValue as? [String] {
AppDelegate.braze?.user.setCustomAttributeArray(key: customAttributeKey, array: customAttibuteValue)
}
}
func changeUser(parameters: [String: Any]) {
guard let userId = parameters[ChangeUserExternalUserId] as? String else { return }
AppDelegate.braze?.changeUser(userId: userId)
}
}
BrazeGTMTagManager.h 파일에 다음 코드를 추가합니다:
@import Firebase;
@import GoogleTagManager;
@interface BrazeGTMTagManager : NSObject <TAGCustomFunction>
@end
그리고 BrazeGTMTagManager.m 파일에 다음 코드를 추가합니다:
#import <Foundation/Foundation.h>
#import "BrazeGTMTagManager.h"
#import "BrazeKit"
#import "AppDelegate.h"
static NSString *const ActionTypeKey = @"actionType";
// Custom Events
static NSString *const LogEventAction = @"logEvent";
static NSString *const LogEventEventName = @"eventName";
// Custom Attributes
static NSString *const CustomAttributeAction = @"customAttribute";
static NSString *const CustomAttributeKey = @"customAttributeKey";
static NSString *const CustomAttributeValueKey = @"customAttributeValue";
// Change User
static NSString *const ChangeUserAction = @"changeUser";
static NSString *const ChangeUserExternalUserId = @"externalUserId";
@implementation BrazeGTMTagManager
- (NSObject *)executeWithParameters:(NSDictionary *)parameters {
NSMutableDictionary *mutableParameters = [parameters mutableCopy];
NSString *actionType = mutableParameters[ActionTypeKey];
if (!actionType) {
NSLog(@"There is no Braze action type key in this call. Doing nothing.", nil);
return nil;
}
[mutableParameters removeObjectForKey:ActionTypeKey];
if ([actionType isEqualToString:LogEventAction]) {
[self logEvent:mutableParameters];
} else if ([actionType isEqualToString:CustomAttributeAction]) {
[self logCustomAttribute:mutableParameters];
} else if ([actionType isEqualToString:ChangeUserAction]) {
[self changeUser:mutableParameters];
} else {
NSLog(@"Invalid action type. Doing nothing.");
}
return nil;
}
- (void)logEvent:(NSMutableDictionary *)parameters {
NSString *eventName = parameters[LogEventEventName];
[parameters removeObjectForKey:LogEventEventName];
[AppDelegate.braze logCustomEvent:eventName
properties:parameters];
}
- (void)logCustomAttribute:(NSMutableDictionary *)parameters {
NSString *customAttributeKey = parameters[CustomAttributeKey];
id customAttributeValue = parameters[CustomAttributeValueKey];
if ([customAttributeValue isKindOfClass:[NSString class]]) {
[AppDelegate.braze logCustomEvent:customAttributeKey
properties:parameters];
} else if ([customAttributeValue isKindOfClass:[NSDate class]]) {
[AppDelegate.braze.user setCustomAttributeWithKey:customAttributeKey
dateValue:customAttributeValue];
} else if ([customAttributeValue isKindOfClass:[NSNumber class]]) {
if (strcmp([customAttributeValue objCType], [@(YES) objCType]) == 0) {
[AppDelegate.braze.user setCustomAttributeWithKey:customAttributeKey
boolValue:[(NSNumber *)customAttributeValue boolValue]];
} else if (strcmp([customAttributeValue objCType], @encode(short)) == 0 ||
strcmp([customAttributeValue objCType], @encode(int)) == 0 ||
strcmp([customAttributeValue objCType], @encode(long)) == 0) {
[AppDelegate.braze.user setCustomAttributeWithKey:customAttributeKey
intValue:[(NSNumber *)customAttributeValue integerValue]];
} else if (strcmp([customAttributeValue objCType], @encode(float)) == 0 ||
strcmp([customAttributeValue objCType], @encode(double)) == 0) {
[AppDelegate.braze.user setCustomAttributeWithKey:customAttributeKey
doubleValue:[(NSNumber *)customAttributeValue doubleValue]];
} else {
NSLog(@"Could not map NSNumber value to Braze custom attribute:%@", customAttributeValue);
}
} else if ([customAttributeValue isKindOfClass:[NSArray class]]) {
[AppDelegate.braze.user setCustomAttributeArrayWithKey:customAttributeKey
array:customAttributeValue];
}
}
- (void)changeUser:(NSMutableDictionary *)parameters {
NSString *userId = parameters[ChangeUserExternalUserId];
[AppDelegate.braze changeUser:userId];
}
@end
문제 해결
웹 SDK 세션이 잘못된 사용자에게 귀속되는 경우
GTM이 앱에서 로그인한 사용자를 식별하기 전에 Braze 초기화 또는 이벤트 태그를 실행하면, 세션과 이벤트가 잘못된 프로필에 연결될 수 있습니다. 웹 SDK를 초기화하고, 로그인한 사용자의 external_id로 changeUser()를 호출한 다음, 이벤트를 기록하거나 속성을 설정하는 태그가 실행되기 전에 openSession()을 호출하세요. GTM 태그 시퀀싱 또는 동의 트리거를 사용하여 인증 흐름이 완료된 후에만 Braze 태그가 실행되도록 합니다.
Shopify 또는 스크립트 태그 설치 시 웹 SDK 콘솔 로깅
Shopify 앱 임베드는 콘솔 로깅이 비활성화된 상태로 웹 SDK를 로드합니다. GTM 초기화 태그 또는 initialize() 옵션에서 로깅을 설정하세요. Braze 대시보드에는 이러한 로더에 대한 로깅 제어가 포함되어 있지 않습니다.
브라우저 콘솔에 Braze 로그가 표시되는 경우, 프로덕션에 게시하기 전에 GTM 초기화 태그 또는 커스텀 HTML에서 enableLogging: true를 제거하세요. 초기화 후에는 toggleLogging() 또는 ?brazeLogging=true URL 파라미터를 사용하세요. 전체 웹 SDK 옵션은 상세 로깅을 참조하세요.
Braze가 초기화되지 않거나 이벤트가 예상대로 표시되지 않는 경우, GTM 컨테이너가 게시되었는지, 트리거와 태그 실행 순서가 SDK 라이프사이클 및 초기화 전략과 일치하는지, 테스트 기기가 Braze 엔드포인트를 차단하고 있지 않은지 확인하세요.
초기화 실패의 경우, Braze 태그 또는 커스텀 태그 제공업체가 예상되는 actionType 및 파라미터를 수신하는지 확인하세요(이 페이지의 Android, Swift, 웹 탭 참조). GTM에서 실행된 이벤트를 검증하면서 상세 로깅을 활성화하려면 해당 탭에서 링크된 플랫폼 통합 가이드에 설명된 대로 플랫폼의 SDK 디버그 로깅을 활성화하세요.