Skip to content

[feat] 소셜 로그인 OAuth 인증을 프론트엔드에서 직접 처리 - #248

Merged
haesol2022 merged 15 commits into
devfrom
feat/#244/social-login-refactor
Apr 17, 2026
Merged

[feat] 소셜 로그인 OAuth 인증을 프론트엔드에서 직접 처리#248
haesol2022 merged 15 commits into
devfrom
feat/#244/social-login-refactor

Conversation

@haesol2022

@haesol2022 haesol2022 commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

📌 Summary

소셜 로그인 방식을 백엔드 리다이렉트 방식에서 프론트엔드 토큰 직접 전달 방식으로 변경

  • 기존에는 백엔드(together-dallaem-api.vercel.app)가 구글/카카오 로그인 페이지로 리다이렉트하는 방식

  • 로그인 동의 화면에 "같이달램", "together-dallaem-api.vercel.app"이 표시되는 문제가 있었음

  • 서비스명인 "moum-zip"으로 표시하기 위해 카카오 디벨로퍼스와 구글 클라우드 콘솔에서 moum-zip 전용 앱을 새로 생성하고, 프론트에서 직접 OAuth 인증을 처리하는 방식으로 변경

  • close [feat] 소셜 로그인 OAuth 인증을 프론트엔드에서 직접 처리 #244

📄 Tasks

소셜 로그인 로직 교체

  • social-login-url.ts 삭제 -> social-login.ts로 교체
    • 기존: 백엔드 리다이렉트 URL만 반환
    • 변경: Google / Kakao access_token을 백엔드(POST /{teamId}/oauth/google, POST /{teamId}/oauth/kakao)로 전달하는 함수로 교체

Google 로그인

  • login-form.tsx, signup-form.tsx onClick 핸들러 교체
    • Google Identity Services SDK(initTokenClient)로 구글 팝업을 띄워 access_token 획득
    • 백엔드에서 JWT 발급 후 /api/auth/token으로 httpOnly 쿠키에 저장
  • layout.tsx에 Google Identity Services SDK 스크립트 추가
  • global.d.ts 추가: window.google 타입 선언
    • @types/google.accounts 패키지로 타입 자동 인식
    • @types/google.accounts에 없는 error, error_description 필드 추가

Kakao 로그인

  • login-form.tsx, signup-form.tsx onClick 핸들러 교체
    • 카카오 인가 코드 요청 URL로 리다이렉트
  • app/api/oauth/kakao/route.ts 신규 추가
    • 카카오 인가 코드 -> access_token 교환 처리 (client_secret 노출 방지를 위해 서버에서 처리)
  • oauth/callback/page.tsx -> oauth/callback/kakao/page.tsx로 이동 및 Kakao 전용으로 수정
    • 기존: 백엔드에서 발급한 accessToken, refreshToken을 URL 파라미터로 받아 저장
    • 변경: 카카오 인가 코드(code) 수신 -> Route Handler에서 access_token 교환 -> 백엔드 JWT 발급 -> /api/auth/token으로 쿠키 저장

기타

  • shared/config/env.ts: API_BASE_URL trailing slash 정규화 (replace 사용)

변경 없음

  • app/api/auth/token/route.ts: httpOnly 쿠키 저장 로직 그대로 재사용

👀 To Reviewer

  • Google 로그인은 팝업 방식이라 콜백 페이지가 없고 callback 함수 내에서 쿠키 저장까지 처리합니다!
  • 로컬에서 테스트 시에는 .env.local에 아래 환경변수 추가가 필요합니다.
    NEXT_PUBLIC_GOOGLE_CLIENT_ID
    NEXT_PUBLIC_KAKAO_CLIENT_ID
    KAKAO_CLIENT_SECRET
    NEXT_PUBLIC_KAKAO_REDIRECT_URI

📸 Screenshot

Summary by CodeRabbit

출시 노트

  • 새로운 기능

    • Google Identity Services를 통한 개선된 소셜 로그인 인증 흐름
    • Kakao OAuth 인증 코드 기반 로그인 프로세스 업데이트
  • Chores

    • 개발 환경용 TypeScript 타입 정의 패키지 추가

