diff --git a/apps/client/src/App.tsx b/apps/client/src/App.tsx index 9abf403b..adb15a6a 100644 --- a/apps/client/src/App.tsx +++ b/apps/client/src/App.tsx @@ -1,22 +1,14 @@ -import { analytics } from '@pinback/analytics'; import { router } from '@routes/router'; -import { useGetAmplitudeUserProperties } from '@shared/apis/queries'; -import { useEffect } from 'react'; +import AmplitudeProvider from 'src/providers/AmplitudeProvider'; import { RouterProvider } from 'react-router-dom'; import './App.css'; function App() { - const { data: userProperties } = useGetAmplitudeUserProperties(); - - useEffect(() => { - if (!userProperties) return; - analytics.identify( - String(userProperties.userId), - userProperties.jobRole ? { job_role: userProperties.jobRole } : undefined - ); - }, [userProperties]); - - return ; + return ( + + + + ); } export default App; diff --git a/apps/client/src/pages/onBoarding/GoogleCallback.tsx b/apps/client/src/pages/onBoarding/GoogleCallback.tsx index 6812524b..dacbcb9a 100644 --- a/apps/client/src/pages/onBoarding/GoogleCallback.tsx +++ b/apps/client/src/pages/onBoarding/GoogleCallback.tsx @@ -6,76 +6,71 @@ import { useQueryClient } from '@tanstack/react-query'; import { useEffect } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; +const REDIRECT_URI = import.meta.env.VITE_GOOGLE_REDIRECT_URI; + const GoogleCallback = () => { const navigate = useNavigate(); const [searchParams] = useSearchParams(); const queryClient = useQueryClient(); - useEffect(() => { - const code = searchParams.get('code'); + const saveSession = (params: { + accessToken: string | null; + refreshToken: string | null; + email: string; + userId: string; + hasJob?: boolean; + }) => { + const { accessToken, refreshToken, email, userId, hasJob } = params; - if (!code) { - alert('로그인 실패. 다시 시도해주세요.'); - navigate('/onboarding?step=SOCIAL_LOGIN'); - return; + authStorage.setUserIdentity(email, userId); + + if (accessToken) { + authStorage.setAccessToken(accessToken); + extensionBridge.syncToken(accessToken); } - loginWithCode(code); - }, []); + if (refreshToken) { + authStorage.setRefreshToken(refreshToken); + } - const handleUserLogin = ( - isUser: boolean, - accessToken: string | null, - refreshToken: string | null, - hasJob?: boolean - ) => { - if (isUser) { - if (accessToken) { - authStorage.setAccessToken(accessToken); - extensionBridge.syncToken(accessToken); - } - - if (refreshToken) { - authStorage.setRefreshToken(refreshToken); - } - - if (typeof hasJob === 'boolean') { - authStorage.setHasJob(hasJob); - } - navigate('/'); - } else { - navigate('/onboarding?step=JOB'); + if (typeof hasJob === 'boolean') { + authStorage.setHasJob(hasJob); } }; - const redirectUri = import.meta.env.VITE_GOOGLE_REDIRECT_URI; - const loginWithCode = async (code: string) => { try { const res = await apiRequest.post( '/api/v3/auth/google', - { - code, - uri: redirectUri, - }, - { - withCredentials: true, - } + { code, uri: REDIRECT_URI }, + { withCredentials: true } ); const { isUser, userId, email, accessToken, refreshToken, hasJob } = res.data.data; - authStorage.setUserIdentity(email, userId); + saveSession({ accessToken, refreshToken, email, userId, hasJob }); queryClient.invalidateQueries({ queryKey: ['amplitudeUserProperties'] }); - handleUserLogin(isUser, accessToken, refreshToken, hasJob); + navigate(isUser ? '/' : '/onboarding?step=JOB'); } catch (error) { console.error('로그인 오류:', error); navigate('/onboarding?step=SOCIAL_LOGIN'); } }; + useEffect(() => { + const code = searchParams.get('code'); + + if (!code) { + alert('로그인 실패. 다시 시도해주세요.'); + navigate('/onboarding?step=SOCIAL_LOGIN'); + return; + } + + loginWithCode(code); + }, []); + return (
diff --git a/apps/client/src/providers/AmplitudeProvider.tsx b/apps/client/src/providers/AmplitudeProvider.tsx new file mode 100644 index 00000000..2cf66bf1 --- /dev/null +++ b/apps/client/src/providers/AmplitudeProvider.tsx @@ -0,0 +1,19 @@ +import { analytics } from '@pinback/analytics'; +import { useGetAmplitudeUserProperties } from '@shared/apis/queries'; +import { useEffect, type PropsWithChildren } from 'react'; + +const AmplitudeProvider = ({ children }: PropsWithChildren) => { + const { data: userProperties } = useGetAmplitudeUserProperties(); + + useEffect(() => { + if (!userProperties) return; + analytics.identify( + String(userProperties.userId), + userProperties.jobRole ? { job_role: userProperties.jobRole } : undefined + ); + }, [userProperties]); + + return <>{children}; +}; + +export default AmplitudeProvider; diff --git a/apps/client/src/shared/apis/queries.ts b/apps/client/src/shared/apis/queries.ts index 7498dbb3..7e9d699f 100644 --- a/apps/client/src/shared/apis/queries.ts +++ b/apps/client/src/shared/apis/queries.ts @@ -229,6 +229,7 @@ export const useGetAmplitudeUserProperties = queryFn: getAmplitudeUserProperties, staleTime: Infinity, retry: false, + enabled: authStorage.hasAccessToken(), }); }; diff --git a/apps/client/src/shared/apis/setting/axiosInstance.ts b/apps/client/src/shared/apis/setting/axiosInstance.ts index 8cd853a0..76f814ed 100644 --- a/apps/client/src/shared/apis/setting/axiosInstance.ts +++ b/apps/client/src/shared/apis/setting/axiosInstance.ts @@ -76,7 +76,7 @@ apiRequest.interceptors.response.use( if ( error.response && - (error.response.status === 401 || error.response.status === 403) && + error.response.status === 401 && !originalRequest._retry && !isNoAuth && !isLoginPage diff --git a/packages/analytics/index.ts b/packages/analytics/index.ts index e940f80d..ad59b9ab 100644 --- a/packages/analytics/index.ts +++ b/packages/analytics/index.ts @@ -1,6 +1,6 @@ import { analytics } from './src/analytics'; -import { amplitudeProvider } from './src/providers/amplitude'; -import { consoleProvider } from './src/providers/console'; +import { amplitudeAdapter } from './src/adapters/amplitude'; +import { consoleAdapter } from './src/adapters/console'; export type { AnalyticsProvider, UserProperties } from './src/types'; export type * from './src/ampli'; @@ -13,16 +13,16 @@ interface InitAnalyticsOptions { export const initAnalytics = ({ apiKey, isDev }: InitAnalyticsOptions): void => { if (isDev || !apiKey) { - analytics.setProvider(consoleProvider); + analytics.setProvider(consoleAdapter); return; } try { - amplitudeProvider.init(apiKey); - analytics.setProvider(amplitudeProvider); + amplitudeAdapter.init(apiKey); + analytics.setProvider(amplitudeAdapter); } catch (error) { - console.error('[Analytics] Failed to initialize Amplitude, falling back to console provider', error); - analytics.setProvider(consoleProvider); + console.error('[Analytics] Failed to initialize Amplitude, falling back to console adapter', error); + analytics.setProvider(consoleAdapter); } }; diff --git a/packages/analytics/src/providers/amplitude.ts b/packages/analytics/src/adapters/amplitude.ts similarity index 95% rename from packages/analytics/src/providers/amplitude.ts rename to packages/analytics/src/adapters/amplitude.ts index 37a785fa..18009f6d 100644 --- a/packages/analytics/src/providers/amplitude.ts +++ b/packages/analytics/src/adapters/amplitude.ts @@ -2,7 +2,7 @@ import { Identify, identify, setUserId, reset, track, initAll } from '@amplitude import type { AnalyticsProvider, UserProperties } from '../types'; -export const amplitudeProvider: AnalyticsProvider = { +export const amplitudeAdapter: AnalyticsProvider = { init(apiKey: string) { initAll(apiKey, { analytics: { diff --git a/packages/analytics/src/providers/console.ts b/packages/analytics/src/adapters/console.ts similarity index 86% rename from packages/analytics/src/providers/console.ts rename to packages/analytics/src/adapters/console.ts index 5bf131a8..81ffc850 100644 --- a/packages/analytics/src/providers/console.ts +++ b/packages/analytics/src/adapters/console.ts @@ -1,6 +1,6 @@ import type { AnalyticsProvider } from '../types'; -export const consoleProvider: AnalyticsProvider = { +export const consoleAdapter: AnalyticsProvider = { init: (_apiKey) => console.log('[Analytics] init'), track: (name, props) => console.log('[Analytics] track', name, props ?? {}), identify: (userId, userProperties) => console.log('[Analytics] identify', { userId, ...userProperties }), diff --git a/packages/analytics/src/providers/noop.ts b/packages/analytics/src/adapters/noop.ts similarity index 75% rename from packages/analytics/src/providers/noop.ts rename to packages/analytics/src/adapters/noop.ts index 52b0062f..59007fe5 100644 --- a/packages/analytics/src/providers/noop.ts +++ b/packages/analytics/src/adapters/noop.ts @@ -1,6 +1,6 @@ import type { AnalyticsProvider } from '../types'; -export const noopProvider: AnalyticsProvider = { +export const noopAdapter: AnalyticsProvider = { init: () => {}, track: () => {}, identify: (_userId: string) => {}, diff --git a/packages/analytics/src/analytics.ts b/packages/analytics/src/analytics.ts index e49b2090..611c28f6 100644 --- a/packages/analytics/src/analytics.ts +++ b/packages/analytics/src/analytics.ts @@ -1,7 +1,7 @@ -import { noopProvider } from './providers/noop'; +import { noopAdapter } from './adapters/noop'; import type { AnalyticsProvider, UserProperties } from './types'; -let provider: AnalyticsProvider = noopProvider; +let provider: AnalyticsProvider = noopAdapter; export const analytics = { setProvider(newProvider: AnalyticsProvider): void {