FastAPI에 JWT 사용하기 -2

DEVHOB·2025년 6월 11일

FastAPI 학습 과정

목록 보기
4/6
모듈주요 기능언제 사용하나요?
python-jose[cryptography]JWT 생성, 검증, 암호화 등 보안 토큰 작업인증 시스템 (로그인 등)
passlib[bcrypt]비밀번호 해싱 및 검증회원가입/로그인 비밀번호 처리
python-multipartmultipart/form-data 요청 처리파일 업로드 API 등
  • 🔗 APIRouter란???
    router = APIRouter(
        prefix='/auth',
        tags=['auth']
    )
    
    # main.py
    import auth
    app = FastAPI()
    app.include_router(auth.router) # auth.py에서 만든 라우터를 메인 애플리케이션에 틍록하는 작업
    • APIRouter()는 FastAPI에서 라우터(경로)를 분리/관리할 수 있게 해주는 객체
    • prefix='/auth': 이 라우터에 등록된 모든 경로 앞에 /auth가 자동으로 붙는다. 예: @router.post("/login")이면 실제 경로는 /auth/login이 됩니다.
    • tags=['auth']: 이건 Swagger 문서에서 카테고리 이름으로 사용됩니다.
      • ✔️ 목적: 인증 관련 API들을 별도의 라우터 파일로 분리해 유지 보수를 쉽게 하려는 것
  • ⚒️ 비밀번호 해싱 도구
    brypt_context = CryptContext(schemes=['bcrypt'],deprecated='auto')
    비밀번호 해싱
    hashed_password = bcrypt_context.hash("plain_password")
    
    # 비밀번호 검증
    is_valid = bcrypt_context.verify("plain_password", hashed_password)
    deprecated='auto' : 어떤 해시 알고리즘을 더 이상 기본으로 사용하지 않을 것인지 자동으로 판단하게 함 향후 알고리즘 교체/ 마이그레이션 전략에 유용 bcrypt: 비밀번호 저장용 안전한 해시 알고리즘 SHA256는 빠르기 떄문에 공격자도 빠르게 시도를 할 수 있다. Salt가 없어서 같은 비밀번호는 같은 해시값이다. 🔐 왜 bcrypt가 필요한가? ✅ 일반적인 해시 함수(e.g. SHA256)는:
    • 매우 빠르다 → 공격자도 빠르게 시도할 수 있음

    • salt가 없으면 같은 비밀번호는 같은 해시값 → rainbow table 공격에 취약

      ✅ bcrypt는 다음과 같은 보안 기능을 제공:

      기능설명
      Salt 내장무작위 salt 자동 추가 → 해시 중복 방지
      느린 계산 속도고의로 느리게 처리 → 공격자가 빠르게 추측 시도 못 함
      Cost 설정 가능rounds나 cost 값을 조정하여 연산량 조절 가능
  • 🪙 토큰 기반 인증
    • OAuth2 방식의 토큰 인증을 구현할 떄 사용하는 의존성 설정 객체

      🔍 상세 설명:

    • OAuth2PasswordBearer는 FastAPI가 제공하는 인증 방식 중 하나로,

      • 로그인 후 받은 토큰을 헤더에 넣어야만 접근이 가능한 보호된 라우터를 만들 때 사용합니다.
    • tokenUrl='/auth/token'는 토큰을 발급받기 위한 엔드포인트 URL을 의미합니다.
      - 예: 클라이언트는 /auth/token에 사용자명/비번을 POST해서 토큰을 받고,
      - 이후 요청 시 Authorization: Bearer 형태로 헤더를 보냅니다.

      from fastapi import Depends
      
      # 보호된 엔드포인트 예시
      @app.get("/protected")
      async def protected_route(token: str = Depends(oauth2_bearer)):
          return {"token": token}

      ✅결론 :

      OAuth2PasswordBearer는 단순히 헤더에서 토큰 문자열만 추출해줄 뿐!

  • ✔️ pydantic의 검증 기능!
    1. 자동 데이터 검증(Validation)

      BaseModel을 상속한 클래스는 타입 힌트를 기반으로 입력값을 검증

      from pydantic import BaseModel
      
      class CreateUserRequest(BaseModel):
          username: str
          password: str
      
      data = CreateUserRequest(username="john", password="1234")  # OK
      data = CreateUserRequest(username=123, password=True)       # 에러 발생

      username에 정수가 들어오거나 password에 불리언이 들어오면 ValidationError가 발생!!

    2. 자동 타입 변환(Type Coercion)

      가능하다면, pydantic은 값을 지정된 타입으로 변환할려고 시도함.

      user = CreateUserRequest(username=123, password=456)
      print(user.username)  # '123'
      print(user.password)  # '456'
    3. 직렬화 지원(Serialization)

      .dict() 또는 .json() 메서드로 Python 객체를 딕셔너리나 JSON으로 변환 가능

      user.dict()
      # {'username': 'john', 'password': '1234'}
      
      user.json()
      # '{"username": "john", "password": "1234"}'
    4. IDE지원 및 자동완성

      BaseModel 을 사용하면 타입 힌트 기반 자동완성과 오타 방지에 매우 유리하다.

    5. 보안, 문서화 및 FastAPI연계

      Pydantic은 FastAPI와 같이 사용할 때, 요청/응답 바디 검증, 자동 Swagger 문서화 등에 아주 유용하게 사용됩니다.

      class Token(BaseModel):
          access_token: str
          token_type: str

      정의 클래스 사용 법!

      @app.post("/login", response_model=Token)
      def login(user: CreateUserRequest):
          ...
      
  • DB Depends() 데이터베이스 의존성 확인
    from typing import Annotated
    from fastapi import APIRouter, Depends, HTTPException
    
    def get_db():
        db = SessionLocal()
        try:
            yield db
        finally:
            db.close()
            
            
    db_dependency = Annotated[Session, Depends(get_db)]
    
    @router.post("/", status_code=status.HTTP_201_CREATED)
    # db_dependency  : 의존성 주읩으로 SQLALchemy의 Session 객체를 받아온다.
    async def create_user(db: db_dependency, create_user_request: CreateUserRequest):
        create_user_model = Users(
            username= create_user_request.username,
            hashed_password = bcrypt_context.hash(create_user_request.password)
        )
    
        db.add(create_user_model)
        db.commit()
    Annotated[기본타입, 메타데이터] : 타입 힌트 확장 도구! db_dependency = Annotated[Session, Depends(get_db)] SQLAlchemy 세션을 자동으로 주입받기 위해 사용하는 의존성 정의 → "db_dependencySession 타입이고, get_db() 함수를 실행해서 얻은 결과를 여기에 넣어줘." 🔹 create_user_request 👉 클라이언트가 보내는 요청 데이터입니다. 보통 JSON body로 전송되고, FastAPI는 이를 Pydantic 모델로 자동 파싱합니다.
    {
      "username": "johndoe",
      "email": "john@example.com",
      "password": "secret123"
    }
    이런 JSON이 오면, FastAPI는 create_user_request: CreateUserRequest로 자동 변환 🔹 db 👉 FastAPI가 자동으로 만들어서 넣어주는 의존성입니다. Depends(get_db)를 통해 get_db() 함수가 호출되고, 그 결과인 SQLAlchemy 세션(Session) 객체가 db 매개변수로 들어갑니다. 즉, 클라이언트는 db를 몰라도 되고, FastAPI가 내부적으로 관리해줍니다.
profile
배운 내용 복습 및 새로운 내용 학습

0개의 댓글