Skip to content

Commit

Permalink
feat: Amplitude 사용자 트래킹 코드 고도화 (#979)
Browse files Browse the repository at this point in the history
* refactor: @amplitude/analytics-browser에서 필요한 모듈만 사용하는 방식으로 변경

* style: 함수명 오타 수정

* feat: auth store에 카카오 유저 여부도 추가

* fix: 회원 행사 생성 완료 시 trackCompleteCreateEvent이벤트로 변경 (상위에서 이미 카카오 유저 판단하고 있음)

* feat: Amplitude 트래킹 코드 추가 정의

* feat: 랜딩 페이지 최하단을 확인하는지를 체크하는 track 코드 적용

* feat: 전체 지출 내역 탭을 클릭했을 때 트래킹 코드 실행

* feat: 전체 사용자 이름 변경 수와 입금상태 변경 수를 트래킹하는 기능 구현

* feat: 행사 이벤트 이름을 변경할 때 트래킹 코드 삽입

* feat: 이미지 추가할 때 이미지 추가횟수 이벤트 발생

* feat: 유저 이름 변경 시 트래킹 코드 이벤트 발생

* feat: 은행 이름을 트래킹 하는 코드 추가

* feat: 회원 탈퇴를 트래킹하는 코드 추가

* feat: 행사 삭제 관련 트래킹 코드 추가

* feat: qr code 공유 방식 트래킹 코드 추가

* refactor: track + 동사 + 목적어 형태로 메서드 명 수정

* refactor: 범용적으로 사용할 수 있도록 컴포넌트 이름 변경
  • Loading branch information
jinhokim98 authored Feb 5, 2025
1 parent 4628f73 commit 52c9929
Show file tree
Hide file tree
Showing 22 changed files with 267 additions and 85 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import {useEffect} from 'react';

import {useAmplitudeStore} from '@store/amplitudeStore';
import {init} from '@amplitude/analytics-browser';

const AmplitudeInitializer = ({children}: React.PropsWithChildren) => {
const {amplitude} = useAmplitudeStore();

useEffect(() => {
amplitude.init(process.env.AMPLITUDE_KEY, undefined, {
init(process.env.AMPLITUDE_KEY, undefined, {
defaultTracking: true,
});
}, []);
Expand Down
5 changes: 4 additions & 1 deletion client/src/components/CreatedEventList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {useCreatedEventsPageContext} from '@pages/main/events/CreatedEvent.conte
import {CreatedEvent} from 'types/serviceType';
import {CreatedEventItem} from '@components/Design/components/CreatedEventItem/CreatedEventItem';

import useAmplitude from '@hooks/useAmplitude';

import {FixedButton, Flex, Input} from '@components/Design';

type CreatedEventListProps = {
Expand All @@ -17,10 +19,11 @@ export const CreatedEventList = ({createdEvents, eventName, onSearch, placeholde
const setViewMode = () => handleMode('view');

const {deleteEvents} = useRequestDeleteEvents();
const {trackDeleteEvent} = useAmplitude();

const onDeleteClick = async () => {
const selectedEventsId = selectedEvents.map(event => event.eventId);
await deleteEvents({eventIds: selectedEventsId});
await deleteEvents({eventIds: selectedEventsId}, {onSuccess: () => trackDeleteEvent('multi')});
handleMode('view');
};

Expand Down
1 change: 1 addition & 0 deletions client/src/components/Design/components/Tabs/Tab.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {FlexProps} from '../Flex/Flex.type';
export interface TabProps {
label: string;
content: React.ReactNode;
onClick?: () => void;
}

export interface TabsCustomProps {
Expand Down
7 changes: 6 additions & 1 deletion client/src/components/Design/components/Tabs/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,12 @@ const Tabs: React.FC<TabsProps> = ({children, tabsContainerStyle}) => {
id={`tab-${tabItem.props.label}`}
css={tabItemStyle}
aria-selected={isActive(index)}
onClick={() => setActiveTabIndex(index)}
onClick={() => {
if (tabItem.props.onClick) {
tabItem.props.onClick();
}
setActiveTabIndex(index);
}}
aria-controls={`tabpanel-${tabItem.props.label}`}
>
<Text css={tabTextStyle({theme, selected: isActive(index)})} size={isActive(index) ? 'bodyBold' : 'body'}>
Expand Down
3 changes: 3 additions & 0 deletions client/src/components/EditAccountPageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import BankSelectModal from '@components/Modal/BankSelectModal/BankSelectModal';
import {BankAccount, BankName} from 'types/serviceType';

import useAccount from '@hooks/useAccount';
import useAmplitude from '@hooks/useAmplitude';

import {FixedButton, Flex, FunnelLayout, Input, MainLayout, Top, TopNav} from '@components/Design';

Expand All @@ -22,6 +23,7 @@ const EditAccountPageView = ({
redirectUrlOnSubmit,
}: EditAccountPageProps) => {
const navigate = useNavigate();
const {trackSetBankName} = useAmplitude();

const [isBottomSheetOpen, setIsBottomSheetOpen] = useState(false);

Expand All @@ -42,6 +44,7 @@ const EditAccountPageView = ({

const enrollAccountAndRedirectTo = async () => {
await enrollAccount();
trackSetBankName(bankName);

navigate(redirectUrlOnSubmit);
};
Expand Down
20 changes: 5 additions & 15 deletions client/src/components/ShareEventButton/DesktopShareEventButton.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,25 @@
import {useNavigate} from 'react-router-dom';

import toast from '@hooks/useToast/toast';

import {Dropdown, DropdownButton} from '@components/Design';

import getEventIdByUrl from '@utils/getEventIdByUrl';

type DesktopShareEventButtonProps = React.PropsWithChildren<React.HTMLAttributes<HTMLButtonElement>> & {
onCopy: () => Promise<void>;
copyShare: () => Promise<void>;
qrShare: () => void;
};

const DesktopShareEventButton = ({onCopy}: DesktopShareEventButtonProps) => {
const DesktopShareEventButton = ({copyShare, qrShare}: DesktopShareEventButtonProps) => {
const copyAndToast = async () => {
await onCopy();
await copyShare();
toast.confirm('링크가 복사되었어요 :) \n참여자들에게 링크를 공유해 주세요!', {
showingTime: 3000,
position: 'bottom',
});
};

const navigate = useNavigate();
const eventId = getEventIdByUrl();

const navigateQRPage = () => {
navigate(`/event/${eventId}/qrcode`);
};

return (
<Dropdown base="button" baseButtonText="정산 초대하기">
<DropdownButton text="링크 복사하기" onClick={copyAndToast} />
<DropdownButton text="QR코드로 초대하기" onClick={navigateQRPage} />
<DropdownButton text="QR코드로 초대하기" onClick={qrShare} />
</Dropdown>
);
};
Expand Down
15 changes: 3 additions & 12 deletions client/src/components/ShareEventButton/MobileShareEventButton.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import {useNavigate} from 'react-router-dom';

import toast from '@hooks/useToast/toast';

import {Dropdown, DropdownButton} from '@components/Design';

import getEventIdByUrl from '@utils/getEventIdByUrl';
import initKakao from '@utils/initKakao';

type MobileShareEventButtonProps = {
copyShare: () => Promise<void>;
kakaoShare: () => void;
qrShare: () => void;
};

const MobileShareEventButton = ({copyShare, kakaoShare}: MobileShareEventButtonProps) => {
const MobileShareEventButton = ({copyShare, kakaoShare, qrShare}: MobileShareEventButtonProps) => {
const copyAndToast = async () => {
await copyShare();
toast.confirm('링크가 복사되었어요 :) \n참여자들에게 링크를 공유해 주세요!', {
Expand All @@ -21,17 +19,10 @@ const MobileShareEventButton = ({copyShare, kakaoShare}: MobileShareEventButtonP
});
};

const navigate = useNavigate();
const eventId = getEventIdByUrl();

const navigateQRPage = () => {
navigate(`/event/${eventId}/qrcode`);
};

return (
<Dropdown base="button" baseButtonText="정산 초대하기" onBaseButtonClick={initKakao}>
<DropdownButton text="링크 복사하기" onClick={copyAndToast} />
<DropdownButton text="QR코드로 초대하기" onClick={navigateQRPage} />
<DropdownButton text="QR코드로 초대하기" onClick={qrShare} />
<DropdownButton text="카카오톡으로 초대하기" onClick={kakaoShare} />
</Dropdown>
);
Expand Down
16 changes: 15 additions & 1 deletion client/src/hooks/queries/user/useRequestGetUserInfo.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import {useSuspenseQuery} from '@tanstack/react-query';
import {useEffect} from 'react';

import {requestGetUserInfo} from '@apis/request/user';
import {UserInfo} from 'types/serviceType';

import {useAuthStore} from '@store/authStore';

import QUERY_KEYS from '@constants/queryKeys';

export type UseRequestGetUserInfo = ReturnType<typeof useRequestGetUserInfo>;
Expand All @@ -20,16 +23,27 @@ const useRequestGetUserInfo = ({enableInitialData = true}: UseRequestGetUserInfo
bankName: '',
};

const {data, ...rest} = useSuspenseQuery({
const {data, isSuccess, isError, ...rest} = useSuspenseQuery({
queryKey: [QUERY_KEYS.userInfo],
queryFn: () => requestGetUserInfo({errorHandlingStrategy: enableInitialData ? 'ignore' : 'errorBoundary'}),
// quernFn은 ErrorCatcher 구독을 하지 않으며 오류가 났을 경우 로그인 화면을 띄워야하므로 initialData를 설정했습니다.
initialData: enableInitialData ? initialData : undefined,
initialDataUpdatedAt: enableInitialData ? 0 : undefined,
});

const {updateKakaoAuth} = useAuthStore();

useEffect(() => {
if (isSuccess) {
updateKakaoAuth(!data.isGuest);
} else if (isError) {
updateKakaoAuth(false);
}
}, [isSuccess, isError]);

return {
userInfo: {...data},
isSuccess,
...rest,
};
};
Expand Down
4 changes: 3 additions & 1 deletion client/src/hooks/useAddImagesPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import getEventIdByUrl from '@utils/getEventIdByUrl';
import useRequestGetImages from './queries/images/useRequestGetImages';
import useRequestPostImages from './queries/images/useRequestPostImages';
import useRequestDeleteImage from './queries/images/useRequestDeleteImages';
import useAmplitude from './useAmplitude';

type LoadedImage = ImageFile;
type AddedImage = File;
Expand All @@ -31,6 +32,7 @@ const useAddImagesPage = () => {

const {postImages, isPending} = useRequestPostImages();
const {deleteImage} = useRequestDeleteImage();
const {trackUploadImageCount} = useAmplitude();

useEffect(() => {
if (!isSuccess) return;
Expand Down Expand Up @@ -65,7 +67,7 @@ const useAddImagesPage = () => {
formData.append('images', addedImages[i], addedImages[i].name);
}

await postImages({formData});
await postImages({formData}, {onSuccess: () => trackUploadImageCount(addedImages.length)});
}

navigate(`/event/${eventId}/admin`);
Expand Down
Loading

0 comments on commit 52c9929

Please sign in to comment.