팀 프로젝트 중 백엔드 구현 이전 단계에서, Google OAuth 로그인을 프론트에서 먼저 구현해두었습니다.
기존에 구현했던 Kakao OAuth 로그인 구조를 확장하는 형태로 Google 로그인을 자연스럽게 통합했습니다.
✅ 이 글은 프론트엔드 단에서 미리 작업해둔 Google OAuth 구현 과정, 코드 설계, 에러 해결 기록 등을 정리한 것입니다.
프로젝트 구조: Next.js (App Router), Zustand, TanStack Query, TailwindCSS
상태 관리: useAuthStore로 로그인 상태 유지
요청 관리: loginWithOauthCode() 함수를 통한 OAuth 로그인 요청
OAuth 구현 방식: Web Server Applications 방식 (response_type=code)
공통된 구조(provider, code)를 유지하면서 Google도 같은 구조로 통합
버튼 UI는 Google의 공식 Material 가이드를 참고
SVG 아이콘은 컴포넌트화하여 재사용 가능하도록 개선
// api/oauth/google.ts
const GOOGLE_CLIENT_ID = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID!;
const REDIRECT_URI = `${process.env.NEXT_PUBLIC_REDIRECT_URI}/auth/google/callback`;
const SCOPE = "openid email profile";
export const getGoogleAuthURL = () => {
const baseUrl = "https://accounts.google.com/o/oauth2/v2/auth";
const config: Record<string, string> = {
client_id: GOOGLE_CLIENT_ID,
redirect_uri: REDIRECT_URI,
response_type: "code",
scope: SCOPE,
access_type: "offline",
prompt: "consent",
};
return `${baseUrl}?${new URLSearchParams(config).toString()}`;
};
📘 설명:
Google OAuth 로그인 창으로 리디렉션할 URL을 생성
scope는 openid email profile로 기본적인 사용자 정보를 요청
Web Server Application 방식이므로 code를 받아 백엔드에서 토큰 교환 필요
// api/oauth/loginWithOauthCode.ts
export const loginWithOauthCode = async (
provider: "kakao" | "google",
code: string
) => {
const url = `/auth/${provider}`;
const payload = { code };
return await requestHandler("post", url, payload);
};
📘 설명:
Kakao, Google 등 provider 이름과 code만 전달받아 백엔드로 POST 요청
이후 백엔드에서 access_token, id_token 등을 받아 처리할 예정
기존
"kakao"만 받던 부분을"kakao" | "google"로 확장했습니다.
export const useOauthLogin = () => {
const { login } = useAuthStore();
return useMutation({
mutationFn: ({ provider, code }: { provider: "kakao" | "google"; code: string }) =>
loginWithOauthCode(provider, code),
onSuccess: () => {
login();
// TODO: 추후 user 정보, accessToken 저장 등 확장
},
});
};
📘 설명:
react-query의 useMutation을 활용해 로그인 요청을 전송
로그인 성공 시 zustand를 사용해 로그인 상태를 true로 변경
import GoogleLogo from "@/assets/icons/google.svg";
export default function LoginPage() {
return (
<div className="flex flex-col items-center justify-center h-screen gap-4">
<button onClick={handleKakaoLogin}>
<Image src={kakao} alt="카카오 로그인" />
</button>
<button onClick={handleGoogleLogin} className="...">
<GoogleLogo className="w-5 h-5 mr-3" />
Google 계정으로 로그인
</button>
</div>
);
}
📘 설명:
Kakao 로그인은 이미지 버튼 형태로, Google은 SVG + 텍스트로 구현
Google 로고는 SVG 파일을 @svgr/webpack 설정으로 컴포넌트화하여 import
Google 로고를 JSX에 직접 작성하지 않고, 컴포넌트 형태로 import하기 위해 @svgr/webpack 설정을 추가했습니다.
npm install --save-dev webpack @svgr/webpack
// next.config.ts
import type { Configuration } from "webpack";
const nextConfig = {
webpack(config: Configuration) {
config.module?.rules?.push({
test: /\.svg$/,
issuer: /\.(js|ts)x?$/,
use: ["@svgr/webpack"],
});
return config;
},
};
export default nextConfig;
| 파일 | 설명 |
|---|---|
api/oauth/google.ts | Google OAuth 인증 URL 생성 |
api/oauth/loginWithOauthCode.ts | 공통 로그인 요청 처리 (Kakao/Google) |
hooks/useOauthLogin.ts | 로그인 mutation 처리 |
components/icons/google.svg | Google 로고 컴포넌트 |
app/login/page.tsx | 로그인 페이지 UI |