391 lines
10 KiB
TypeScript
391 lines
10 KiB
TypeScript
import * as amplitude from '@amplitude/analytics-browser'
|
|
import type { TrackingObject } from '#layers/types/api/common'
|
|
import type { AnalyticsDetailType } from '#layers/types/AnalyticsType'
|
|
import type {
|
|
IdentityInfo,
|
|
ActionInfo,
|
|
MarketingInfo,
|
|
} from '#layers/types/Stove'
|
|
|
|
declare const svcLog: any
|
|
declare const twq: any
|
|
declare const ttq: any
|
|
|
|
// ============================================================================
|
|
// 유틸 함수
|
|
// ============================================================================
|
|
|
|
/** 브라우저 환경인지 체크 */
|
|
const isClient = () => typeof window !== 'undefined' && import.meta.client
|
|
|
|
/** Analytics 객체가 비어있는지 체크 */
|
|
const isEmptyAnalytics = (analytics?: Partial<AnalyticsDetailType>) =>
|
|
!analytics || Object.keys(analytics).length === 0
|
|
|
|
// ============================================================================
|
|
// Analytics 데이터 생성
|
|
// ============================================================================
|
|
|
|
/**
|
|
* 로그 데이터를 생성하는 composable (직접 객체 반환)
|
|
* @param analytics 트래킹 데이터
|
|
* @param type 'page' | 'template'
|
|
* @returns 분석용 로그 데이터 객체 (ref 없이)
|
|
*/
|
|
export const useAnalyticsData = (
|
|
analytics: string | TrackingObject
|
|
): Partial<AnalyticsDetailType> => {
|
|
const pageDataStore = usePageDataStore()
|
|
const { pageName, pageNameEn } = storeToRefs(pageDataStore)
|
|
|
|
if (!analytics || !pageName.value || !pageNameEn.value) {
|
|
return {}
|
|
}
|
|
|
|
const baseEvent = pageName.value
|
|
const baseViewArea = pageNameEn.value
|
|
let logData = {} as AnalyticsDetailType
|
|
|
|
// 페이지뷰
|
|
if (analytics === 'view') {
|
|
logData = {
|
|
actionType: analytics,
|
|
event: baseEvent,
|
|
eventCategory: baseEvent,
|
|
viewArea: baseViewArea,
|
|
viewType: 'pageView',
|
|
} as AnalyticsDetailType
|
|
|
|
return logData
|
|
}
|
|
|
|
// 객체인 경우
|
|
if (typeof analytics === 'object') {
|
|
const { action_type, click_sarea, click_item } = analytics
|
|
|
|
logData = {
|
|
actionType: action_type,
|
|
event: baseEvent,
|
|
eventCategory: `${baseEvent}_${click_sarea}_${click_item}`,
|
|
} as AnalyticsDetailType
|
|
|
|
if (action_type === 'click') {
|
|
logData.clickArea = baseViewArea
|
|
logData.clickSarea = click_sarea
|
|
logData.clickItem = click_item
|
|
} else if (action_type === 'view') {
|
|
logData.viewArea = baseViewArea
|
|
logData.viewType = 'view_frame'
|
|
}
|
|
}
|
|
|
|
return logData
|
|
}
|
|
|
|
// ============================================================================
|
|
// Stove Analytics(81 Plug) + Amplitude
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Stove Analytics(81 Plug) 전송
|
|
*
|
|
* @param analytics Partial<AnalyticsDetailType>
|
|
* @param param1 mcode, options
|
|
*/
|
|
const sendSA = (
|
|
analytics: Partial<AnalyticsDetailType>,
|
|
{ mcode = '', options = {} }: { mcode?: string; options?: any } = {}
|
|
) => {
|
|
if (!isClient()) return
|
|
if (isEmptyAnalytics(analytics)) return
|
|
|
|
const gameDataStore = useGameDataStore()
|
|
const { gameCode } = storeToRefs(gameDataStore)
|
|
|
|
try {
|
|
const device = useDevice()
|
|
|
|
const gameNo = gameCode.value
|
|
const deviceType = device.isDesktop ? 'pcweb' : 'mobileweb'
|
|
const country = `${csrGetCountry()}`
|
|
const memberNo = `${csrGetStoveMemberNo()}`
|
|
|
|
const {
|
|
actionType = '',
|
|
logSourceType = '',
|
|
viewArea = '',
|
|
viewType = '',
|
|
clickArea = '',
|
|
clickSarea = '',
|
|
eventLocale = '',
|
|
clickItem,
|
|
} = analytics
|
|
|
|
const identityInfo: IdentityInfo = {
|
|
app_id: 'stove',
|
|
log_source_type: logSourceType,
|
|
country,
|
|
locale: eventLocale,
|
|
lang_cd: eventLocale,
|
|
member_no: memberNo,
|
|
channeling_cd: 'SO',
|
|
}
|
|
|
|
const marketingInfo: MarketingInfo = {
|
|
marketing_code: mcode || '',
|
|
device_type: deviceType,
|
|
media_type: '',
|
|
media_page: '',
|
|
}
|
|
|
|
let actionParam: ActionInfo['action_param'] = {}
|
|
|
|
if (actionType === 'view') {
|
|
actionParam = {
|
|
view_area: viewArea,
|
|
view_type: viewType,
|
|
view_info: {
|
|
game_no: gameNo,
|
|
lang_cd: eventLocale,
|
|
service_cd: 'official_home',
|
|
...options?.viewInfo,
|
|
},
|
|
}
|
|
} else if (actionType === 'click') {
|
|
actionParam = {
|
|
click_area: clickArea,
|
|
click_sarea: clickSarea,
|
|
click_item: {
|
|
pwt_click_item: clickItem,
|
|
game_no: gameNo,
|
|
lang_cd: eventLocale,
|
|
service_cd: 'official_home',
|
|
...options?.clickItem,
|
|
},
|
|
}
|
|
}
|
|
|
|
const actionInfo: ActionInfo = {
|
|
action_type: actionType,
|
|
action_param: actionParam,
|
|
marketing_info: marketingInfo,
|
|
}
|
|
|
|
const amplitudeActionInfo = {
|
|
...actionInfo,
|
|
url: isClient() ? `${location?.href || ''}` : '',
|
|
agent: isClient() ? `${navigator?.userAgent || ''}` : '',
|
|
}
|
|
|
|
const amplitudeActionParams: {
|
|
event_type: string
|
|
event_properties: ActionInfo & { url: string; agent: string }
|
|
} = {
|
|
event_type: actionType,
|
|
event_properties: amplitudeActionInfo,
|
|
}
|
|
|
|
// 81plug
|
|
svcLog.identity(identityInfo)
|
|
// 81plug warning log 제거를 위해 2번째 인자부터 빈 객체 세팅
|
|
svcLog.action(actionInfo, {}, {})
|
|
|
|
// Amplitude
|
|
amplitude.track(amplitudeActionParams)
|
|
} catch (e) {
|
|
console.error('[Exception] useAnalytics.sendSA: ', e)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Google Analytics
|
|
// ============================================================================
|
|
|
|
/** GA 공통 설정 래퍼 */
|
|
const withGA = (callback: (gtag: any) => void) => {
|
|
if (!isClient()) return
|
|
|
|
try {
|
|
const { gtag } = useGtag()
|
|
const hostname = window?.location?.hostname || ''
|
|
|
|
gtag('set', 'cookie_domain', hostname) // env 값으로 설정 시 쿠키 생성 안 돼서 window.location.hostname으로 설정
|
|
gtag('set', 'cookie_expires', '0') // 0으로 설정 시 쿠키가 Session 기반 쿠키로 전환
|
|
|
|
callback(gtag)
|
|
} catch (e) {
|
|
console.error('[Exception] useAnalytics.withGA: ', e)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Google Analytics 전송
|
|
*
|
|
* @param analytics Partial<AnalyticsDetailType>
|
|
*/
|
|
const sendGA = (analytics: Partial<AnalyticsDetailType>) => {
|
|
if (isEmptyAnalytics(analytics)) return
|
|
|
|
withGA(gtag => {
|
|
const eventName = analytics.event || ''
|
|
const eventLocale = analytics.eventLocale || ''
|
|
const eventCategory = analytics.eventCategory || ''
|
|
// GA 클릭 이벤트 명 뒤에 언어 값 추가 노출되도록 개발. ex) GNB_자유게시판_KO
|
|
const eventLabel = `${eventCategory}_${eventLocale}`
|
|
|
|
gtag('event', `${eventName}`, {
|
|
event_category: eventLabel,
|
|
})
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Google Analytics 전송 (기본 이벤트만 전송)
|
|
*
|
|
* @param gaEventName 이벤트명
|
|
*/
|
|
const sendGAEventOnly = (gaEventName: string) => {
|
|
if (!gaEventName) return
|
|
|
|
withGA(gtag => {
|
|
gtag('event', `${gaEventName}`)
|
|
})
|
|
}
|
|
|
|
// ============================================================================
|
|
// 기본 로그 일괄 전송
|
|
// ============================================================================
|
|
|
|
/**
|
|
* 기본 로그 일괄 전송
|
|
*
|
|
* @param locale 언어 코드
|
|
* @param analytics Partial<AnalyticsDetailType>
|
|
*/
|
|
const sendLog = (
|
|
locale: string,
|
|
analytics: string | TrackingObject,
|
|
options: { useGA?: boolean } = { useGA: false }
|
|
) => {
|
|
const analyticsData = useAnalyticsData(analytics)
|
|
analyticsData.eventLocale = locale.toUpperCase()
|
|
|
|
// SA 전송 : actionType, logSourceType 유무로 판별
|
|
if (analyticsData.actionType && analyticsData.actionType !== '') {
|
|
sendSA(analyticsData, {
|
|
mcode: analyticsData?.mcode,
|
|
options: analyticsData?.options,
|
|
})
|
|
if (options.useGA) {
|
|
sendGA(analyticsData)
|
|
}
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Meta / Twitter / TikTok Pixel
|
|
// ============================================================================
|
|
|
|
/**
|
|
* 메타 픽셀 전송
|
|
*
|
|
* @param fbEventName 이벤트명
|
|
* @description 수집 대상 페이지에 useHead({ meta: [loadMetaPixelMeta()] }) 선언
|
|
*/
|
|
const sendMetaPixel = (fbEventName?: string) => {
|
|
if (!isClient() || !fbEventName) return
|
|
|
|
try {
|
|
const { $fbq } = useNuxtApp()
|
|
if (typeof $fbq === 'function') {
|
|
$fbq('trackCustom', fbEventName)
|
|
}
|
|
} catch (e) {
|
|
console.error('[Exception] useAnalytics.sendMetaPixel: ', e)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* X(트위터) 픽셀 전송
|
|
*
|
|
* @param twEventName 이벤트명
|
|
* @description 수집 대상 페이지에 useHead({ script: [loadTwitterPixelScript()] }) 선언
|
|
*/
|
|
const sendTwitterPixel = (twEventName?: string) => {
|
|
if (!isClient() || !twEventName) return
|
|
|
|
try {
|
|
twq('event', twEventName, {})
|
|
} catch (e) {
|
|
console.error('[Exception] useAnalytics.sendTwitterPixel: ', e)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 틱톡 픽셀 전송
|
|
*
|
|
* @param ttEventName 이벤트명
|
|
* @description 수집 대상 페이지에 onMounted(() => { loadTikTokPixelScript() }) 선언
|
|
*/
|
|
const sendTiktokPixel = (ttEventName?: string) => {
|
|
if (!isClient() || !ttEventName) return
|
|
|
|
try {
|
|
ttq.track(ttEventName)
|
|
} catch (e) {
|
|
console.error('[Exception] useAnalytics.sendTiktokPixel: ', e)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// 마케팅 스크립트 일괄 전송
|
|
// ============================================================================
|
|
|
|
/**
|
|
* 마케팅 인텔리전스 팀 요청 마케팅 스크립트 일괄 전송
|
|
*
|
|
* @param logName 'ga' | 'meta' | 'twitter' | 'tiktok'
|
|
* @param eventName 이벤트명 (ga: eventOnly, meta/twitter/tiktok: 필수)
|
|
* @param analytics GA용 analytics 데이터 (eventName 없을 때만 사용)
|
|
*/
|
|
const sendMarketingLog = ({
|
|
logName,
|
|
eventName,
|
|
}: {
|
|
logName: 'ga' | 'meta' | 'twitter' | 'tiktok'
|
|
eventName?: string
|
|
}) => {
|
|
switch (logName) {
|
|
case 'ga': {
|
|
sendGAEventOnly(eventName)
|
|
break
|
|
}
|
|
case 'meta': {
|
|
sendMetaPixel(eventName)
|
|
break
|
|
}
|
|
case 'twitter': {
|
|
sendTwitterPixel(eventName)
|
|
break
|
|
}
|
|
case 'tiktok': {
|
|
sendTiktokPixel(eventName)
|
|
break
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// export
|
|
// ============================================================================
|
|
|
|
export default () => {
|
|
return {
|
|
sendLog,
|
|
sendMarketingLog,
|
|
useAnalyticsData,
|
|
}
|
|
}
|