[feat] 소셜 로그인 API 연동 - #15
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
Warning
|
| Cohort / File(s) | Summary |
|---|---|
App Routing & Callback Page src/app/App.tsx, src/pages/oauth/KakaoCallbackPage.tsx |
Registers /oauth/kakao/callback route and implements KakaoCallbackPage which reads query params, reports errors, posts authorizationCode to opener if present, or calls loginSocial directly. |
Login UI & Popup Handling src/features/auth/ui/KakaoLoginButton.tsx, src/pages/login/index.tsx |
Kakao login now includes authorizationCode and redirectUri; login page listens for popup postMessage (type alter-kakao-oauth) and invokes loginSocial, handling B011 specially. |
Social Signup UI & Hook src/pages/signup/components/Step2AccountInfo.tsx, src/pages/signup/hooks/useSignupForm.ts, src/pages/signup/index.tsx |
Introduces isSocialSignup flag from socialLoginData in router state. Hides password/email flows for social signup; useSignupForm accepts options, refreshes Firebase ID token, may call signupSocial, caches signupSessionId, and returns isSocialSignup. |
Auth API & Social Signup Endpoint src/shared/api/auth.ts |
Extends SocialLoginRequest with redirectUri, adds SignupSocialRequest and signupSocial which sets auth and redirects based on scope; updates login/signup redirect targets and error mappings. |
Kakao OAuth Utilities src/shared/lib/socialLogin.ts |
Adds getKakaoOAuthRedirectUri, KAKAO_OAUTH_MESSAGE_TYPE, decodeKakaoOauthState, requestFreshKakaoAuthorizationCode; updates loginWithKakao to include authorizationCode and force web flow semantics. |
HTTP / Firebase Helpers src/shared/lib/axiosInstance.ts, src/shared/lib/firebase.ts |
Axios interceptor skips Authorization for signup/login allowlist. Adds getFreshFirebaseIdToken() to refresh Firebase ID token before use. |
Sequence Diagram(s)
sequenceDiagram
participant User as User (Browser)
participant LoginPage as Login Page
participant KakaoSDK as Kakao SDK / Popup
participant CallbackPopup as Callback Popup
participant Server as Backend
participant Firebase as Firebase
User->>LoginPage: Click "Login with Kakao"
LoginPage->>KakaoSDK: loginWithKakao() / open popup
KakaoSDK->>CallbackPopup: OAuth consent & redirect (code)
CallbackPopup->>LoginPage: postMessage(authorizationCode) OR redirect to /oauth/kakao/callback
LoginPage->>Server: loginSocial(provider:'KAKAO', authorizationCode, redirectUri)
Server->>Server: Exchange code for tokens / create session
Server-->>LoginPage: LoginResponse (accessToken, scope)
LoginPage->>Firebase: update auth context / setAuth
LoginPage-->>User: redirect based on scope
sequenceDiagram
participant User as User (Browser)
participant SignupPage as Signup Page
participant useSignupForm as useSignupForm Hook
participant Firebase as Firebase
participant Server as Backend
User->>SignupPage: Submit step2 (social signup)
SignupPage->>useSignupForm: submit with socialLoginData
useSignupForm->>Firebase: getFreshFirebaseIdToken()
Firebase-->>useSignupForm: refreshed idToken
useSignupForm->>Server: signupSocial(provider:'KAKAO', authorizationCode, redirectUri, profile)
Server->>Server: Create user / exchange code
Server-->>useSignupForm: LoginResponse
useSignupForm->>SignupPage: setAuth() + navigate (based on scope)
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~50 minutes
Suggested reviewers
- limtjdghks
Poem
🐰 A tiny rabbit hops to say hello,
OAuth codes in pockets, ready to go,
Popups whisper secrets through the night,
Tokens refreshed beneath the moonlight,
Social signups bounding toward the show 🥕✨
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |
✅ Passed checks (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title check | ✅ Passed | The title '[feat] 소셜 로그인 API 연동' clearly and concisely describes the main feature—social login API integration—which is accurately reflected across all file changes in the changeset. |
| Description check | ✅ Passed | The description comprehensively covers all required template sections: ID (ALT-177), 변경 내용 (changes made), 구현 사항 (implementation details), and 구현 시연 (demo video), with clear explanations of the social login/signup flow, callback handling, and error handling improvements. |
| Linked Issues check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
| Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing Touches
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Commit unit tests in branch
feat/ALT-177
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.
Review rate limit: 0/1 reviews remaining, refill in 50 minutes and 52 seconds.Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (4)
src/pages/signup/index.tsx (1)
13-16:errorCodeappears unused.The
errorCodeproperty is received from the B011 redirect inloginSocialbut isn't used in this component. If it's meant for debugging or future use, consider adding a comment; otherwise, it can be removed from the type.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/signup/index.tsx` around lines 13 - 16, The SignupLocationState type currently declares errorCode?: string but that property is never used in the signup page; either remove errorCode from the SignupLocationState definition to keep the type minimal, or if you intend to keep it for future debugging, add a short comment above SignupLocationState explaining its planned use and why it’s kept, and ensure any code reading loginSocial redirect data assigns to socialLoginData (type SocialLoginRequest) only; update or remove references accordingly (look for the type name SignupLocationState and usages in this file).src/shared/lib/axiosInstance.ts (1)
22-31: Consider using a Set for cleaner path matching.The allowlist approach is correct. For easier maintenance as more endpoints are added, consider using a Set:
♻️ Optional refactor using Set
+const ANONYMOUS_ENDPOINTS = new Set([ + '/public/users/signup-session', + '/public/users/signup', + '/public/users/signup-social', + '/public/users/login', + '/public/users/login-social', +]) + function shouldOmitBearerAuth(url: string): boolean { const path = url.split('?')[0] ?? '' - return ( - path === '/public/users/signup-session' || - path === '/public/users/signup' || - path === '/public/users/signup-social' || - path === '/public/users/login' || - path === '/public/users/login-social' - ) + return ANONYMOUS_ENDPOINTS.has(path) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/lib/axiosInstance.ts` around lines 22 - 31, The current shouldOmitBearerAuth function hardcodes an allowlist via multiple equality checks; refactor it to use a Set for cleaner, more maintainable path matching by creating a constant Set of the public paths (e.g., '/public/users/signup-session', '/public/users/signup', '/public/users/signup-social', '/public/users/login', '/public/users/login-social') and replace the chained || comparisons with a single Set.has(path) check inside shouldOmitBearerAuth to keep behavior identical but make additions/removals simpler.src/shared/api/auth.ts (1)
322-365: Consider extracting common auth response handling.The scope transformation,
setAuthcall, and navigation logic are duplicated acrosssignupSocial,signup, andloginSocial. This works correctly but could be consolidated into a shared helper.♻️ Example helper extraction
function handleAuthSuccess( data: GenerateTokenResponseDto, setAuth: (data: LoginResponse) => void, navigate: NavigateFunction ): LoginResponse { const scope = data.scope === 'APP' ? 'USER' : (data.scope as 'MANAGER' | 'USER') const response: LoginResponse = { token: data.accessToken, refreshToken: data.refreshToken, scope, } setAuth(response) navigate(scope === 'MANAGER' ? '/main' : '/user/job-lookup-map', { replace: true }) return response }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/api/auth.ts` around lines 322 - 365, Extract the duplicated scope transformation, setAuth call, and navigation logic from signupSocial (and also apply to signup and loginSocial) into a shared helper (e.g., handleAuthSuccess) that accepts a GenerateTokenResponseDto, setAuth, and NavigateFunction and returns a LoginResponse; replace the inline logic in signupSocial with a call to that helper so it computes scope (mapping 'APP' -> 'USER' else 'MANAGER'|'USER'), builds the LoginResponse { token, refreshToken, scope }, invokes setAuth(response), performs navigate(scope === 'MANAGER' ? '/main' : '/user/job-lookup-map', { replace: true }), and returns the response.src/pages/login/index.tsx (1)
31-78: LGTM with a minor consideration.The postMessage handler correctly validates origin, message type, and payload before triggering
loginSocial. The error handling for B011 (silent return since navigation already occurred) is consistent withKakaoLoginButton.One minor consideration: if the component unmounts while
loginSocialis in flight, the alert on line 71 could still fire. This is unlikely in practice since successful login navigates away, but if you want to be defensive:♻️ Optional: Add unmount guard
useEffect(() => { + let isMounted = true + function handleMessage(event: MessageEvent) { // ... validation ... void (async () => { try { await loginSocial(/* ... */) } catch (error: unknown) { const apiError = error as { data?: { code?: string }; message?: string } if (apiError?.data?.code === 'B011') return - alert(apiError.message || '카카오 로그인에 실패했습니다.') + if (isMounted) { + alert(apiError.message || '카카오 로그인에 실패했습니다.') + } } })() } window.addEventListener('message', handleMessage) - return () => window.removeEventListener('message', handleMessage) + return () => { + isMounted = false + window.removeEventListener('message', handleMessage) + } }, [navigate, setAuth])🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/login/index.tsx` around lines 31 - 78, Add an unmount guard inside the useEffect handling messages so that if the component unmounts while the async loginSocial is in flight you don't call alert or update state/navigation: create a local let isMounted = true at the top of useEffect, set isMounted = false in the cleanup, then inside the async IIFE check isMounted before calling setAuth, navigate, or showing alert (and return early if not mounted); reference the existing handleMessage, loginSocial, setAuth, and navigate identifiers when adding these checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/pages/oauth/KakaoCallbackPage.tsx`:
- Around line 50-57: The postMessage targetOrigin is currently derived from
window.location.origin which fails when the OAuth redirect URI differs; update
the Kakao OAuth flow to carry the opener's origin through the request (e.g.
include it in the OAuth state you create before redirect and parse it in the
callback) and use that parsed opener origin as the second argument to
window.opener.postMessage instead of window.location.origin; locate the logic
around getKakaoOAuthRedirectUri(), the code that builds the OAuth state, and the
callback handler that reads code and state (the block checking window.opener and
calling window.opener.postMessage) to store and read the opener origin and pass
it as targetOrigin in postMessage.
In `@src/pages/signup/hooks/useSignupForm.ts`:
- Around line 164-180: The signup flow normalizes the phone into contact but
still calls createSignupSession(phone) with the raw input, causing session
identity to vary by formatting; change the call(s) to createSignupSession to
pass the normalized contact (from normalizePhone and stored in
signupSessionCacheRef.current) instead of phone, and update the other similar
call site in this file (the createSignupSession invocation later in the hook) so
all session creation and cache keys consistently use contact.
In `@src/shared/lib/socialLogin.ts`:
- Around line 87-91: The current OAuth popup flow in socialLogin.ts (the code
building URLSearchParams with client_id/redirect_uri/response_type and the popup
message listener) is vulnerable to cross-window resolution; generate a
per-request nonce/state, add it to the authorize URL params, and when listening
for the popup message require both event.source === popup and that the returned
state matches the nonce before resolving the promise; update the
KakaoCallbackPage to postMessage back the same echoed state so the check can
succeed and apply the same state-checking change to the other popup flow
handling around the 106-120 region.
---
Nitpick comments:
In `@src/pages/login/index.tsx`:
- Around line 31-78: Add an unmount guard inside the useEffect handling messages
so that if the component unmounts while the async loginSocial is in flight you
don't call alert or update state/navigation: create a local let isMounted = true
at the top of useEffect, set isMounted = false in the cleanup, then inside the
async IIFE check isMounted before calling setAuth, navigate, or showing alert
(and return early if not mounted); reference the existing handleMessage,
loginSocial, setAuth, and navigate identifiers when adding these checks.
In `@src/pages/signup/index.tsx`:
- Around line 13-16: The SignupLocationState type currently declares errorCode?:
string but that property is never used in the signup page; either remove
errorCode from the SignupLocationState definition to keep the type minimal, or
if you intend to keep it for future debugging, add a short comment above
SignupLocationState explaining its planned use and why it’s kept, and ensure any
code reading loginSocial redirect data assigns to socialLoginData (type
SocialLoginRequest) only; update or remove references accordingly (look for the
type name SignupLocationState and usages in this file).
In `@src/shared/api/auth.ts`:
- Around line 322-365: Extract the duplicated scope transformation, setAuth
call, and navigation logic from signupSocial (and also apply to signup and
loginSocial) into a shared helper (e.g., handleAuthSuccess) that accepts a
GenerateTokenResponseDto, setAuth, and NavigateFunction and returns a
LoginResponse; replace the inline logic in signupSocial with a call to that
helper so it computes scope (mapping 'APP' -> 'USER' else 'MANAGER'|'USER'),
builds the LoginResponse { token, refreshToken, scope }, invokes
setAuth(response), performs navigate(scope === 'MANAGER' ? '/main' :
'/user/job-lookup-map', { replace: true }), and returns the response.
In `@src/shared/lib/axiosInstance.ts`:
- Around line 22-31: The current shouldOmitBearerAuth function hardcodes an
allowlist via multiple equality checks; refactor it to use a Set for cleaner,
more maintainable path matching by creating a constant Set of the public paths
(e.g., '/public/users/signup-session', '/public/users/signup',
'/public/users/signup-social', '/public/users/login',
'/public/users/login-social') and replace the chained || comparisons with a
single Set.has(path) check inside shouldOmitBearerAuth to keep behavior
identical but make additions/removals simpler.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: ca637d1a-8eb4-4313-bf3d-13a454645264
📒 Files selected for processing (11)
src/app/App.tsxsrc/features/auth/ui/KakaoLoginButton.tsxsrc/pages/login/index.tsxsrc/pages/oauth/KakaoCallbackPage.tsxsrc/pages/signup/components/Step2AccountInfo.tsxsrc/pages/signup/hooks/useSignupForm.tssrc/pages/signup/index.tsxsrc/shared/api/auth.tssrc/shared/lib/axiosInstance.tssrc/shared/lib/firebase.tssrc/shared/lib/socialLogin.ts
| if (scope === 'MANAGER') { | ||
| navigate('/main', { replace: true }) |
There was a problem hiding this comment.
사장님 메인페이지 url이 다릅니다 /manager/home으로 수정해주세요
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/pages/oauth/KakaoCallbackPage.tsx`:
- Around line 58-65: Replace the hardcoded message type string in the
window.opener.postMessage call with the imported KAKAO_OAUTH_MESSAGE_TYPE
constant: update the object passed to window.opener.postMessage (in the
KakaoCallbackPage component where window.opener.postMessage is called) to set
type: KAKAO_OAUTH_MESSAGE_TYPE while keeping authorizationCode: code, state and
targetOrigin unchanged so message correlation remains resilient to future
constant changes.
In `@src/shared/api/auth.ts`:
- Around line 70-84: SignupSocialRequest is missing the optional redirectUri
needed for Kakao WEB code exchange; add redirectUri?: string to the
SignupSocialRequest interface (matching the field and comment style used in
SocialLoginRequest) and update the form flow to pass that value from
useSignupForm (ensure useSignupForm supplies the same redirectUri used during
the authorize request when provider === 'KAKAO' && platformType === 'WEB') so
the backend can perform the code exchange correctly.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: f2dadf38-9f2d-48a9-9f76-5cabd34c9f40
📒 Files selected for processing (8)
src/app/App.tsxsrc/features/auth/ui/KakaoLoginButton.tsxsrc/pages/login/index.tsxsrc/pages/oauth/KakaoCallbackPage.tsxsrc/pages/signup/components/Step2AccountInfo.tsxsrc/pages/signup/hooks/useSignupForm.tssrc/shared/api/auth.tssrc/shared/lib/socialLogin.ts
✅ Files skipped from review due to trivial changes (1)
- src/app/App.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
- src/features/auth/ui/KakaoLoginButton.tsx
- src/pages/login/index.tsx
- src/pages/signup/components/Step2AccountInfo.tsx
| window.opener.postMessage( | ||
| { | ||
| type: 'alter-kakao-oauth', | ||
| authorizationCode: code, | ||
| state, | ||
| }, | ||
| targetOrigin | ||
| ) |
There was a problem hiding this comment.
Use the imported KAKAO_OAUTH_MESSAGE_TYPE constant.
Line 60 hardcodes 'alter-kakao-oauth' instead of using the imported constant. If the constant value changes, this would break the message correlation.
Proposed fix
+import {
+ decodeKakaoOauthState,
+ getKakaoOAuthRedirectUri,
+ KAKAO_OAUTH_MESSAGE_TYPE,
+} from '@/shared/lib/socialLogin'
...
window.opener.postMessage(
{
- type: 'alter-kakao-oauth',
+ type: KAKAO_OAUTH_MESSAGE_TYPE,
authorizationCode: code,
state,
},
targetOrigin
)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| window.opener.postMessage( | |
| { | |
| type: 'alter-kakao-oauth', | |
| authorizationCode: code, | |
| state, | |
| }, | |
| targetOrigin | |
| ) | |
| window.opener.postMessage( | |
| { | |
| type: KAKAO_OAUTH_MESSAGE_TYPE, | |
| authorizationCode: code, | |
| state, | |
| }, | |
| targetOrigin | |
| ) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/oauth/KakaoCallbackPage.tsx` around lines 58 - 65, Replace the
hardcoded message type string in the window.opener.postMessage call with the
imported KAKAO_OAUTH_MESSAGE_TYPE constant: update the object passed to
window.opener.postMessage (in the KakaoCallbackPage component where
window.opener.postMessage is called) to set type: KAKAO_OAUTH_MESSAGE_TYPE while
keeping authorizationCode: code, state and targetOrigin unchanged so message
correlation remains resilient to future constant changes.
ID
변경 내용
구현 사항
카카오 콜백 처리 페이지 추가
소셜 회원가입 API 연동 추가
회원가입 훅 분기 로직 개선
인가 코드 만료(A010) 대응
구현 시연 (필요 시)
2026-04-29.3.09.33.mov
Summary by CodeRabbit
New Features
Bug Fixes / UX