@haesol2022 haesol2022 self-assigned this Apr 16, 2026
@haesol2022
haesol2022 requested a review from a team as a code owner April 16, 2026 16:23
@haesol2022
haesol2022 requested review from blueiz920 and removed request for a team April 16, 2026 16:23
@haesol2022 haesol2022 added ✨ Feature 새로운 기능 추가 / 퍼블리싱 🛠️ Refactor 코드 리팩토링, QA 반영 labels Apr 16, 2026
@coderabbitai

coderabbitai Bot commented Apr 16, 2026

Copy link
Copy Markdown
📝 Walkthrough

요약

프론트엔드에서 소셜 로그인 OAuth 인증을 직접 처리하도록 변경되었습니다. 기존 백엔드 리다이렉트 URL 기반 방식에서 구글은 Identity Services로 액세스 토큰을 직접 획득하고, 카카오는 인가 코드 플로우로 서버 측 토큰 교환을 수행하는 방식으로 전환되었습니다.

변경 사항

코호트 / 파일(들) 요약
의존성 추가
apps/web/package.json
Google OAuth 타입 정의 패키지(@types/google-one-tap, @types/google.accounts) 추가
Google OAuth 구현
apps/web/src/global.d.ts, apps/web/src/app/layout.tsx, apps/web/src/_pages/auth/use-cases/social-login.ts
Google Identity Services 전역 타입 선언, SDK 스크립트 주입, 액세스 토큰 기반 로그인 함수 추가
Kakao OAuth 구현
apps/web/src/app/api/oauth/kakao/route.ts, apps/web/src/_pages/auth/use-cases/social-login.ts
인가 코드를 액세스 토큰으로 교환하는 API 엔드포인트 및 로그인 함수 추가
UI 핸들러 업데이트
apps/web/src/features/auth/ui/login-form.tsx, apps/web/src/features/auth/ui/signup-form.tsx
구글 Identity Services initTokenClient 적용, 카카오 인가 URL 리다이렉트 방식으로 변경
OAuth 콜백 페이지
apps/web/src/app/oauth/callback/kakao/page.tsx
카카오 전용 콜백으로 축소, 인가 코드 기반 플로우로 변경
삭제된 파일
apps/web/src/_pages/auth/use-cases/social-login-url.ts
기존 URL 생성 헬퍼 함수 제거 (getGoogleLoginUrl, getKakaoLoginUrl)

시퀀스 다이어그램

sequenceDiagram
    actor User
    participant Frontend as 프론트엔드
    participant GoogleSDK as Google<br/>Identity Services
    participant BackendAuth as 백엔드<br/>/auth/token
    participant BrowserStorage as 쿠키 저장소

    User->>Frontend: 구글 로그인 클릭
    Frontend->>GoogleSDK: initTokenClient 초기화
    GoogleSDK-->>Frontend: TokenClient 반환
    Frontend->>GoogleSDK: requestAccessToken()
    GoogleSDK->>User: 로그인 팝업 표시
    User->>GoogleSDK: 자격증명 제공
    GoogleSDK-->>Frontend: TokenResponse (access_token)
    Frontend->>BackendAuth: POST { accessToken, refreshToken }
    BackendAuth->>BackendAuth: 토큰 검증 및 처리
    BackendAuth-->>BrowserStorage: httpOnly 쿠키 설정
    BackendAuth-->>Frontend: 응답
    Frontend->>Frontend: 홈 페이지 이동
Loading
sequenceDiagram
    actor User
    participant Frontend as 프론트엔드
    participant KakaoAuth as 카카오<br/>인증 서버
    participant BackendKakao as 백엔드<br/>/api/oauth/kakao
    participant BackendAuth as 백엔드<br/>/auth/token
    participant BrowserStorage as 쿠키 저장소

    User->>Frontend: 카카오 로그인 클릭
    Frontend->>KakaoAuth: 인가 URL로 리다이렉트<br/>(response_type=code)
    KakaoAuth->>User: 로그인 페이지 표시
    User->>KakaoAuth: 자격증명 제공
    KakaoAuth-->>Frontend: 인가 코드와 함께 콜백
    Frontend->>BackendKakao: GET /api/oauth/kakao?code=...
    BackendKakao->>KakaoAuth: POST (code 교환)
    KakaoAuth-->>BackendKakao: access_token 응답
    BackendKakao-->>Frontend: { accessToken }
    Frontend->>BackendAuth: POST loginWithKakao(accessToken)
    BackendAuth->>BackendAuth: 토큰 검증 및 처리
    BackendAuth-->>BrowserStorage: httpOnly 쿠키 설정
    BackendAuth-->>Frontend: { accessToken, refreshToken }
    Frontend->>Frontend: 홈 페이지 이동
