Notice
Recent Posts
Recent Comments
Link
«   2026/07   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

Pause & Play

[FastAPI] 카카오 소셜 로그인 백엔드 구현하기 (OAuth 2.0 & Azure 배포) 본문

개발

[FastAPI] 카카오 소셜 로그인 백엔드 구현하기 (OAuth 2.0 & Azure 배포)

eun_ll 2026. 4. 18. 18:56

최근 프로젝트를 진행하며 FastAPI 환경에서 카카오 소셜 로그인을 구현할 기회를 가지게 되었다. 소셜 로그인 구현이 처음 해보았을 때, 좀 헷갈렸어서... 이렇게 정리를 하는 시간을 가지게 되었다.

 

구현 성공을 기념으로 어떻게 진행하면 좋을지 정리해보도록 하겠다.

1. 전체적인 로그인 흐름 이해하기

본격적으로 코드를 작성하기 전에 카카오 로그인 API의 인증 흐름을 이해하는 게 중요하다.

  • Frontend: 사용자가 '카카오 로그인' 버튼 클릭 → 카카오 인가 코드 요청
  • Kakao API: 사용자 인증 후 인가 코드(Authorization Code)를 Backend로 전달
  • Backend: 전달받은 인가 코드로 카카오에 Access Token 요청
  • Backend: 토큰을 이용해 사용자 정보(이메일, 닉네임 등) 획득
  • Backend: 우리 서비스의 자체 JWT 발급 또는 로그인 처리

Note: 나 같은 경우는 프론트에서 인가코드를 받아 백엔드로 전달해주는 흐름이 아닌 프론트에서 엑세스 토큰을 발급받을 줄 알아서 처음 프론트와의 연결에서 살짝 문제가 생겼었다.. 그래서 개발 시작 전 SwaggerPostman을 활용해 요청(Request)과 응답(Response)의 데이터 타입, 필수값 여부, 에러 코드 규격을 먼저 확정하는 'API-First' 접근 방식을 지향하게 되었다.

JWT(JSON Web Token)란?

로그인에 성공한 사용자에게 "당신은 인증된 사용자입니다"라고 증명서를 끊어준다고 생각하면 이해하기 쉽다. 토큰을 받아오면 그 토큰의 유효성만 검증하면 되는 형식이기 때문에 서버 부하를 줄일 수 있어 가장 많이 쓰인다.

  • Header: 토큰의 타입(JWT)과 사용 중인 해시 알고리즘 정보.
  • Payload: 사용자 ID, 토큰 만료 시간 등 실제 전달하려는 정보(Claim).
  • Signature: 서버만 알고 있는 'Secret Key'로 암호화된 서명. (변조 확인용)

2. 사전 준비

카카오 디벨로퍼스에서 다음 설정을 완료한다. (메인화면 > 앱 > 앱생성 클릭)

