Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions src/analytics/google-analytics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Google Analytics 설정 및 추적 함수들

// Google Analytics 설정 ID
const GA_MEASUREMENT_ID = import.meta.env.VITE_GA_MEASUREMENT_ID || 'G-XXXXXXXXXX'

// Google Analytics 초기화
export const initGA = () => {
// 이미 로드되었는지 확인
if (window.gtag) {
console.log('Google Analytics already initialized')
return
}

// gtag 스크립트 로드
const script = document.createElement('script')
script.async = true
script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`
document.head.appendChild(script)

// gtag 초기화
window.dataLayer = window.dataLayer || []
window.gtag = function() {
window.dataLayer.push(arguments)
}

window.gtag('js', new Date())
window.gtag('config', GA_MEASUREMENT_ID, {
// 개인정보 보호 설정
anonymize_ip: true,
send_page_view: false, // 수동으로 페이지뷰 추적
// 쿠키 만료 시간 (90일)
cookie_expires: 90 * 24 * 60 * 60
})

console.log('Google Analytics initialized:', GA_MEASUREMENT_ID)
}

// 페이지뷰 추적
export const trackPageView = (page_title, page_location = window.location.href) => {
if (typeof window.gtag !== 'undefined') {
window.gtag('event', 'page_view', {
page_title,
page_location
})
console.log('GA Page View:', page_title, page_location)
}
}

// 커스텀 이벤트 추적
export const trackEvent = (action, category = 'engagement', label = '', value = 0) => {
if (typeof window.gtag !== 'undefined') {
window.gtag('event', action, {
event_category: category,
event_label: label,
value: value
})
console.log('GA Event:', action, category, label, value)
}
}

// 로그인 이벤트
export const trackLogin = (method = 'email') => {
trackEvent('login', 'auth', method)
}

// 회원가입 이벤트
export const trackSignup = (method = 'email') => {
trackEvent('sign_up', 'auth', method)
}

// 목표 생성 이벤트
export const trackGoalCreated = (goalType, amount = 0) => {
trackEvent('goal_created', 'goal', goalType, amount)
}

// ISA 계좌 등록 이벤트
export const trackIsaRegistration = () => {
trackEvent('isa_registration', 'account', 'isa_account')
}

// 예적금 등록 이벤트
export const trackDepositRegistration = () => {
trackEvent('deposit_registration', 'account', 'deposit_account')
}

// 투자 추천 클릭 이벤트
export const trackInvestmentRecommendation = (recommendationType) => {
trackEvent('investment_recommendation_click', 'recommendation', recommendationType)
}

// 리포트 조회 이벤트
export const trackReportView = (reportType) => {
trackEvent('report_view', 'report', reportType)
}

// 에러 추적
export const trackError = (errorMessage, errorLocation = '') => {
trackEvent('error', 'error', `${errorLocation}: ${errorMessage}`)
}

// 사용자 행동 추적
export const trackUserAction = (action, section, details = '') => {
trackEvent(action, 'user_interaction', `${section}_${details}`)
}
80 changes: 80 additions & 0 deletions src/analytics/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Google Analytics 4 간단 구현

const GA_ID = 'G-KP0NDR0HLP'

// 초기화
export const initGA = () => {
const script = document.createElement('script')
script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_ID}`
script.async = true
document.head.appendChild(script)

window.dataLayer = window.dataLayer || []
window.gtag = function() { window.dataLayer.push(arguments) }

gtag('js', new Date())
gtag('config', GA_ID)
}

// 페이지 조회
export const trackPage = (page) => {
if (window.gtag) {
gtag('event', 'page_view', { page_title: page })
}
}

// 이벤트 추적
export const trackEvent = (action, category = 'general') => {
if (window.gtag) {
gtag('event', action, { event_category: category })
}
}

// 금융 앱 특화 이벤트들
export const trackGoalCreated = (goalType, amount) => {
if (window.gtag) {
gtag('event', 'goal_created', {
event_category: 'goal',
goal_type: goalType,
goal_amount: amount
})
}
}

export const trackAccountLinked = (accountType, bank) => {
if (window.gtag) {
gtag('event', 'account_linked', {
event_category: 'account',
account_type: accountType,
bank_name: bank
})
}
}

export const trackInvestmentRecommendation = (productType, amount) => {
if (window.gtag) {
gtag('event', 'investment_recommendation_view', {
event_category: 'investment',
product_type: productType,
recommended_amount: amount
})
}
}

export const trackReportView = (reportType) => {
if (window.gtag) {
gtag('event', 'report_viewed', {
event_category: 'report',
report_type: reportType
})
}
}

