[FEAT] 구글 소셜 로그인, accessToken 재발급 처리 및 온보딩 API 연동 - #152
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughGoogle OAuth 로그인과 콜백 토큰 교환을 추가하고, accessToken을 Zustand에 저장하도록 구성했습니다. 인증 가드와 axios 토큰 재발급·재시도를 연결했으며, 온보딩 완료 API, 오류 재시도 UI, 빌드 환경 변수도 반영했습니다. Changes인증 및 Google OAuth 흐름
API 토큰 재발급
온보딩 및 오류 처리
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)Google OAuth 및 콜백 처리sequenceDiagram
participant LoginContainer
participant GoogleOAuth
participant OauthCallbackContainer
participant useToken
participant useAuthStore
LoginContainer->>GoogleOAuth: OAuth URL로 이동
GoogleOAuth-->>OauthCallbackContainer: code 전달
OauthCallbackContainer->>useToken: code 토큰 교환
useToken-->>OauthCallbackContainer: accessToken과 사용자 정보 반환
OauthCallbackContainer->>useAuthStore: accessToken 저장
OauthCallbackContainer-->>OauthCallbackContainer: 캐시 갱신 및 라우팅
accessToken 재발급 및 요청 재시도sequenceDiagram
participant axios
participant useAuthStore
participant ReissueEndpoint
participant OriginalRequest
axios->>useAuthStore: accessToken 조회
axios->>OriginalRequest: Authorization 헤더로 요청
OriginalRequest-->>axios: 401 응답
axios->>ReissueEndpoint: 토큰 재발급 요청
ReissueEndpoint-->>axios: 새 accessToken 반환
axios->>useAuthStore: 새 토큰 저장
axios->>OriginalRequest: 원본 요청 재전송
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 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 |
Timo Performance ReportBundle Size — timo-web
Lighthouse — timo-web
Image Optimization — timo-web
측정 커밋: |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/timo-web/api/client/axios.ts`:
- Line 69: Update the axios interceptor’s expired-refresh-token redirect to use
the i18n navigation router’s replace("/login") API, consistent with
AuthGuardProvider and OauthCallbackContainer, instead of assigning
window.location.href. Reuse the existing router/navigation setup and preserve
the current login destination.
- Around line 63-67: Update the response flow around reissueAccessToken so that
an undefined or otherwise missing newAccessToken also redirects the user to
/login. Preserve the existing retry through instance(originalRequest) only when
a valid token is returned, and ensure every failed token reissue path completes
with the login redirect.
In
`@apps/timo-web/app/`[locale]/oauth/callback/_containers/OauthCallbackContainer.tsx:
- Around line 19-37: Update the OAuth callback effect in OauthCallbackContainer
so both missing code and successful responses without data.accessToken redirect
to /login via router.replace instead of leaving the page. Preserve the existing
token setup and onboarding/home redirects for valid successful responses, and
retain the mutation onError redirect.
- Line 29: Update the queryClient.setQueryData call in OauthCallbackContainer to
use the shared getGetMyProfileQueryKey() helper instead of the hard-coded
["user", "me"] key, ensuring it matches the key used by useGetMyProfile.
In `@apps/timo-web/app/`[locale]/oauth/callback/page.tsx:
- Around line 7-9: Add a loading fallback to the Suspense boundary wrapping
OauthCallbackContainer, such as the existing loading spinner component, so users
see feedback while the OAuth callback and token exchange are processing.
🪄 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: ASSERTIVE
Plan: Pro Plus
Run ID: 0651b6bd-2c65-4302-9ca2-03af6a3e55d1
📒 Files selected for processing (8)
apps/timo-web/api/client/axios.tsapps/timo-web/app/[locale]/(main)/layout.tsxapps/timo-web/app/[locale]/login/_containers/LoginContainer.tsxapps/timo-web/app/[locale]/oauth/callback/_containers/OauthCallbackContainer.tsxapps/timo-web/app/[locale]/oauth/callback/page.tsxapps/timo-web/app/[locale]/onboarding/_components/OnboardingGoogleButton.tsxapps/timo-web/providers/auth/AuthGuardProvider.tsxapps/timo-web/stores/auth/useAuthStore.ts
| return instance(originalRequest); | ||
| } | ||
| } catch { | ||
| window.location.href = "/login"; // refreshToken도 만료된 경우 |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
window.location.href = "/login"가 i18n 라우팅을 우회합니다.
AuthGuardProvider와 OauthCallbackContainer는 @/i18n/navigation의 router.replace("/login")을 사용하여 locale prefix가 자동으로 추가됩니다. 반면 axios 인터셉터는 window.location.href를 직접 사용하여 middleware가 처리해주길 기대하고 있습니다. 일관성 및 잉여 리다이렉트 방지를 위해 통일을 권장합니다.
참고: Next.js Internationalization
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/timo-web/api/client/axios.ts` at line 69, Update the axios interceptor’s
expired-refresh-token redirect to use the i18n navigation router’s
replace("/login") API, consistent with AuthGuardProvider and
OauthCallbackContainer, instead of assigning window.location.href. Reuse the
existing router/navigation setup and preserve the current login destination.
…to feat/web/147-google-login-token-refresh
- reissueData 전체를 받아 accessToken을 추출하도록 수정했습니다 - 로그인 리다이렉트 조건을 data 존재 여부 기준으로 수정했습니다
- w-101 고정 너비를 제거하고 좌우 px-12.5(50px) 패딩 기반으로 변경했습니다
- useCompleteOnboarding 훅을 연동하여 온보딩 완료 API를 호출했습니다 - 언어 선택값을 OnboardingRequestLanguage enum으로 매핑했습니다 - 완료 후 선택한 언어 locale로 홈 페이지에 리다이렉트했습니다
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@apps/timo-web/app/`[locale]/onboarding/_containers/OnboardingFunnelContainer.tsx:
- Line 41: Update OnboardingFunnelContainer’s useCompleteOnboarding usage to
also extract its isPending state, then pass that state through to
CalendarConnectStepContainer or connect it to the relevant submit button’s
disabled behavior. Ensure the button remains disabled while completeOnboarding
is in progress, preventing duplicate submissions.
- Around line 124-140: Update the completeOnboarding mutation options in
OnboardingFunnelContainer to add an onError handler alongside onSuccess. Ensure
API failures provide visible user feedback and leave the user able to retry or
continue from the CalendarConnect step instead of remaining stuck, while
preserving the existing successful redirect to ROUTES.HOME.
🪄 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: ASSERTIVE
Plan: Pro Plus
Run ID: 20ad7896-67c3-45ce-a71a-7e25c7b31637
📒 Files selected for processing (2)
apps/timo-web/app/[locale]/login/_containers/LoginContainer.tsxapps/timo-web/app/[locale]/onboarding/_containers/OnboardingFunnelContainer.tsx
- 프로젝트 공통 AsyncBoundary 컴포넌트로 교체했습니다
- useCompleteOnboarding에서 isPending을 추출했습니다 - CalendarConnectStepContainer에 isPending prop을 전달했습니다 - API 호출 중 시작 버튼을 비활성화하여 중복 제출을 방지했습니다
jjangminii
left a comment
There was a problem hiding this comment.
로그인 api 수고하셨습니다~~
간단한 코멘트 확인해주세요~~
| data: { | ||
| language: ONBOARDING_LANGUAGE_MAP[answers.language], | ||
| predictionAccuracy: answers.predictionAccuracy, | ||
| wakeUpTime: answers.wakeUpTime, | ||
| bedTime: answers.bedTime, | ||
| }, | ||
| }, | ||
| { | ||
| onSuccess: () => { | ||
| router.replace(ROUTES.HOME, { | ||
| locale: answers.language, | ||
| }); | ||
| }, | ||
| }, | ||
| ); |
There was a problem hiding this comment.
온보딩 API가 아직 검증되지 않은 상태라고 To Reviewers에 명시하셨는데, 정작 실패 핸들러가 없어요. 실패하면 사용자가 CalendarConnect 단계에서 아무 피드백 없이 멈춰요. 간단하게 .alert 토스트로 피드백 주는건 어떤가요?
There was a problem hiding this comment.
아주 좋은 의견입니다. 온보딩 API에 onError 추가하고 Home 넘어갈 때 errorFallback 추가하겠습니다.
There was a problem hiding this comment.
Next.js는 error.tsx를 통한 라우트 단위 에러 처리만 지원하고, 현재 구조에서는 Server Component → Client Component로 fallback 함수를 전달할 수 없어서 Home의 errorFallback 다시 제거했습니다 ~
- AuthGuardProvider로 감싸 비로그인 시 로그인 페이지로 리다이렉트했습니다
- completeOnboarding onError 핸들러를 추가했습니다 - API 실패 시 AnimatedToast로 에러 메시지를 표시했습니다 - ko/en 메시지 파일에 토스트 및 섹션 에러 키를 추가했습니다
- SectionErrorFallback 컴포넌트를 추가했습니다 - 홈 헤더 및 투두 섹션 AsyncBoundary에 errorFallback을 연결했습니다
- Server Component에서 Client Component로 함수 prop 전달 불가 제약으로 SectionErrorFallback을 제거했습니다 - AsyncBoundary에서 errorFallback을 제거하고 기본 Suspense 동작으로 되돌렸습니다
- AM/PM 표시를 제거하고 24시간 형식으로 단순화했습니다 - 드롭다운 목록에 scrollbar-sm 클래스를 적용했습니다 - 미사용 get-am-pm 유틸 파일을 삭제했습니다
- 취침 시간 유효성 검사를 <= 에서 === 로 수정해 같은 시간만 에러로 처리했습니다 - 취침 시간 에러 메시지를 검증 로직에 맞게 수정했습니다
ISSUE 🔗
close #147
What is this PR? 🔍
구글 OAuth 로그인 흐름을 구현했습니다. 로그인 버튼 클릭부터 콜백 처리, accessToken 저장, 회원/비회원 분기 라우팅, 토큰 만료 시 자동 재발급, 온보딩 완료 API 연동, 그리고 에러 처리까지 포함합니다.
배경
파일별 역할
stores/auth/useAuthStore.tsapi/client/axios.tsapp/[locale]/oauth/callback/providers/auth/AuthGuardProvider.tsxapp/[locale]/(main)/layout.tsxapp/[locale]/login/_containers/LoginContainer.tsxapp/[locale]/onboarding/page.tsxonboarding/_containers/OnboardingFunnelContainer.tsxonboarding/_containers/CalendarConnectStepContainer.tsxcomponents/boundary/SectionErrorFallback.tsxapp/[locale]/(main)/(with-time-sidebar)/home/page.tsx로그인 전체 흐름
useAuthStore
useAuthStore.getState()로 모듈 스코프에서도 접근 가능한 Zustand를 선택했습니다.AuthGuardProvider의 reissue 호출이 담당합니다.axios 인터셉터
reissuePromise변수에 진행 중인 Promise를 저장합니다. 동시에 401이 여러 번 발생해도 reissue는 한 번만 호출되고, 완료 후null로 초기화됩니다. 무한 재시도 방지를 위해retriedRequestsWeakSet에 이미 재시도한 요청을 기록합니다.isReissueRequest플래그).OauthCallbackContainer
/oauth/callback라우트에서 authorization code를 accessToken으로 교환하고 분기 라우팅을 처리합니다.useSearchParams로code를 읽어useTokenmutation을 호출합니다. 성공 시queryClient.setQueryData로 user 정보를 React Query 캐시에 직접 주입해 별도 조회 없이 이후 컴포넌트에서 사용 가능하게 합니다.hasRequestedref로 Strict Mode 이중 호출을 방지합니다. 프로젝트 공통AsyncBoundary로 교체했습니다.AuthGuardProvider
/login으로 이동합니다. reissue 완료 전에는null을 반환해 children을 렌더하지 않습니다.온보딩 완료 API 연동
useCompleteOnboarding훅을 CalendarConnect 단계 제출에 연동했습니다."ko"|"en")을OnboardingRequestLanguageenum으로 매핑 후 API 호출합니다. 완료 후 선택한 언어 locale로 홈 페이지에 리다이렉트합니다.isPending상태를 시작 버튼에 연결해 중복 제출을 방지하고, 실패 시AnimatedToast로 에러 메시지를 표시합니다.SectionErrorFallback
AsyncBoundary에 연결했습니다.reset콜백으로 ErrorBoundary를 초기화해 재시도를 지원합니다.To Reviewers
로그인/온보딩 완료 여부에 따른 라우트 접근 제어는 아직 구현되지 않았습니다. onboardingCompleted 상태를 어디에서 관리할지(store/localStorage 등)와 백엔드에서 해당 접근을 보장하는지 확인이 필요하여, 구현 방향을 추가로 논의하고 반영할 예정입니다.
Screenshot 📷
Test Checklist ✔
/oauth/callback콜백 처리 및 홈 분기 — 백엔드 연동 환경 필요