diff --git a/package.json b/package.json
index 9b80ba9..0b7b829 100644
--- a/package.json
+++ b/package.json
@@ -34,6 +34,7 @@
"@commitlint/cli": "^18.2.0",
"@commitlint/config-conventional": "^18.1.0",
"@tanstack/eslint-plugin-query": "^5.6.0",
+ "@types/gtag.js": "^0.0.18",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
diff --git a/src/app/GoogleAnalytics.tsx b/src/app/GoogleAnalytics.tsx
new file mode 100644
index 0000000..efc94eb
--- /dev/null
+++ b/src/app/GoogleAnalytics.tsx
@@ -0,0 +1,37 @@
+'use client';
+
+import Script from 'next/script';
+
+import * as gtag from '@/utils/gtag';
+
+export default function GoogleAnalytics() {
+ gtag.useGtag();
+
+ return (
+ <>
+ {process.env.NODE_ENV !== 'development' && (
+ <>
+ {/* Global Site Tag (gtag.js) - Google Analytics */}
+
+
+ >
+ )}
+ >
+ );
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index beecd8d..c9dee21 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -3,13 +3,11 @@ import './globals.css';
import { Analytics } from '@vercel/analytics/react';
import type { Metadata } from 'next';
import { Noto_Sans_KR } from 'next/font/google';
-import { useRouter } from 'next/router';
-import { useEffect } from 'react';
import Footer from '@/components/layout/Footer';
import Header from '@/components/layout/Header';
-import * as ga from '@/types/ga/gtag';
+import GoogleAnalytics from './GoogleAnalytics';
import ReactQueryProvider from './ReactQueryProvider';
const inter = Noto_Sans_KR({
@@ -30,41 +28,9 @@ export default function RootLayout({
}: {
children: React.ReactNode;
}) {
- const router = useRouter();
-
- useEffect(() => {
- const handleRouteChange = (url: URL) => {
- ga.pageView(url);
- };
-
- router.events.on('routeChangeComplete', handleRouteChange);
-
- return () => {
- router.events.off('routeChangeComplete', handleRouteChange);
- };
- }, [router.events]);
-
return (
-
-
-
-
+
diff --git a/src/types/ga/gtag.ts b/src/types/ga/gtag.ts
deleted file mode 100644
index 50eca0b..0000000
--- a/src/types/ga/gtag.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-declare global {
- interface Window {
- gtag: (param1: string, param2: string, param3: object) => void;
- }
-}
-
-export const pageView = (url: URL) => {
- window.gtag('config', process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_TAG!, {
- page_path: url,
- });
-};
diff --git a/src/types/type.d.ts b/src/types/type.d.ts
new file mode 100644
index 0000000..f0b1264
--- /dev/null
+++ b/src/types/type.d.ts
@@ -0,0 +1,3 @@
+///
+
+declare module 'gtag.js';
diff --git a/src/utils/gtag.ts b/src/utils/gtag.ts
new file mode 100644
index 0000000..d0f0a6c
--- /dev/null
+++ b/src/utils/gtag.ts
@@ -0,0 +1,44 @@
+import { usePathname } from 'next/navigation';
+import { useEffect, useRef } from 'react';
+
+export const GA_TRACKING_ID = process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_TAG;
+
+// https://developers.google.com/analytics/devguides/collection/gtagjs/pages
+export const pageview = (url: URL) => {
+ window.gtag('config', GA_TRACKING_ID as string, {
+ page_path: url,
+ });
+};
+
+// https://developers.google.com/analytics/devguides/collection/gtagjs/events
+export const event = (
+ action: Gtag.EventNames,
+ { event_category, event_label, value }: Gtag.EventParams,
+) => {
+ window.gtag('event', action, {
+ event_category,
+ event_label,
+ value,
+ });
+};
+
+export const useGtag = () => {
+ const pathname = usePathname(); // Get current route
+
+ // Save pathname on component mount into a REF
+ const savedPathNameRef = useRef(pathname);
+
+ useEffect(() => {
+ if (process.env.NODE_ENV === 'development') return;
+
+ const handleRouteChange = (url: URL) => {
+ pageview(url);
+ };
+
+ if (savedPathNameRef.current !== pathname) {
+ handleRouteChange(new URL(pathname, window.location.origin));
+ // Update REF
+ savedPathNameRef.current = pathname;
+ }
+ }, [pathname]);
+};
diff --git a/yarn.lock b/yarn.lock
index 079e94d..19921a9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -751,6 +751,11 @@
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.2.tgz#ff02bc3dc8317cd668dfec247b750ba1f1d62453"
integrity sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==
+"@types/gtag.js@^0.0.18":
+ version "0.0.18"
+ resolved "https://registry.yarnpkg.com/@types/gtag.js/-/gtag.js-0.0.18.tgz#d6bc7cb1acc64ff4f4e4be918d401c53fe9ccf20"
+ integrity sha512-GJxnIvuXuVhKaHfsOdzGipoOoXq72y3mdcncc9h6i6E7nlz89zBEj2wrLM7bqO5Xk9Lm2B94MwdQsSwRlaPSWw==
+
"@types/json-schema@^7.0.12":
version "7.0.14"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.14.tgz#74a97a5573980802f32c8e47b663530ab3b6b7d1"