Fix(project): fix amplitude loading issue #331
Fix(project): fix amplitude loading issue #331constantly-dev wants to merge 4 commits intodevelopfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAmplitude 분석 통합을 App.tsx에서 새로운 AmplitudeProvider로 이동하고, GoogleCallback 인증 흐름을 REDIRECT_URI 상수와 saveSession 헬퍼로 리팩토링했습니다. 분석 패키지의 provider 명칭을 adapter로 통일하고, 쿼리 실행을 인증 상태에 따라 조건부로 제어하도록 변경했습니다. Changes
Sequence DiagramsequenceDiagram
participant Client as 클라이언트
participant App as App.tsx
participant AP as AmplitudeProvider
participant GC as GoogleCallback
participant AS as AuthStorage
participant Analytics as Analytics
participant Backend as 백엔드
Client->>App: 앱 진입
App->>AP: AmplitudeProvider 래핑
AP->>Analytics: userProperties 감시 시작
Client->>GC: Google 로그인 클릭
GC->>Backend: Google OAuth code 전달
Backend->>GC: 인증 토큰 반환
GC->>AS: saveSession 호출 (email, userId)
AS->>AS: 세션 저장
GC->>AS: accessToken 조건부 저장
GC->>AS: refreshToken 조건부 저장
AP->>Analytics: analytics.identify(userId, jobRole)
Analytics->>Analytics: Amplitude 사용자 식별
GC->>Client: isUser 여부에 따라 네비게이션
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (3 warnings)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
✅ Storybook chromatic 배포 확인: |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/client/src/pages/onBoarding/GoogleCallback.tsx`:
- Around line 9-10: The component uses REDIRECT_URI (const REDIRECT_URI =
import.meta.env.VITE_GOOGLE_REDIRECT_URI) directly when building the login
request (the code that sets uri: REDIRECT_URI around the GoogleCallback logic),
so add a guard that checks if REDIRECT_URI is a non-empty string before sending
the request; if missing, log an error (or call the app's error handler), show a
user-friendly message and return early to avoid making the request. Update the
block that constructs/sends the request (the place that sets uri: REDIRECT_URI)
to validate REDIRECT_URI and abort the operation when undefined/empty.
In `@apps/client/src/providers/AmplitudeProvider.tsx`:
- Around line 11-13: The code currently forces String(userProperties.userId)
which will serialize null/undefined to "null"/"undefined" and send invalid
identifiers; update the call that uses String(userProperties.userId) (the userId
argument where userProperties.userId is referenced) to first nullish-check
userProperties.userId and only convert when present—e.g., use
userProperties.userId != null ? String(userProperties.userId) : undefined (or
omit the argument) so no literal "undefined" is sent.
In `@apps/client/src/shared/apis/queries.ts`:
- Line 232: The enabled check for the amplitudeUserProperties query uses a
one-time snapshot authStorage.hasAccessToken() (in queries.ts) which can remain
false after token refresh paths (axiosInstance.ts → syncAccessToken and
usePostSignUp onSuccess → setAccessToken) and thus never re-enable identify; fix
by either making the enabled condition reactive (read from a reactive auth
state/store or subscribe to authStorage changes instead of calling
hasAccessToken() once in useQuery) or explicitly invalidate the
amplitudeUserProperties query when tokens are set/renewed (call
queryClient.invalidateQueries for the amplitudeUserProperties key from
syncAccessToken and from usePostSignUp onSuccess where setAccessToken is called)
so the identify side-effect always runs after login/signup/token refresh.
In `@packages/analytics/index.ts`:
- Around line 21-23: The AnalyticsProvider init signature should return
Promise<void> (not void); update the interface accordingly, make amplitude.ts's
init method async and await the `@amplitude/unified` initAll call (e.g., async
init(apiKey: string) { await initAll(...) }), then update index.ts to await
amplitudeAdapter.init(apiKey) and mark initAnalytics as async returning
Promise<void> so the try/catch can catch initialization errors before calling
analytics.setProvider.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: c8a67491-bc9e-4f1f-ad50-ca380152ca5c
📒 Files selected for processing (10)
apps/client/src/App.tsxapps/client/src/pages/onBoarding/GoogleCallback.tsxapps/client/src/providers/AmplitudeProvider.tsxapps/client/src/shared/apis/queries.tsapps/client/src/shared/apis/setting/axiosInstance.tspackages/analytics/index.tspackages/analytics/src/adapters/amplitude.tspackages/analytics/src/adapters/console.tspackages/analytics/src/adapters/noop.tspackages/analytics/src/analytics.ts
| const REDIRECT_URI = import.meta.env.VITE_GOOGLE_REDIRECT_URI; | ||
|
|
There was a problem hiding this comment.
환경변수 누락 시 로그인 요청을 보내지 않도록 가드가 필요합니다.
Line 45에서 uri: REDIRECT_URI를 사용하지만, REDIRECT_URI 미설정 시 undefined로 요청이 전송됩니다. 배포/환경설정 실수에서 전면 로그인 실패로 이어질 수 있어 사전 차단이 필요합니다.
수정 제안 diff
const GoogleCallback = () => {
@@
const loginWithCode = async (code: string) => {
+ if (!REDIRECT_URI) {
+ console.error('VITE_GOOGLE_REDIRECT_URI is not configured');
+ alert('로그인 설정 오류가 발생했습니다. 잠시 후 다시 시도해주세요.');
+ navigate('/onboarding?step=SOCIAL_LOGIN');
+ return;
+ }
+
try {
const res = await apiRequest.post(
'/api/v3/auth/google',
{ code, uri: REDIRECT_URI },
{ withCredentials: true }
);Also applies to: 45-47
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/client/src/pages/onBoarding/GoogleCallback.tsx` around lines 9 - 10, The
component uses REDIRECT_URI (const REDIRECT_URI =
import.meta.env.VITE_GOOGLE_REDIRECT_URI) directly when building the login
request (the code that sets uri: REDIRECT_URI around the GoogleCallback logic),
so add a guard that checks if REDIRECT_URI is a non-empty string before sending
the request; if missing, log an error (or call the app's error handler), show a
user-friendly message and return early to avoid making the request. Update the
block that constructs/sends the request (the place that sets uri: REDIRECT_URI)
to validate REDIRECT_URI and abort the operation when undefined/empty.
| String(userProperties.userId), | ||
| userProperties.jobRole ? { job_role: userProperties.jobRole } : undefined | ||
| ); |
There was a problem hiding this comment.
userId nullish 가드 없이 문자열 변환하면 잘못된 식별자가 전송될 수 있습니다.
Line 11에서 String(...) 강제 변환 시 "undefined" 같은 값이 전송될 수 있으니 최소 nullish 체크를 넣는 편이 안전합니다.
수정 예시
useEffect(() => {
if (!userProperties) return;
+ if (userProperties.userId == null) return;
analytics.identify(
String(userProperties.userId),
userProperties.jobRole ? { job_role: userProperties.jobRole } : undefined
);
}, [userProperties]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/client/src/providers/AmplitudeProvider.tsx` around lines 11 - 13, The
code currently forces String(userProperties.userId) which will serialize
null/undefined to "null"/"undefined" and send invalid identifiers; update the
call that uses String(userProperties.userId) (the userId argument where
userProperties.userId is referenced) to first nullish-check
userProperties.userId and only convert when present—e.g., use
userProperties.userId != null ? String(userProperties.userId) : undefined (or
omit the argument) so no literal "undefined" is sent.
| queryFn: getAmplitudeUserProperties, | ||
| staleTime: Infinity, | ||
| retry: false, | ||
| enabled: authStorage.hasAccessToken(), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 목적: 토큰 저장/세션 초기화 시 amplitudeUserProperties 쿼리 재실행 경로가 있는지 확인
rg -n -C3 --type=ts --type=tsx "setAccessToken\(|clearSession\(|useGetAmplitudeUserProperties\(|amplitudeUserProperties|invalidateQueries\("
# 목적: authStorage 기반 enabled 패턴이 다른 곳에서도 동일하게 사용되는지 확인
rg -n -C2 --type=ts --type=tsx "enabled:\s*authStorage\."Repository: Pinback-Team/pinback-client
Length of output: 126
🏁 Script executed:
#!/bin/bash
# Search for patterns related to token storage, session clearing, and amplitude query
# Using correct rg syntax without problematic file type declarations
echo "=== Searching for setAccessToken, clearSession, and query invalidation patterns ==="
rg -n -C3 "setAccessToken|clearSession|amplitudeUserProperties|invalidateQueries" \
--include="*.ts" --include="*.tsx"
echo ""
echo "=== Searching for enabled conditions with authStorage ==="
rg -n -C2 "enabled.*authStorage|authStorage.*enabled" \
--include="*.ts" --include="*.tsx"Repository: Pinback-Team/pinback-client
Length of output: 1114
🏁 Script executed:
#!/bin/bash
# Find the queries.ts file and read around line 232
echo "=== Finding queries.ts file ==="
fd -t f "queries.ts"
echo ""
echo "=== Read queries.ts to see context around line 232 ==="
if [ -f "apps/client/src/shared/apis/queries.ts" ]; then
cat -n "apps/client/src/shared/apis/queries.ts" | sed -n '200,270p'
fi
echo ""
echo "=== Search for amplitudeUserProperties and authStorage in all ts/tsx files ==="
cd apps/client/src && find . -name "*.ts" -o -name "*.tsx" | xargs grep -l "amplitudeUserProperties\|authStorage" 2>/dev/null | head -20Repository: Pinback-Team/pinback-client
Length of output: 2509
🏁 Script executed:
#!/bin/bash
echo "=== Read authStorage.ts to understand token storage ==="
cat -n "apps/client/src/shared/utils/authStorage.ts"
echo ""
echo "=== Search for setAccessToken in the codebase ==="
grep -rn "setAccessToken" apps/client/src --include="*.ts" --include="*.tsx" | head -30
echo ""
echo "=== Search for token invalidation or refetch patterns ==="
grep -rn "invalidateQueries\|refetch" apps/client/src --include="*.ts" --include="*.tsx" | grep -i "token\|auth\|amplitude" | head -20
echo ""
echo "=== Check GoogleCallback.tsx to see how amplitudeUserProperties is used ==="
cat -n "apps/client/src/pages/onBoarding/GoogleCallback.tsx"Repository: Pinback-Team/pinback-client
Length of output: 4995
🏁 Script executed:
#!/bin/bash
echo "=== Check token refresh flow in queries.ts ==="
cat -n "apps/client/src/shared/apis/queries.ts" | sed -n '80,120p'
echo ""
echo "=== Check axiosInstance.ts token setter context ==="
cat -n "apps/client/src/shared/apis/setting/axiosInstance.ts" | head -50
echo ""
echo "=== Search for other places where token is set or session changes ==="
grep -rn "setAccessToken\|clearSession" apps/client/src --include="*.ts" --include="*.tsx"
echo ""
echo "=== Check Layout.tsx for how it uses amplitudeUserProperties ==="
grep -n "amplitudeUserProperties\|useGetAmplitudeUserProperties" apps/client/src/layout/Layout.tsx -B3 -A3Repository: Pinback-Team/pinback-client
Length of output: 3862
🏁 Script executed:
#!/bin/bash
echo "=== Try reading Layout.tsx directly ==="
if [ -f "apps/client/src/layout/Layout.tsx" ]; then
cat -n "apps/client/src/layout/Layout.tsx"
else
echo "File not found, searching for Layout file..."
find apps/client/src -name "Layout*" -type f
fi
echo ""
echo "=== Check if query is used anywhere with explicit invalidation ==="
grep -rn "invalidateQueries.*amplitudeUserProperties\|amplitudeUserProperties.*invalidateQueries" apps/client/src
echo ""
echo "=== Check full context of token refresh in axiosInstance ==="
cat -n "apps/client/src/shared/apis/setting/axiosInstance.ts" | sed -n '50,150p'Repository: Pinback-Team/pinback-client
Length of output: 5453
enabled 필드가 localStorage 스냅샷으로만 평가되므로, 토큰 갱신 후 쿼리가 영구 비활성화될 수 있습니다.
enabled: authStorage.hasAccessToken()는 Hook 호출 시점의 동기 스냅샷이며 반응형이 아닙니다. 문제는 다음 경로에서 발생합니다:
- 토큰 갱신 시 (axiosInstance.ts 응답 인터셉터 → syncAccessToken): 쿼리 무효화 없음
- 회원가입 완료 시 (queries.ts usePostSignUp onSuccess → setAccessToken): 쿼리 무효화 없음
- 로그인 시 (GoogleCallback.tsx): 명시적 invalidateQueries 있음 ✓
staleTime: Infinity와 함께, 토큰이 설정되더라도 Hook을 포함한 컴포넌트가 다시 렌더링되지 않으면 enabled 조건이 재평가되지 않아 identify 사이드이펙트가 누락될 수 있습니다. 토큰 갱신 경로 2곳에서도 amplitudeUserProperties 쿼리를 명시적으로 무효화하거나, enabled 조건을 반응형 상태에 연동해주세요.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/client/src/shared/apis/queries.ts` at line 232, The enabled check for
the amplitudeUserProperties query uses a one-time snapshot
authStorage.hasAccessToken() (in queries.ts) which can remain false after token
refresh paths (axiosInstance.ts → syncAccessToken and usePostSignUp onSuccess →
setAccessToken) and thus never re-enable identify; fix by either making the
enabled condition reactive (read from a reactive auth state/store or subscribe
to authStorage changes instead of calling hasAccessToken() once in useQuery) or
explicitly invalidate the amplitudeUserProperties query when tokens are
set/renewed (call queryClient.invalidateQueries for the amplitudeUserProperties
key from syncAccessToken and from usePostSignUp onSuccess where setAccessToken
is called) so the identify side-effect always runs after login/signup/token
refresh.
| amplitudeAdapter.init(apiKey); | ||
| analytics.setProvider(amplitudeAdapter); | ||
| } catch (error) { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== amplitude adapter 파일 위치 및 init 정의 확인 =="
fd -i 'amplitude.ts' | while read -r f; do
echo "-- $f"
rg -n -C3 'init\s*[:(]|amplitudeAdapter' "$f"
done
echo
echo "== init 호출부 확인 (await 사용 여부) =="
rg -n -C2 'amplitudeAdapter\.init\('
echo
echo "== Promise 반환 타입 힌트 검색 =="
rg -n -C2 'init\s*:\s*\(.*\)\s*=>\s*Promise|async\s+init\s*\('Repository: Pinback-Team/pinback-client
Length of output: 712
🏁 Script executed:
cat packages/analytics/src/adapters/amplitude.tsRepository: Pinback-Team/pinback-client
Length of output: 1311
🏁 Script executed:
cat packages/analytics/src/types.tsRepository: Pinback-Team/pinback-client
Length of output: 353
🏁 Script executed:
rg -A 5 'function initAll|export.*initAll' packages/analytics/Repository: Pinback-Team/pinback-client
Length of output: 53
🏁 Script executed:
# Check if node_modules or package.json has amplitude info
rg -A 3 '"@amplitude/unified"' packages/analytics/package.jsonRepository: Pinback-Team/pinback-client
Length of output: 168
🏁 Script executed:
# Search for any usage of initAll in the codebase to see if it's awaited elsewhere
rg -n 'initAll' packages/Repository: Pinback-Team/pinback-client
Length of output: 277
🏁 Script executed:
# Check the index.ts file context more broadly to understand error handling intent
cat -n packages/analytics/index.ts | head -30Repository: Pinback-Team/pinback-client
Length of output: 1150
🌐 Web query:
@amplitude/unified v1.0.10 initAll function return type Promise async
💡 Result:
The initAll function in @amplitude/unified v1.0.10 returns a Promise. It is an asynchronous function that resolves when the SDKs (Analytics, Experiment, Session Replay, etc.) are initialized. From the TypeScript definitions (unified.d.ts), the signature is: initAll(apiKey: string, unifiedOptions?: UnifiedOptions): Promise<void> (inferred void since no generic type specified). This is consistent across documented versions (1.0.1+), official Amplitude docs, and npm package files. Usage examples show it can be awaited: await initAll('API_KEY', options);
Citations:
- 1: https://app.unpkg.com/@amplitude/unified@1.0.1/files/lib/esm/unified.d.ts
- 2: https://registry.npmjs.org/%40amplitude%2Funified
- 3: https://amplitude.com/docs/sdks/analytics/browser/browser-unified-sdk
- 4: chore(unified): create a new package @amplitude/unified amplitude/Amplitude-TypeScript#1039
🌐 Web query:
@amplitude/unified initAll documentation TypeScript types
💡 Result:
The @amplitude/unified package provides the initAll function for initializing Amplitude's Unified SDK for web, which handles Analytics, Experiment, Session Replay, Guides & Surveys, and more in a single entry point. ## Installation npm install @amplitude/unified ## Import typescript import { initAll } from '@amplitude/unified'; ## Usage typescript initAll('YOUR_API_KEY', { // Shared options (optional) serverZone: 'US', // or 'EU' instanceName: 'my-instance', // Product-specific options analytics: { // Options from @amplitude/analytics-browser, e.g., { autocapture: true } }, sessionReplay: { // Options from @amplitude/plugin-session-replay-browser, e.g., { sampleRate: 1 } }, experiment: { // Options from @amplitude/plugin-experiment-browser, e.g., { deploymentKey: 'your-key' } }, engagement: { // Options for Guides & Surveys }, }); Returns Promise. After initialization, import and use features: typescript import { track, identify, experiment, sessionReplay } from '@amplitude/unified'; track('Button Clicked'); ## TypeScript Types From unified.d.ts: typescript interface UnifiedOptions { serverZone?: 'US' | 'EU'; instanceName?: string; analytics?: Omit<BrowserOptions, 'apiKey'>; // From @amplitude/analytics-browser sessionReplay?: /* Session Replay config /; experiment?: / Experiment config /; engagement?: / Engagement config /; } interface UnifiedClient { initAll(apiKey: string, unifiedOptions?: UnifiedOptions): Promise; // Plus methods like sessionReplay: AmplitudeSessionReplay, etc. } Full types are in the package's lib//unified.d.ts and generated docs at Amplitude-TypeScript GitHub. Analytics options reference Browser SDK 2 docs. Package latest versions from 2025-2026 (e.g., 1.0.14 as of Mar 2026).
Citations:
- 1: https://registry.npmjs.org/%40amplitude%2Funified
- 2: https://npmjs.com/package/@amplitude/unified
- 3: https://amplitude.com/docs/sdks/analytics/browser/browser-unified-sdk
- 4: https://app.unpkg.com/@amplitude/unified@1.0.1/files/lib/esm/unified.d.ts
- 5: chore(unified): create a new package @amplitude/unified amplitude/Amplitude-TypeScript#1039
- 6: https://www.npmjs.com/package/@amplitude/unified
- 7: https://github.com/amplitude/Amplitude-TypeScript
amplitudeAdapter.init은 비동기 함수이며, 현재 구현에서 Promise가 처리되지 않고 있습니다.
@amplitude/unified의 initAll은 Promise<void>를 반환하는 비동기 함수입니다. 하지만 amplitude.ts의 init 메서드에서 initAll 호출 시 await를 사용하지 않아, Promise가 대기되지 않습니다. 이로 인해 index.ts의 try/catch 블록이 비동기 에러를 포착하지 못하고, 초기화 실패 여부와 관계없이 provider가 설정됩니다(Line 22).
필요한 수정:
AnalyticsProvider인터페이스의init반환 타입을void에서Promise<void>로 변경amplitude.ts의init메서드를 비동기로 전환:async init(apiKey: string) { await initAll(...) }index.ts의 호출부에await추가:await amplitudeAdapter.init(apiKey)initAnalytics함수를 비동기로 변경:async initAnalytics(...): Promise<void>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/analytics/index.ts` around lines 21 - 23, The AnalyticsProvider init
signature should return Promise<void> (not void); update the interface
accordingly, make amplitude.ts's init method async and await the
`@amplitude/unified` initAll call (e.g., async init(apiKey: string) { await
initAll(...) }), then update index.ts to await amplitudeAdapter.init(apiKey) and
mark initAnalytics as async returning Promise<void> so the try/catch can catch
initialization errors before calling analytics.setProvider.
📌 Related Issues
📄 Tasks
fix amplitude loading issue
⭐ PR Point (To Reviewer)
📷 Screenshot
Summary by CodeRabbit
릴리스 노트
Bug Fixes
Refactor