플랫폼키로 들어간다.
  1. 내 애플리케이션 등록
  2. Platform: 웹 서비스 URL 등록 (예: http://localhost:8000)
  3. Redirect URI: 인가 코드를 받을 주소 등록 (예: /auth/kakao/callback)
  4. Client Secret: 보안을 위해 활성화 권장

REST API 방식을 선택한 이유?

백엔드에서 server to server로 통신을 하기 때문에 좀 더 토큰 관리의 안정성이 보장되기도 하고, 추후 플랫폼 확장성과 비즈니스 로직을 생각했을 때도 REST API가 적합하다는 판단을 내렸다.


3. FastAPI 코드 구현

기본적으로 팀 프로젝트를 진행하여 깃허브에 올릴 경우 보안상의 문제로 .env 파일을 활용한다.

api키와 리디엑트 uri 만 있어도 되지만, 나는 인가 코드를 가로채는 공격을 방지하기 위해 서버 사이드에서 client_secret을 사용해 검증력을 높였다.

 

[.env]

KAKAO_REST_API_KEY=<사용한 RESTAPI 키>
KAKAO_USER_INFO_URL=https://kapi.kakao.com/v2/user/me
KAKAO_REDIRECT_URI=<리디렉트 URI 주소>
KAKAO_CLIENT_SECRET=<클라이언트 시크릿 키>

[Backend Logic]

from fastapi import APIRouter, HTTPException, Depends
from sqlalchemy.orm import Session
import httpx
from src.app.core.config import settings
from src.app.core.security import create_access_token
from src.app.core.database import get_db
from src.app.models.user import User
from src.app.schemas.auth import LoginRequest

router = APIRouter()

@router.post("/login")
async def kakao_login(request: LoginRequest, db: Session = Depends(get_db)):
    """
    카카오톡을 이용한 로그인 프로세스를 진행합니다.
    """
    # 1. 인가 코드로 카카오 토큰 발급 요청 (POST 방식)
    token_url = "https://kauth.kakao.com/oauth/token"
    token_data = {
        "grant_type": "authorization_code",
        "client_id": settings.KAKAO_REST_API_KEY,
        "redirect_uri": settings.KAKAO_REDIRECT_URI,
        "code": request.accessToken, # 프론트에서 받은 인가코드
        "client_secret": settings.KAKAO_CLIENT_SECRET,
    }

    async with httpx.AsyncClient() as client:
        token_resp = await client.post(token_url, data=token_data)

        if token_resp.status_code != 200:
            raise HTTPException(status_code=401, detail="카카오 토큰 발급 실패")

        kakao_tokens = token_resp.json()
        real_access_token = kakao_tokens.get("access_token")

        # 2. 받은 진짜 access_token으로 사용자 정보 가져오기
        user_info_url = settings.KAKAO_USER_INFO_URL
        headers = {
            "Authorization": f"Bearer {real_access_token}",
            "Content-type": "application/x-www-form-urlencoded;charset=utf-8",
        }
        user_resp = await client.post(user_info_url, headers=headers)

    if user_resp.status_code != 200:
        raise HTTPException(status_code=401, detail="사용자 정보 조회 실패")

    kakao_user = user_resp.json()
    kakao_id = str(kakao_user.get("id"))

    # 3. DB 조회 및 회원가입 로직
    user = db.query(User).filter(User.social_id == kakao_id).first()
    if not user:
        temp_nickname = f"BINGO_{kakao_id[:5]}"
        user = User(social_id=kakao_id, social_provider="KAKAO", nickname=temp_nickname)
        db.add(user)
        db.commit()
        db.refresh(user)

    # 4. 우리 서비스 전용 JWT 발급
    my_token = create_access_token(data={"sub": str(user.id)})
    is_required_nickname = user.nickname.startswith("BINGO_")

    return {
        "status": "success",
        "message": "로그인 성공",
        "data": {
            "accessToken": my_token,
            "is_required_nickname": is_required_nickname,
            "user": {"id": user.id, "nickname": user.nickname},
        },
    }

4. Azure 배포 및 CORS 설정

코드를 다 짰다면 이제 실제 서버에 올려서 프론트엔드와 연결할 차례다. 우리는 Azure 인프라를 활용해 환경을 구축하였다.

1. 인프라 구성

  • Frontend: Azure Static Web Apps (SWA)를 이용해 배포했다.  SWA는 GitHub 액션과 연동되어 push만 하면 자동으로 배포되는 형식으로 정말 편하다.
  • Backend: Azure App Service를 사용했다. 

[배포 tip] Azure Static Web Apps 생성 및 GitHub 연동

1. 빌드 세부 설정 (Build Details)

연동 후 하단에 나타나는 빌드 상세 설정도 꼼꼼히 봐야 한다. 이 부분이 틀리면 "배포는 성공했는데 화면이 안 떠요" 하는 상황이 발생한다.

  • Build Presets: 프론트엔드 프레임워크(React, Vue, Next.js 등)에 맞춰 선택한다.
  • App location: 소스 코드가 있는 루트 경로 (보통 /)
  • Output location: 빌드 결과물이 생기는 폴더명. 프레임워크마다 dist인지 build인지 다르므로 본인의 프로젝트 구조를 꼭 확인해야 한다. (예: Vite는 보통 dist, React 기본 설정은 build)

2. 배포 권한 설정 (Deployment configuration)

리소스를 처음 생성할 때 가장 중요한 부분이다.

  • GitHub 선택 필수: 캡처 화면처럼 Deployment authorization policy에서 GitHub를 선택해야 한다. 그래야 GitHub Repository의 코드가 수정될 때마다 Azure가 알아서 빌드하고 배포하는 자동화(CI/CD)가 완성된다.
  • 권한 승인: GitHub를 선택하면 내 계정을 연동하라는 창이 뜨는데, 거기서 대상 리포지토리를 정확히 골라주면 된다.

2. 프론트엔드와 연결 시 주의할 점 (CORS 설정)

서로 다른 도메인(SWA 주소와 App Service 주소) 간에 통신을 하려면 FastAPI에서 CORS(Cross-Origin Resource Sharing) 설정을 반드시 해주어야 한다.

CORS(Cross-Origin Resource Sharing)란?

브라우저에서 보안을 위해 출처가 다른 서버로의 요청을 막는 정책이다. FastAPI에서는 CORSMiddleware를 통해 허용할 프론트엔드 주소를 등록해주어야 한다.

 
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://your-static-web-app-url.azurestaticapps.net"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

또한 Azure App Service의 설정(Configuration) 메뉴에서 환경 변수를 등록해주는 것도 잊지 말아야 한다.

이렇게 연결 후 테스트를 하면 로그인이 성공하는 걸 확인할 수 있다.

 


처음에는 프론트엔드에서 넘겨주는 값이 인가 코드인지 엑세스 토큰인지 헷갈려 커뮤니케이션 미스가 있기도 했지만, 덕분에 OAuth 2.0의 흐름을 정확히 머릿속에 새길 수 있었다. 역시 개발은 직접 삽질해보고 구현해봐야 가장 빠르게 배우는 것 같다...

 

Azure 배포 환경에서의 설정이나 CORS 이슈 등 제가 겪은 과정이 소셜 로그인을 처음 구현하시는 분들께 조금이나마 이정표가 되길 바란다!!

 

 

'개발' 카테고리의 다른 글

GitHub - 협업 편  (1) 2026.04.17