diff --git a/@types/global.d.ts b/@types/global.d.ts index e16466d7..bd0bfadf 100644 --- a/@types/global.d.ts +++ b/@types/global.d.ts @@ -3,6 +3,10 @@ declare global { ReactNativeWebView: { postMessage(msg: string): void; }; + mixpanel: { + track(event_name: string, ...props: unknown): void; + identify(userId: string): void; + }; } } diff --git a/src/components/common/UserProvider/UserProvider.tsx b/src/components/common/UserProvider/UserProvider.tsx index e4221ac5..26d38c98 100644 --- a/src/components/common/UserProvider/UserProvider.tsx +++ b/src/components/common/UserProvider/UserProvider.tsx @@ -31,7 +31,6 @@ export function UserProvider({ children }: PropsWithChildren) { const { isRouterGuardPassed } = useRouterGuard({ isLoaded }); - // 컴포넌트 마운트 시 useDidMount(() => { if (isLoaded) return; diff --git a/src/hooks/api/member/useGetUserInformation.ts b/src/hooks/api/member/useGetUserInformation.ts index f5e9c292..d0a4acb5 100644 --- a/src/hooks/api/member/useGetUserInformation.ts +++ b/src/hooks/api/member/useGetUserInformation.ts @@ -4,11 +4,11 @@ import { get } from '~/libs/api/client'; export const USER_INFORMATION_QUERY_KEY = 'userInformation'; -export default function useGetUserInformation(onSuccess?: (data: UserInformationType) => void) { - const fetchUserInformation = () => { - return get(`/v1/members/info`); - }; +export const fetchUserInformation = () => { + return get(`/v1/members/info`); +}; +export default function useGetUserInformation(onSuccess?: (data: UserInformationType) => void) { const query = useQuery( [USER_INFORMATION_QUERY_KEY], async () => await fetchUserInformation(), diff --git a/src/hooks/common/useUser/useUser.ts b/src/hooks/common/useUser/useUser.ts index c20e0951..238c5e58 100644 --- a/src/hooks/common/useUser/useUser.ts +++ b/src/hooks/common/useUser/useUser.ts @@ -2,8 +2,10 @@ import { useCallback, useState } from 'react'; import { useQueryClient } from '@tanstack/react-query'; import { localStorageUserTokenKeys } from '~/constants/localStorage'; +import { fetchUserInformation } from '~/hooks/api/member/useGetUserInformation'; import useInternalRouter from '~/hooks/common/useInternalRouter'; import { replaceAccessTokenForRequestInstance } from '~/libs/api/client'; +import { setMixpanelIdentify } from '~/libs/mixpanel'; const SYNC_YGT_RT = 'SYNC_YGT_RT'; @@ -21,6 +23,13 @@ export function useUser() { } }; + const setMixpanelIdentifyWhenLogedIn = async () => { + const { nickName, email } = await fetchUserInformation(); + if (!nickName || !email) return; + + setMixpanelIdentify(`${nickName} - ${email}`); + }; + /** * 유저 로그인 시에 사용합니다. * @param accessToken 엑세스 토큰 값 @@ -42,6 +51,8 @@ export function useUser() { localStorage.setItem(localStorageUserTokenKeys.refreshToken, refreshToken); postRefreshTokenReactNativeWebView(refreshToken); queryClient.clear(); + + setMixpanelIdentifyWhenLogedIn(); }, [queryClient] ); diff --git a/src/libs/mixpanel/index.test.ts b/src/libs/mixpanel/index.test.ts new file mode 100644 index 00000000..297f825e --- /dev/null +++ b/src/libs/mixpanel/index.test.ts @@ -0,0 +1,11 @@ +import { mixpanelTrack, setMixpanelIdentify } from './index'; + +describe('libs/mixpanel/index', () => { + it('mixpanelTrack이 정의되어 있어야 함', () => { + expect(mixpanelTrack).toBeDefined(); + }); + + it('setMixpanelIdentify이 정의되어 있어야 함', () => { + expect(setMixpanelIdentify).toBeDefined(); + }); +}); diff --git a/src/libs/mixpanel/index.ts b/src/libs/mixpanel/index.ts index 9576d46a..a6f318e4 100644 --- a/src/libs/mixpanel/index.ts +++ b/src/libs/mixpanel/index.ts @@ -1 +1 @@ -export { mixpanelTrack } from './mixpanel'; +export { mixpanelTrack, setMixpanelIdentify } from './mixpanel'; diff --git a/src/libs/mixpanel/mixpanel.test.ts b/src/libs/mixpanel/mixpanel.test.ts new file mode 100644 index 00000000..ffafd6b3 --- /dev/null +++ b/src/libs/mixpanel/mixpanel.test.ts @@ -0,0 +1,49 @@ +import { mixpanelTrack, setMixpanelIdentify } from './mixpanel'; + +describe('libs/mixpanel/mixpanel', () => { + const mixpanelTrackSpy = jest.fn(); + const mixpanelIdentifySpy = jest.fn(); + + const setWindowMixpanelWithSpy = () => { + Object.assign(window, { mixpanel: { track: mixpanelTrackSpy, identify: mixpanelIdentifySpy } }); + }; + + const setWindowMixpanelToUndefined = () => { + Object.assign(window, { mixpanel: undefined }); + }; + + describe('window에 mixpanel이 없을 시', () => { + beforeEach(() => { + setWindowMixpanelToUndefined(); + }); + + it('window.mixpanel.track이 호출되지 않음', () => { + mixpanelTrack('test', { test: 'test' }); + expect(mixpanelTrackSpy).not.toHaveBeenCalled(); + }); + + it('window.mixpanel.identify가 호출되지 않음', () => { + setMixpanelIdentify('test'); + expect(mixpanelIdentifySpy).not.toHaveBeenCalled(); + }); + }); + + describe('window에 mixpanel이 있을 시', () => { + beforeEach(() => { + setWindowMixpanelWithSpy(); + }); + + it('window.mixpanel.track이 호출됨', () => { + setWindowMixpanelWithSpy(); + mixpanelTrack('test', { test: 'test' }); + expect(mixpanelTrackSpy).toHaveBeenCalled(); + expect(mixpanelTrackSpy).toHaveBeenCalledWith('test', [{ test: 'test' }]); + }); + + it('mixpanel.identify가 호출됨', () => { + setMixpanelIdentify('test'); + expect(mixpanelIdentifySpy).toHaveBeenCalled(); + expect(mixpanelIdentifySpy).toHaveBeenCalledWith('test'); + }); + }); +}); diff --git a/src/libs/mixpanel/mixpanel.ts b/src/libs/mixpanel/mixpanel.ts index fe41cc32..ecd0defe 100644 --- a/src/libs/mixpanel/mixpanel.ts +++ b/src/libs/mixpanel/mixpanel.ts @@ -1,9 +1,21 @@ -export function mixpanelTrack(event_name: string, ...props: any) { +function callWhenMixpanelReady(callback: VoidFunction) { try { - if ((window as any).mixpanel) { - (window as any).mixpanel.track(event_name, props); + if (window?.mixpanel) { + callback(); } } catch (e) { - console.log(e); + console.error(e); } } + +export function mixpanelTrack(event_name: string, ...props: any) { + callWhenMixpanelReady(() => { + window.mixpanel.track(event_name, props); + }); +} + +export function setMixpanelIdentify(userId: string) { + callWhenMixpanelReady(() => { + window.mixpanel.identify(userId); + }); +} diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx index 077474fc..758452f8 100644 --- a/src/pages/login/index.tsx +++ b/src/pages/login/index.tsx @@ -62,7 +62,6 @@ export default function Login() { }, [password.debouncedValue]); useDidUpdate(() => { - // if (loginMutationData && loginMutationData.data) { userLogin({ accessToken: loginMutationData.data.accessToken,