diff --git a/frontend/src/features/songs/components/KillingPartTrack.tsx b/frontend/src/features/songs/components/KillingPartTrack.tsx index 7d4a79819..2489e5ca1 100644 --- a/frontend/src/features/songs/components/KillingPartTrack.tsx +++ b/frontend/src/features/songs/components/KillingPartTrack.tsx @@ -9,6 +9,8 @@ import useVideoPlayerContext from '@/features/youtube/hooks/useVideoPlayerContex import useModal from '@/shared/components/Modal/hooks/useModal'; import useTimerContext from '@/shared/components/Timer/hooks/useTimerContext'; import useToastContext from '@/shared/components/Toast/hooks/useToastContext'; +import { GA_ACTIONS, GA_CATEGORIES } from '@/shared/constants/GAEventName'; +import sendGAEvent from '@/shared/googleAnalytics/sendGAEvent'; import { toPlayingTimeText } from '@/shared/utils/convertTime'; import copyClipboard from '@/shared/utils/copyClipBoard'; import formatOrdinals from '@/shared/utils/formatOrdinals'; @@ -49,6 +51,12 @@ const KillingPartTrack = ({ const partLength = end - start; const copyKillingPartUrl = async () => { + sendGAEvent({ + action: GA_ACTIONS.COPY_URL, + category: GA_CATEGORIES.SONG_DETAIL, + memberId: user?.memberId, + }); + await copyClipboard(partVideoUrl); showToast('영상 링크가 복사되었습니다.'); }; @@ -80,6 +88,12 @@ const KillingPartTrack = ({ }; const toggleTrackPlayAndStop = () => { + sendGAEvent({ + action: GA_ACTIONS.PLAY, + category: GA_CATEGORIES.SONG_DETAIL, + memberId: user?.memberId, + }); + if (isNowPlayingTrack) { stopTrack(); } else { @@ -87,6 +101,16 @@ const KillingPartTrack = ({ } }; + const toggleLike = () => { + sendGAEvent({ + action: GA_ACTIONS.LIKE, + category: GA_CATEGORIES.SONG_DETAIL, + memberId: user?.memberId, + }); + + toggleKillingPartLikes(); + }; + return ( diff --git a/frontend/src/pages/MyPage.tsx b/frontend/src/pages/MyPage.tsx index ad8c44744..fb146e76e 100644 --- a/frontend/src/pages/MyPage.tsx +++ b/frontend/src/pages/MyPage.tsx @@ -9,7 +9,9 @@ import Flex from '@/shared/components/Flex'; import Spacing from '@/shared/components/Spacing'; import SRHeading from '@/shared/components/SRHeading'; import useToastContext from '@/shared/components/Toast/hooks/useToastContext'; +import { GA_ACTIONS, GA_CATEGORIES } from '@/shared/constants/GAEventName'; import ROUTE_PATH from '@/shared/constants/path'; +import sendGAEvent from '@/shared/googleAnalytics/sendGAEvent'; import useFetch from '@/shared/hooks/useFetch'; import fetcher from '@/shared/remotes'; import { secondsToMinSec, toPlayingTimeText } from '@/shared/utils/convertTime'; @@ -38,11 +40,22 @@ const MyPage = () => { const navigate = useNavigate(); const logoutRedirect = () => { + sendGAEvent({ + action: GA_ACTIONS.LOGOUT, + category: GA_CATEGORIES.MY_PAGE, + memberId: user?.memberId, + }); logout(); navigate(ROUTE_PATH.ROOT); }; const goEditPage = () => { + sendGAEvent({ + action: GA_ACTIONS.EDIT_PROFILE, + category: GA_CATEGORIES.MY_PAGE, + memberId: user?.memberId, + }); + navigate(`/${ROUTE_PATH.EDIT_PROFILE}`); }; @@ -181,18 +194,17 @@ type LikePartItemProps = LikeKillingPart & { rank: number; }; -const LikePartItem = ({ - songId, - albumCoverUrl, - title, - singer, - // partId, - start, - end, -}: LikePartItemProps) => { +const LikePartItem = ({ songId, albumCoverUrl, title, singer, start, end }: LikePartItemProps) => { const { showToast } = useToastContext(); + const { user } = useAuthContext(); const shareUrl = () => { + sendGAEvent({ + action: GA_ACTIONS.COPY_URL, + category: GA_CATEGORIES.MY_PAGE, + memberId: user?.memberId, + }); + copyClipboard(`${BASE_URL?.replace('/api', '')}/songs/${songId}`); showToast('클립보드에 영상링크가 복사되었습니다.'); }; diff --git a/frontend/src/shared/constants/GAEventName.ts b/frontend/src/shared/constants/GAEventName.ts new file mode 100644 index 000000000..9d6019454 --- /dev/null +++ b/frontend/src/shared/constants/GAEventName.ts @@ -0,0 +1,16 @@ +export const GA_ACTIONS = { + COPY_URL: 'click_copy_part', + PLAY: 'click_play_part', + LIKE: 'click_like', + EDIT_PROFILE: 'click_edit_profile', + LOGOUT: 'click_logout', +}; + +export const GA_CATEGORIES = { + SONG_DETAIL: 'song_playing', + MY_PAGE: 'profile', +}; + +export const GA_MEMBER = { + NOT_LOGGED_IN: -1, +}; diff --git a/frontend/src/shared/googleAnalytics/sendGAEvent.ts b/frontend/src/shared/googleAnalytics/sendGAEvent.ts new file mode 100644 index 000000000..db6a74412 --- /dev/null +++ b/frontend/src/shared/googleAnalytics/sendGAEvent.ts @@ -0,0 +1,18 @@ +import { GA_MEMBER } from '@/shared/constants/GAEventName'; + +interface GAProps { + action: string; + category: string; + memberId?: number; +} + +export const sendGAEvent = ({ action, category, memberId = GA_MEMBER.NOT_LOGGED_IN }: GAProps) => { + if ('gtag' in window) { + window?.gtag('event', action, { + event_category: category, + member_id: memberId ? memberId : GA_MEMBER.NOT_LOGGED_IN, + }); + } +}; + +export default sendGAEvent; diff --git a/frontend/src/shared/types/ga.d.ts b/frontend/src/shared/types/ga.d.ts new file mode 100644 index 000000000..216fa2914 --- /dev/null +++ b/frontend/src/shared/types/ga.d.ts @@ -0,0 +1,3 @@ +interface Window { + gtag: typeof gtag; +}