From 7accba68481b93477d84ea1e86d1f37f0ce7e85a Mon Sep 17 00:00:00 2001 From: mayrang Date: Fri, 4 Jul 2025 12:07:22 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20=EA=B5=AC=EA=B8=80=20=EC=95=A0=EB=84=90?= =?UTF-8?q?=EB=A6=AC=ED=8B=B1=EC=8A=A4=20=EB=8F=84=EC=9E=85=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/providers.tsx | 19 ++++--- src/context/AnalyticsProvider.tsx | 86 +++++++++++++++++++++++++++++++ src/hooks/useClickTracking.tsx | 15 ++++++ src/hooks/usePageTracking.tsx | 14 +++++ src/page/Home/Home.tsx | 5 ++ src/utils/gtag.ts | 15 ++++++ 6 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 src/context/AnalyticsProvider.tsx create mode 100644 src/hooks/useClickTracking.tsx create mode 100644 src/hooks/usePageTracking.tsx create mode 100644 src/utils/gtag.ts diff --git a/src/app/providers.tsx b/src/app/providers.tsx index c47a52a1..6be0fa11 100644 --- a/src/app/providers.tsx +++ b/src/app/providers.tsx @@ -6,19 +6,22 @@ import RootStyleRegistry from "./RootStyleRegistry"; import { GlobalErrorBoundary } from "@/components/errorHandling/GlobalErrorBoundary"; import { ViewTransitions } from "next-view-transitions"; import PageNavigationProvider from "@/context/PageNavigationProvider"; +import { AnalyticsProvider } from "@/context/AnalyticsProvider"; export default function Providers({ children }: { children: ReactNode }) { return ( - - - - + + + + + - {children} - - - + {children} + + + + ); } diff --git a/src/context/AnalyticsProvider.tsx b/src/context/AnalyticsProvider.tsx new file mode 100644 index 00000000..dfb9f83b --- /dev/null +++ b/src/context/AnalyticsProvider.tsx @@ -0,0 +1,86 @@ +"use client"; + +import { createContext, useContext, useEffect, ReactNode } from "react"; +import { usePathname, useSearchParams } from "next/navigation"; +import Script from "next/script"; +import { GA_TRACKING_ID, pageview, event } from "@/utils/gtag"; + +interface AnalyticsContextType { + trackPageView: (pageName?: string) => void; + trackClick: (elementName: string, additionalData?: Record) => void; +} + +const AnalyticsContext = createContext(undefined); + +interface AnalyticsProviderProps { + children: ReactNode; +} + +export function AnalyticsProvider({ children }: AnalyticsProviderProps) { + const pathname = usePathname(); + const searchParams = useSearchParams(); + + // 자동 페이지 뷰 추적 + useEffect(() => { + if (GA_TRACKING_ID) { + const url = pathname + (searchParams.toString() ? `?${searchParams.toString()}` : ""); + pageview(url); + } + }, [pathname, searchParams]); + + const trackPageView = (pageName?: string) => { + if (GA_TRACKING_ID) { + const name = pageName || pathname.replace("/", "") || "home"; + event("page_view", { + page_title: name, + page_location: window.location.href, + page_path: pathname, + }); + } + }; + + const trackClick = (elementName: string, additionalData?: Record) => { + if (GA_TRACKING_ID) { + event("click", { + event_category: "engagement", + event_label: elementName, + page_path: pathname, + ...additionalData, + }); + } + }; + + return ( + + {/* Google Analytics Scripts */} + {GA_TRACKING_ID && ( + <> +