From d9064acfe4f516dcc6c4426279b54be2644b18ca Mon Sep 17 00:00:00 2001 From: caseBread Date: Tue, 20 Jan 2026 18:20:33 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat=20:=20click=5Fcreate=5F4cut=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../album/4cut/components/ScreenAlbum4Cut.tsx | 14 +++++++++++++- src/global/constants/gaEvents.ts | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/feature/album/4cut/components/ScreenAlbum4Cut.tsx b/src/feature/album/4cut/components/ScreenAlbum4Cut.tsx index b30329c2..73605241 100644 --- a/src/feature/album/4cut/components/ScreenAlbum4Cut.tsx +++ b/src/feature/album/4cut/components/ScreenAlbum4Cut.tsx @@ -6,12 +6,14 @@ import LongButton from '@/global/components/LongButton'; import ConfirmModal from '@/global/components/modal/ConfirmModal'; import Toast from '@/global/components/toast/Toast'; import BubbleTooltip from '@/global/components/tooltip/BubbleTooltip'; +import { GA_EVENTS } from '@/global/constants/gaEvents'; import PersonSvg from '@/global/svg/PersonSvg'; import { downloadFile } from '@/global/utils/downloadFile'; import { getDeviceType } from '@/global/utils/getDeviceType'; import { extractHtmlToBlob } from '@/global/utils/image/extractHtmlToBlob'; import { shareImage } from '@/global/utils/image/shareImage'; import { shareViaNavigator } from '@/global/utils/shareNavigator'; +import { trackGaEvent } from '@/global/utils/trackGaEvent'; import { useQueryClient } from '@tanstack/react-query'; import { Download, Loader2, LucideIcon, Menu, Send } from 'lucide-react'; import dynamic from 'next/dynamic'; @@ -57,6 +59,10 @@ export default function ScreenAlbum4Cut({ albumId }: ScreenAlbum4CutProps) { requestAnimationFrame(() => resolve()); }); + const handleClickCreate4Cut = () => { + trackGaEvent(GA_EVENTS.click_create_4cut); + }; + const handleConfirm = async (): Promise => { await mutateAsync({ albumId, @@ -200,7 +206,13 @@ export default function ScreenAlbum4Cut({ albumId }: ScreenAlbum4CutProps) { } + trigger={ + + } title='이대로 확정하시겠어요?' description='예쁜 치즈네컷을 만들어드릴게요' confirmText='확정하기' diff --git a/src/global/constants/gaEvents.ts b/src/global/constants/gaEvents.ts index 3699e4e5..0ef8d9f3 100644 --- a/src/global/constants/gaEvents.ts +++ b/src/global/constants/gaEvents.ts @@ -10,4 +10,5 @@ export const GA_EVENTS = { view_albumsidebar: 'view_albumsidebar', click_invite: 'click_invite', click_invite_complete: 'click_invite_complete', + click_create_4cut: 'click_create_4cut', }; From d5319a4286763698dcbe5a4dd82aea02245a256d Mon Sep 17 00:00:00 2001 From: caseBread Date: Tue, 20 Jan 2026 18:39:12 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat=20:=204cut=20view=20=EC=9D=B4=EB=B2=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../album/4cut/components/ScreenAlbum4Cut.tsx | 67 ++++++++++++++----- src/global/constants/gaEvents.ts | 6 ++ 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/src/feature/album/4cut/components/ScreenAlbum4Cut.tsx b/src/feature/album/4cut/components/ScreenAlbum4Cut.tsx index 73605241..e4a95cda 100644 --- a/src/feature/album/4cut/components/ScreenAlbum4Cut.tsx +++ b/src/feature/album/4cut/components/ScreenAlbum4Cut.tsx @@ -1,5 +1,6 @@ 'use client'; import { useGetUserMe } from '@/feature/main/hooks/useGetUserMe'; +import { useGetAlbumInform } from '@/feature/upload/hooks/useGetAlbumInform'; import { EP } from '@/global/api/ep'; import CustomHeader from '@/global/components/header/CustomHeader'; import LongButton from '@/global/components/LongButton'; @@ -18,8 +19,7 @@ import { useQueryClient } from '@tanstack/react-query'; import { Download, Loader2, LucideIcon, Menu, Send } from 'lucide-react'; import dynamic from 'next/dynamic'; import Link from 'next/link'; -import { useRouter } from 'next/navigation'; -import { useRef, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useGetAlbumInfo } from '../../detail/hooks/useGetAlbumInfo'; import { use4CutFixed } from '../hooks/use4CutFixed'; import { use4CutPreviewQuery } from '../hooks/use4CutPreviewQuery'; @@ -33,24 +33,39 @@ interface ScreenAlbum4CutProps { } export default function ScreenAlbum4Cut({ albumId }: ScreenAlbum4CutProps) { - const router = useRouter(); const queryClient = useQueryClient(); - const [isConfirmed, setIsConfirmed] = useState(false); const [isCaptureVisible, setIsCaptureVisible] = useState(false); const [isDownloading, setIsDownloading] = useState(false); const captureRef = useRef(null); const { data } = useGetAlbumInfo(albumId); + const { data: albumInformData } = useGetAlbumInform({ code: albumId }); const { data: { name } = {} } = useGetUserMe(); // TODO : openapi type이 이상해서 임시 any처리. 백엔드랑 협의 필요 - const { data: { myRole, previewPhotos, isFinalized } = {}, isPending: is4CutPreviewPending, + isSuccess, // eslint-disable-next-line @typescript-eslint/no-explicit-any }: any = use4CutPreviewQuery(albumId); const { mutateAsync } = use4CutFixed(); + useEffect(() => { + if (isSuccess) { + if (isFinalized) { + trackGaEvent(GA_EVENTS.view_4cut_confirmed, { + album_id: albumId, + access_type: myRole === 'MAKER' ? 'creator' : 'member', + }); + } else { + trackGaEvent(GA_EVENTS.view_4cut_unconfirmed, { + album_id: albumId, + access_type: myRole === 'MAKER' ? 'creator' : 'member', + }); + } + } + }, [isSuccess]); + const isMaker = myRole === 'MAKER'; const showCaptureNode = async () => @@ -64,20 +79,31 @@ export default function ScreenAlbum4Cut({ albumId }: ScreenAlbum4CutProps) { }; const handleConfirm = async (): Promise => { - await mutateAsync({ - albumId, - photoIds: previewPhotos.map( - (photo: { photoId: number; imageUrl: string; photoRank: number }) => - photo.photoId, - ), - }); - queryClient.invalidateQueries({ - queryKey: [EP.cheese4cut.preview(albumId)], - }); - setIsConfirmed(true); + trackGaEvent(GA_EVENTS.click_create_4cut_complete, { album_id: albumId }); + try { + await mutateAsync({ + albumId, + photoIds: previewPhotos.map( + (photo: { photoId: number; imageUrl: string; photoRank: number }) => + photo.photoId, + ), + }); + + queryClient.invalidateQueries({ + queryKey: [EP.cheese4cut.preview(albumId)], + }); + } catch (e) { + console.log(e); + Toast.alert(`잠시후에 다시 시도해주세요.`); + } }; const handleDownload = async () => { + trackGaEvent(GA_EVENTS.click_download_4cut, { + album_id: albumId, + access_type: albumInformData?.myRole === 'MAKER' ? 'creator' : 'member', + }); + const deviceType = getDeviceType(); if (!captureRef.current) { @@ -117,6 +143,11 @@ export default function ScreenAlbum4Cut({ albumId }: ScreenAlbum4CutProps) { }; const handleShare = async () => { + trackGaEvent(GA_EVENTS.click_share_4cut, { + album_id: albumId, + access_type: albumInformData?.myRole === 'MAKER' ? 'creator' : 'member', + }); + if (!captureRef.current) { Toast.alert('공유할 이미지를 찾지 못했어요. 잠시 후 다시 시도해주세요.'); return; @@ -230,6 +261,10 @@ export default function ScreenAlbum4Cut({ albumId }: ScreenAlbum4CutProps) { { + trackGaEvent(GA_EVENTS.click_request_4cut, { + album_id: albumId, + }); + if (!data) return; await shareViaNavigator({ diff --git a/src/global/constants/gaEvents.ts b/src/global/constants/gaEvents.ts index 0ef8d9f3..a42b27a0 100644 --- a/src/global/constants/gaEvents.ts +++ b/src/global/constants/gaEvents.ts @@ -11,4 +11,10 @@ export const GA_EVENTS = { click_invite: 'click_invite', click_invite_complete: 'click_invite_complete', click_create_4cut: 'click_create_4cut', + click_create_4cut_complete: 'click_create_4cut_complete', + click_download_4cut: 'click_download_4cut', + click_share_4cut: 'click_share_4cut', + click_request_4cut: 'click_request_4cut', + view_4cut_unconfirmed: 'view_4cut_unconfirmed', + view_4cut_confirmed: 'view_4cut_confirmed', };