Next.js + FastAPI로 Google 로그인 구현하기 (JWT 검증 포함)

이번 글에서는 Google Auth를 사용해 사용자 로그인 기능을 구현하고, 로그인 후 발급받은 AccessToken을 백엔드(FastAPI)에서 검증 및 사용자 인증의 흐름을 확인할 수 있습니다.
소셜 로그인을 적용하면 사용자는 별도의 회원가입 없이 Google 계정만으로 간편하게 로그인할 수 있습니다. 또한 NextAuth를 이용하면 보안성과 확장성을 모두 확보할 수 있습니다.

📚 목차

  1. 프로젝트 구조
  2. Google Cloud Console 설정
  3. NextAuth 설정하기
  4. FastAPI에서 사용자 등록 및 검증 API 구현
  5. JWT를 이용한 FastAPI 인증 검증
  6. 프론트엔드에서 FastAPI 호출하기
  7. 참고

📂 프로젝트 구조

PORTFOLIO-APP/
├── backend/
│   ├── models/
│   ├── routes/
│   │   └── user.py
│   ├── schemas/
│   ├── db.py
│   ├── main.py
│   ├── auth_utils.py
│   └── models.py
│
├── frontend/
│   ├── pages/
│   │   └── api/
│   │       └── auth/
│   │           └── [...nextauth].ts
│   ├── lib/
│   │   └── api.ts
│   ├── components/
│   ├── styles/
│   ├── .env.local
│   ├── package.json
│   └── tsconfig.json
│
├── docker-compose.yml
└── package-lock.json

☁️ Google Cloud Console 설정

Google OAuth 클라이언트를 등록해야 로그인 인증이 가능합니다.

1️⃣ OAuth 클라이언트 생성

  1. Google Cloud Console 접속
  2. API 및 서비스 → 사용자 인증 정보 → 사용자 인증 정보 만들기 → OAuth 클라이언트 ID
  3. OAuth 클라이언트 ID 생성
    • 애플리케이션 유형: 웹 애플리케이션
    • 승인된 리디렉션 URI: http://localhost:3000/api/auth/callback/google

2️⃣ 환경 변수 설정

Next.js 루트 디렉토리에 .env.local 파일을 생성합니다.

GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your_random_secret

💡 NEXTAUTH_SECRET는 JWT 서명용 키입니다.
터미널에서 다음 명령어로 생성할 수 있습니다.

python -c “import secrets; import base64; print(base64.b64encode(secrets.token_bytes(32)).decode())”


⚙️ NextAuth 설정하기

Next.js에서는 next-auth 패키지를 이용하면 간단하게 Google 로그인을 구현할 수 있습니다.

✅ 설치 명령어

npm install next-auth

[...nextauth].ts 설정 예시

import NextAuth, { NextAuthOptions } from "next-auth";
import GoogleProvider from "next-auth/providers/google";

const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID!;
const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET!;
const NEXTAUTH_SECRET = process.env.NEXTAUTH_SECRET!;

export const authOptions: NextAuthOptions = {
  providers: [
    GoogleProvider({
      clientId: GOOGLE_CLIENT_ID,
      clientSecret: GOOGLE_CLIENT_SECRET,
    }),
  ],
  secret: NEXTAUTH_SECRET,
  callbacks: {
    // ✅ JWT에 accessToken 저장
    async jwt({ token, account }) {
      if (account?.access_token) {
        token.accessToken = account.access_token;
      }
      return token;
    },
    // ✅ session에 accessToken 포함
    async session({ session, token }) {
      (session as any).accessToken = (token as any).accessToken;
      return session;
    },
  },
};

export default NextAuth(authOptions);

🔍 핵심 포인트
useSession() 훅으로 로그인 상태를 쉽게 관리 가능
jwt() 콜백에서 구글에서 받은 access_token을 저장
session() 콜백에서 클라이언트가 접근 가능한 세션에 전달


🧩FastAPI에서 사용자 등록 및 검증 API 구현

이제 FastAPI에서 Google 로그인 사용자 정보를 저장 및 조회하는 로직을 구현합니다.

user.py — 사용자 라우터 예시

from fastapi import APIRouter, Depends, HTTPException, Header, Body
from sqlmodel import Session, select
from db import get_session
from models import User
from auth_utils import verify_google_token  # JWT 검증 로직

router = APIRouter()

# ✅ Google 로그인 후 사용자 검증
@router.post("/auth/google/check")
def check_google_user(
    authorization: str = Header(...),
    session: Session = Depends(get_session)
):
    if not authorization.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Invalid Authorization header")

    token = authorization.replace("Bearer ", "")
    google_data = verify_google_token(token)
    email = google_data.get("email")

    if not email:
        raise HTTPException(status_code=401, detail="Invalid token: email not found")

    user = session.exec(select(User).where(User.email == email)).first()
    if user:
        return {"exists": True, "user": {"id": user.id, "email": user.email, "nickname": user.nickname}}

📄 JWT를 이용한 FastAPI 인증 검증

Google 로그인으로 받은 AccessToken이 유효한지 확인하려면
Google의 tokeninfo API를 호출해 JWT를 검증합니다.

auth_utils.py 예시

import requests
from fastapi import HTTPException

GOOGLE_TOKEN_INFO_URL = "https://www.googleapis.com/oauth2/v3/tokeninfo"

def verify_google_token(token: str):
    response = requests.get(GOOGLE_TOKEN_INFO_URL, params={"access_token": token})
    if response.status_code != 200:
        raise HTTPException(status_code=401, detail="Invalid Google token")
    return response.json()

이 함수는 Google의 인증 서버에 토큰을 전달하고,
유효하지 않으면 401 Unauthorized 예외를 발생시킵니다.


🎨 프론트엔드에서 FastAPI 호출하기

Next.js 프론트엔드에서는 next-authsession 객체에 담긴 accessToken을 이용해
FastAPI로 JWT 기반 인증 요청을 보낼 수 있습니다.

✅ FastAPI로 사용자 검증 요청 예시

// ✅ FastAPI로 사용자 확인 요청
const accessToken = (session as any)?.accessToken;

const userCheckRes = await callApi.post(
  "/users/auth/google/check",
  {},
  {
    headers: {
      Authorization: `Bearer ${accessToken}`,
    },
  }
);

if (userCheckRes.data.exists) {
  console.log("✅ 기존 사용자 로그인:", userCheckRes.data.user);
} else {
  console.log("🆕 신규 사용자 회원가입 완료:", userCheckRes.data.user);
}

🧩 전체 흐름 요약

단계동작 내용
사용자가 Google 로그인 버튼 클릭
next-auth가 Google OAuth 인증 처리
인증 성공 시, session.accessToken에 Google JWT 저장
프론트엔드가 FastAPI로 Authorization: Bearer <accessToken> 전송
FastAPI가 verify_google_token()으로 JWT 유효성 검증

이로써 Next.js와 FastAPI가 분리된 환경에서도 완전한 Google 인증 연동이 가능합니다 ✅


🔒 보안 팁

  • .env 파일은 반드시 .gitignore에 포함시켜 커밋하지 않습니다.
  • 토큰 검증은 반드시 HTTPS 환경에서만 수행해야 합니다.
  • AccessToken은 세션에만 일시적으로 저장하고, 장기 저장은 피해야 합니다.

📎 참고

댓글 남기기