Loading

예상 코드 리뷰 소요시간

🎯 4 (복잡함) | ⏱️ ~50분

제안 검토자

  • miloupark
  • chan-byeong
  • Jeayeong-Hong

축하 시

🐰 OAuth의 흐름을 다시 짰네,
🔐 프론트엔드에서 토큰을 받아,
🚀 구글과 카카오 춤을 춘다!
🎭 팝업과 리다이렉트의 조화,
✨ 로그인이 더 우아해졌구나!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed 모든 주요 목표가 구현되었습니다: social-login-url.ts 삭제 및 social-login.ts 추가 [#244], Google Identity Services SDK 팝업 방식 구현 [#244], Kakao 인가 코드 방식 구현 [#244], api/oauth/kakao/route.ts 추가 [#244], oauth/callback/kakao/page.tsx로 분리 [#244], global.d.ts 타입 선언 [#244], layout.tsx에 Google SDK 스크립트 추가 [#244], @types/google-one-tap 패키지 추가 [#244].
Out of Scope Changes check ✅ Passed 모든 변경사항이 #244 이슈의 소셜 로그인 OAuth 인증 프론트엔드 직접 처리 기능 범위 내에 있습니다.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/app/oauth/callback/kakao/page.tsx (1)

56-58: 🧹 Nitpick | 🔵 Trivial

catch 블록에서 에러 로깅 누락

에러가 발생해도 로깅 없이 로그인 페이지로 리다이렉트됩니다. 디버깅을 위해 에러 로깅을 추가하는 것이 좋습니다.

♻️ 에러 로깅 추가
-      } catch {
+      } catch (error) {
+        console.error("Kakao login callback failed:", error);
         router.replace(ROUTES.login);
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/oauth/callback/kakao/page.tsx` around lines 56 - 58, catch
블록에서 발생한 예외를 로깅하지 않고 바로 router.replace(ROUTES.login)만 호출하고 있어 디버깅 정보가 사라집니다; 해당
catch 블록(try/catch around the Kakao OAuth callback handling, where
router.replace(ROUTES.login) is called)에서 캐치된 에러를 캡처해 적절한 로거나 console.error에
기록하도록 추가하고, 로그 메시지에 문맥(예: "Kakao OAuth callback error")과 에러 객체를 포함하도록 수정하세요.
🤖 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/web/package.json`:
- Around line 46-47: Remove the partial custom declaration for the
google.accounts.oauth2 namespace from global.d.ts to avoid duplicate/conflicting
types with the installed `@types/google.accounts` package; locate any "declare
namespace google.accounts.oauth2" (or related interface/type overrides) in
global.d.ts and delete those declarations so the project uses the official types
from `@types/google.accounts`, then run a TypeScript build/IDE reload to confirm
no missing types remain (if any missing APIs are actually used, add minimal
augmentation files that use module augmentation rather than redeclaring the
whole namespace).

In `@apps/web/src/_pages/auth/use-cases/social-login.ts`:
- Around line 15-16: When the fetch response check throws on (!res.ok) the error
lacks HTTP status and body details; update the failure path where the code
currently does "if (!res.ok) throw new Error('구글 로그인 실패')" to read the response
body (await res.text() or await res.json() safely) and include res.status and
the parsed error text/JSON in the thrown Error (or attach them to a custom Error
object) so the thrown error contains both HTTP status and server error message
for debugging.
- Line 9: Normalize API_BASE_URL to remove any trailing slashes before it’s used
so the fetch in social-login.ts (the expression using API_BASE_URL and TEAM_ID,
i.e., the fetch call to `${API_BASE_URL}/${TEAM_ID}/oauth/google`) and any
HttpClient URL concatenation never produce double slashes; implement this by
trimming trailing slashes from the API_BASE_URL value when it’s
initialized/derived (e.g., replace trailing / characters via a regex) and then
use that normalized constant in the fetch and HttpClient construction.

In `@apps/web/src/app/api/oauth/kakao/route.ts`:
- Around line 29-31: The code extracts access_token from tokenRes.json() but
doesn't validate it; update the block after const { access_token } = await
tokenRes.json() to check that access_token exists and is a non-empty string and,
if not, return a proper error response (e.g., NextResponse.json({ error:
'Missing access_token from Kakao' }, { status: 400 }) or throw a clear error)
instead of returning { accessToken: undefined }; reference tokenRes,
access_token, and NextResponse.json when making this change.
- Around line 18-21: The code uses non-null assertions for
NEXT_PUBLIC_KAKAO_CLIENT_ID, KAKAO_CLIENT_SECRET, and
NEXT_PUBLIC_KAKAO_REDIRECT_URI when building the token request, which can pass
undefined to the Kakao API; add an explicit runtime check at the start of the
route handler (the function that performs the token exchange in route.ts) to
validate process.env.NEXT_PUBLIC_KAKAO_CLIENT_ID,
process.env.KAKAO_CLIENT_SECRET, and process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI
are present, remove the non-null assertions, and if any are missing return a
clear 500/4xx response or throw an error with a descriptive message so missing
environment variables are detected immediately. Ensure the validation runs
before using those values in the token request payload or calling any Kakao API.

In `@apps/web/src/features/auth/ui/login-form.tsx`:
- Around line 122-124: The redirect URL assignment that sets
window.location.href uses process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI raw in the
template string which can break if it contains special characters; update the
construction where window.location.href is set (in the click handler inside
login-form.tsx) to pass process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI through
encodeURIComponent (you can leave NEXT_PUBLIC_KAKAO_CLIENT_ID as-is) so the
redirect_uri query parameter is URL-encoded and safe.
- Around line 100-116: The Google OAuth callback lacks error handling: wrap the
callback logic passed to window.google.accounts.oauth2.initTokenClient (the
async callback) in a try/catch and handle failures from loginWithGoogle and the
fetch to "/api/auth/token"; on error, stop the redirect to ROUTES.home, log the
error (or report to process logger), and surface user feedback (e.g., set an
error state or show a toast) so the user isn't left waiting after
client.requestAccessToken() fails. Ensure any thrown errors from
loginWithGoogle(response.access_token) and await fetch(...) are caught and
handled gracefully.

In `@apps/web/src/features/auth/ui/signup-form.tsx`:
- Around line 129-145: The Google OAuth onClick duplicates login-form.tsx and
lacks error handling; extract the flow into a reusable hook (e.g.,
useGoogleLogin returning handleGoogleLogin) that calls
window.google.accounts.oauth2.initTokenClient and client.requestAccessToken, and
inside the callback wrap logic in try/catch: check response.error, call
loginWithGoogle(response.access_token), POST the tokens and verify res.ok, throw
or set an error state on any failure, and only call
window.location.replace(ROUTES.home) on success; replace the inline onClick to
call handleGoogleLogin.
- Around line 150-153: The Kakao OAuth redirect_uri in the onClick handler
(window.location.href) of signup-form.tsx is not URL-encoded; update the
construction of the authorization URL used in the onClick (the anonymous click
handler in the signup form component) to wrap
process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI with encodeURIComponent so the
redirect_uri parameter is properly encoded (mirroring the fix applied in
login-form.tsx).

In `@apps/web/src/global.d.ts`:
- Around line 8-10: The TokenResponse interface in global.d.ts is incomplete;
extend the TokenResponse interface (interface TokenResponse) to include optional
OAuth error fields so error cases can be handled—add at minimum optional
properties like error?: string and error_description?: string (you may also add
other common fields such as expires_in?: number, token_type?: string,
refresh_token?: string) so callers checking TokenResponse can safely inspect
both success and error responses.
- Around line 1-27: Remove the custom duplicate ambient declarations for
google.accounts.oauth2 (TokenClient, TokenResponse, initTokenClient and the
Window.google augmentation) from apps/web/src/global.d.ts and rely on the
installed `@types/google.accounts` package instead; delete the redundant
interfaces and initTokenClient declaration (or remove the entire file if it only
contains these declarations), ensure tsconfig includes the package types (remove
any manual triple-slash or conflicting declarations), and keep references to
TokenClient/TokenResponse/initTokenClient in code to use the official types
provided by the package.

---

Outside diff comments:
In `@apps/web/src/app/oauth/callback/kakao/page.tsx`:
- Around line 56-58: catch 블록에서 발생한 예외를 로깅하지 않고 바로 router.replace(ROUTES.login)만
호출하고 있어 디버깅 정보가 사라집니다; 해당 catch 블록(try/catch around the Kakao OAuth callback
handling, where router.replace(ROUTES.login) is called)에서 캐치된 에러를 캡처해 적절한 로거나
console.error에 기록하도록 추가하고, 로그 메시지에 문맥(예: "Kakao OAuth callback error")과 에러 객체를
포함하도록 수정하세요.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c3d3f875-9107-4587-91d3-cab6d0d91ae5

📥 Commits

Reviewing files that changed from the base of the PR and between 7071bf2 and 4e92a14.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • apps/web/package.json
  • apps/web/src/_pages/auth/use-cases/social-login-url.ts
  • apps/web/src/_pages/auth/use-cases/social-login.ts
  • apps/web/src/app/api/oauth/kakao/route.ts
  • apps/web/src/app/layout.tsx
  • apps/web/src/app/oauth/callback/kakao/page.tsx
  • apps/web/src/features/auth/ui/login-form.tsx
  • apps/web/src/features/auth/ui/signup-form.tsx
  • apps/web/src/global.d.ts
💤 Files with no reviewable changes (1)
  • apps/web/src/_pages/auth/use-cases/social-login-url.ts

Comment thread apps/web/package.json
Comment thread apps/web/src/_pages/auth/use-cases/social-login.ts
Comment on lines +15 to +16
if (!res.ok) throw new Error("구글 로그인 실패");
return res.json() as Promise<{ accessToken: string; refreshToken: string }>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

에러 응답 디버깅 정보 부족

res.ok가 false일 때 응답 상태 코드나 에러 메시지를 포함하지 않아 디버깅이 어려울 수 있습니다.

♻️ 에러 정보 포함 제안
-  if (!res.ok) throw new Error("구글 로그인 실패");
+  if (!res.ok) {
+    const errorBody = await res.text().catch(() => "");
+    throw new Error(`구글 로그인 실패: ${res.status} ${errorBody}`);
+  }
📝 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.

Suggested change
if (!res.ok) throw new Error("구글 로그인 실패");
return res.json() as Promise<{ accessToken: string; refreshToken: string }>;
if (!res.ok) {
const errorBody = await res.text().catch(() => "");
throw new Error(`구글 로그인 실패: ${res.status} ${errorBody}`);
}
return res.json() as Promise<{ accessToken: string; refreshToken: string }>;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/_pages/auth/use-cases/social-login.ts` around lines 15 - 16,
When the fetch response check throws on (!res.ok) the error lacks HTTP status
and body details; update the failure path where the code currently does "if
(!res.ok) throw new Error('구글 로그인 실패')" to read the response body (await
res.text() or await res.json() safely) and include res.status and the parsed
error text/JSON in the thrown Error (or attach them to a custom Error object) so
the thrown error contains both HTTP status and server error message for
debugging.

Comment on lines +18 to +21
client_id: process.env.NEXT_PUBLIC_KAKAO_CLIENT_ID!,
client_secret: process.env.KAKAO_CLIENT_SECRET!,
code,
redirect_uri: process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI!,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

환경변수 미설정 시 런타임 오류 발생 가능

process.env.NEXT_PUBLIC_KAKAO_CLIENT_ID!, process.env.KAKAO_CLIENT_SECRET! 등에 non-null assertion을 사용하고 있으나, 환경변수가 설정되지 않은 경우 undefined가 그대로 전달되어 카카오 API 호출이 실패합니다. 서버 시작 시 또는 요청 처리 전에 검증이 필요합니다.

🛡️ 환경변수 검증 추가 제안
 export async function GET(request: NextRequest) {
+  const clientId = process.env.NEXT_PUBLIC_KAKAO_CLIENT_ID;
+  const clientSecret = process.env.KAKAO_CLIENT_SECRET;
+  const redirectUri = process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI;
+
+  if (!clientId || !clientSecret || !redirectUri) {
+    console.error("Missing Kakao OAuth environment variables");
+    return NextResponse.json({ message: "서버 설정 오류" }, { status: 500 });
+  }
+
   const { searchParams } = new URL(request.url);
   const code = searchParams.get("code");
   
   // ... 이하 동일
   
   body: new URLSearchParams({
     grant_type: "authorization_code",
-    client_id: process.env.NEXT_PUBLIC_KAKAO_CLIENT_ID!,
-    client_secret: process.env.KAKAO_CLIENT_SECRET!,
+    client_id: clientId,
+    client_secret: clientSecret,
     code,
-    redirect_uri: process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI!,
+    redirect_uri: redirectUri,
   }),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/api/oauth/kakao/route.ts` around lines 18 - 21, The code
uses non-null assertions for NEXT_PUBLIC_KAKAO_CLIENT_ID, KAKAO_CLIENT_SECRET,
and NEXT_PUBLIC_KAKAO_REDIRECT_URI when building the token request, which can
pass undefined to the Kakao API; add an explicit runtime check at the start of
the route handler (the function that performs the token exchange in route.ts) to
validate process.env.NEXT_PUBLIC_KAKAO_CLIENT_ID,
process.env.KAKAO_CLIENT_SECRET, and process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI
are present, remove the non-null assertions, and if any are missing return a
clear 500/4xx response or throw an error with a descriptive message so missing
environment variables are detected immediately. Ensure the validation runs
before using those values in the token request payload or calling any Kakao API.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

배포 환경에서 환경변수가 항상 설정되어 있어서 별도로 런타임 검증하는 건 불필요하다고 판단함.

Comment thread apps/web/src/app/api/oauth/kakao/route.ts
Comment thread apps/web/src/features/auth/ui/login-form.tsx Outdated
Comment on lines 129 to 145
onClick={() => {
window.location.href = getGoogleLoginUrl();
// 구글 팝업으로 access_token 획득 후 백엔드로 전달
const client = window.google.accounts.oauth2.initTokenClient({
client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID!,
scope: "email profile",
callback: async (response) => {
const { accessToken, refreshToken } = await loginWithGoogle(response.access_token);
await fetch("/api/auth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ accessToken, refreshToken }),
});
window.location.replace(ROUTES.home);
},
});
client.requestAccessToken();
}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

login-form.tsx와 동일한 에러 처리 누락 및 코드 중복

Google OAuth 콜백에 에러 처리가 없으며, login-form.tsx와 완전히 동일한 코드가 중복되어 있습니다.

♻️ 공통 훅 또는 유틸리티로 추출 제안

소셜 로그인 로직을 커스텀 훅으로 추출하여 재사용하세요:

// hooks/use-google-login.ts
export function useGoogleLogin() {
  const handleGoogleLogin = useCallback(() => {
    const client = window.google.accounts.oauth2.initTokenClient({
      client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID!,
      scope: "email profile",
      callback: async (response) => {
        try {
          if (response.error) throw new Error(response.error);
          const { accessToken, refreshToken } = await loginWithGoogle(response.access_token);
          const res = await fetch("/api/auth/token", { /* ... */ });
          if (!res.ok) throw new Error("Token storage failed");
          window.location.replace(ROUTES.home);
        } catch (error) {
          console.error("Google login failed:", error);
          // 에러 상태 업데이트
        }
      },
    });
    client.requestAccessToken();
  }, []);
  
  return { handleGoogleLogin };
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/features/auth/ui/signup-form.tsx` around lines 129 - 145, The
Google OAuth onClick duplicates login-form.tsx and lacks error handling; extract
the flow into a reusable hook (e.g., useGoogleLogin returning handleGoogleLogin)
that calls window.google.accounts.oauth2.initTokenClient and
client.requestAccessToken, and inside the callback wrap logic in try/catch:
check response.error, call loginWithGoogle(response.access_token), POST the
tokens and verify res.ok, throw or set an error state on any failure, and only
call window.location.replace(ROUTES.home) on success; replace the inline onClick
to call handleGoogleLogin.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

에러 처리는 try/catch + toast로 이미 반영함. 공통 훅 추출은 login과 signup의 토스트 메시지와 리다이렉트 경로가 달라서 훅으로 묶으면 오히려 복잡해질 것 같아 그대로 유지하겠음.

Comment thread apps/web/src/features/auth/ui/signup-form.tsx
Comment thread apps/web/src/global.d.ts Outdated
Comment thread apps/web/src/global.d.ts Outdated
@haesol2022
haesol2022 merged commit 45e4047 into dev Apr 17, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 새로운 기능 추가 / 퍼블리싱 🛠️ Refactor 코드 리팩토링, QA 반영

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 소셜 로그인 OAuth 인증을 프론트엔드에서 직접 처리

1 participant