export const trackTaxSavingsView = (savingsAmount) => {
if (window.gtag) {
gtag('event', 'tax_savings_viewed', {
event_category: 'report',
savings_amount: savingsAmount
})
}
}
5 changes: 5 additions & 0 deletions src/components/report/TaxComparisonChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { CanvasRenderer } from "echarts/renderers";
import { BarChart, LineChart } from "echarts/charts";
import { GridComponent, TooltipComponent, LegendComponent } from "echarts/components";
import api from "@/api";
import { trackTaxSavingsView } from "@/analytics";

use([CanvasRenderer, BarChart, LineChart, GridComponent, TooltipComponent, LegendComponent]);

Expand Down Expand Up @@ -174,6 +175,10 @@ const fetchData = async () => {
quarterlyData: [],
summary: {},
};

// GA 세금 절약 데이터 조회 추적
const totalSavings = taxData.value.quarterlyData?.reduce((sum, item) => sum + (item.isaTaxSaved || 0), 0) || 0;
trackTaxSavingsView(totalSavings);
} catch (err) {
console.error("세금 비교 데이터 가져오기 실패:", err);
error.value = true;
Expand Down
4 changes: 4 additions & 0 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import { createPinia } from "pinia";
import App from "./App.vue";
import router from "./router";
import "./index.css";
import { initGA } from "./analytics";

const app = createApp(App);

app.use(createPinia());
app.use(router);

// Google Analytics 초기화
initGA();

app.mount("#app");
6 changes: 6 additions & 0 deletions src/router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import alarmRoutes from "./alarm";
import reportRoutes from "./report";
import { STORAGE_KEYS } from "@/utils/constants";
import { useAuthStore } from "@/stores/auth";
import { trackPage } from "@/analytics";

const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
Expand Down Expand Up @@ -92,4 +93,9 @@ router.beforeEach((to, _from, next) => {
next();
});

// 페이지 뷰 추적
router.afterEach((to) => {
trackPage(to.name || to.path);
});

export default router;
5 changes: 5 additions & 0 deletions src/views/auth/SignupView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import Button from "@/components/base/Button.vue";
import goBackButton from "@/components/base/GoBackButton.vue";
import BaseTextInput from "@/components/base/BaseTextInput.vue";
import authApi from "@/api/authApi";
import { trackEvent } from "@/analytics";


// import { useUserStore } from "@/stores/user";
Expand Down Expand Up @@ -73,6 +74,10 @@ const handleRegister = async () => {
password: formData.password,
nickname: formData.username,
});

// GA 회원가입 이벤트 추적
trackEvent('sign_up', 'auth');

router.push("/agree-condition");
} catch (error) {
const msg = error?.response?.data?.message || "회원 가입 실패";
Expand Down
4 changes: 4 additions & 0 deletions src/views/goal/GoalEditView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ import DefaultLayout from "@/components/layouts/DefaultLayout.vue";
import AddRegisterModal from "./AddRegisterModal.vue";
import Api from "@/api/objectApi";
import isaApi from "@/api/isaApi";
import { trackGoalCreated } from "@/analytics";

const route = useRoute();
const router = useRouter();
Expand Down Expand Up @@ -723,6 +724,9 @@ const handleCompleteGoal = async () => {
const ok = res?.status === 200 || res?.status === 201 || res?.data?.status === "OK";

if (ok) {
// GA 목표 생성/수정 이벤트 추적
trackGoalCreated(goalName.value, goalAmount.value);

localStorage.removeItem("currentGoalId");
router.push({ path: "/goal" });
} else {
Expand Down
5 changes: 5 additions & 0 deletions src/views/onBoarding/LoginPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import Button from "@/components/base/Button.vue";
import { useAuthStore } from "@/stores/auth";
import { ref, reactive } from "vue";
import { useRouter } from "vue-router";
import { trackEvent } from "@/analytics";
const auth = useAuthStore();
const userData = reactive({
email: "",
Expand All @@ -63,6 +64,10 @@ const login = async () => {
try {
isLoading.value = true;
await auth.login(userData);

// GA 로그인 이벤트 추적
trackEvent('login', 'auth');

//semi_user , user 따라 라우팅
const authData = JSON.parse(localStorage.getItem("auth"));
const userRole = authData?.user?.role;
Expand Down
5 changes: 5 additions & 0 deletions src/views/report/ReportView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ import IsaReport from "@/components/report/IsaReport.vue";
import { ref, onMounted } from "vue";
import api from "@/api";
import TaxSavingsSummary from "@/components/report/TaxSavingsSummary.vue";
import { trackReportView } from "@/analytics";

const activeTab = ref("isa");
const goals = ref([]);
Expand All @@ -113,9 +114,13 @@ const fetchGoals = async () => {

const setActiveTab = (tab) => {
activeTab.value = tab;
// GA 리포트 탭 변경 추적
trackReportView(tab === 'isa' ? 'ISA 리포트' : '투자 리포트');
};

onMounted(() => {
fetchGoals();
// GA 리포트 페이지 접속 추적
trackReportView('리포트 메인');
});
</script>