[FastAPI] TODO Project 앱 스케일링 & User, Todo 모델 재정의

도톨이·2024년 4월 3일

FastAPI

목록 보기
15/17
post-thumbnail

앱 스케일링

새로운 사용자 요청을 받을 때마다, 그 요청을 인증하고 권한을 부여하는 과정은 필수적입니다. 하지만, 이러한 인증과 권한 부여 로직이 우리의 주요 비즈니스 로직과 혼합되어 있으면 코드를 이해하고 관리하기가 어렵습니다. 그래서 우리는 이러한 로직들을 분리하여 관리할 계획입니다.

앱 스케일링과 유지보수성 향상

우리의 앱을 클린하고 유지보수하기 쉽게 만들기 위해, 어떻게 앱을 스케일링할지 생각해 보았습니다. 현재 main 파일이 너무 복잡해지고 있어, router를 사용해 별도의 파일로 분리하기로 결정했습니다. 이렇게 하면 각각의 관심사가 명확히 분리되어, 코드의 가독성과 유지보수성이 크게 향상됩니다.

인증 로직 분리하기

예를 들어, 인증 로직은 auth.py로 분리하여 관리합니다. 아래는 간단한 예시입니다:

from fastapi import APIRouter

router = APIRouter()

@router.get("/auth/")
async def get_user():
    return {'user': 'authenticated'}

할 일 목록 관리 로직 분리하기

또한, 우리의 할 일 목록(todos) 관리 로직도 todos.py로 분리했습니다. 이렇게 하면 코드가 더욱 깔끔해지고, 각 기능별로 파일을 나누어 관리할 수 있어, 유지보수성이 크게 향상됩니다. 아래는 todos.py 파일의 예시입니다:

from fastapi import Depends, HTTPException, Path, APIRouter
from typing import Annotated
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session
from fastapi import status
from models import Todos
from database import SessionLocal

router = APIRouter()

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

db_dependency = Annotated[Session, Depends(get_db)]

class TodoRequest(BaseModel):
    title: str = Field(min_length=3)
    description: str = Field(min_length=3, max_length=100)
    priority: int = Field(gt=0, lt=6)
    complete: bool

@router.get('/')
async def read_all(db: Annotated[Session, Depends(get_db)]):
    return db.query(Todos).all()

@router.get('/todo/{todo_id}', status_code=status.HTTP_200_OK)
async def read_todo(db: db_dependency, todo_id: int = Path(gt=0)):
    todo_model = db.query(Todos).filter(Todos.id == todo_id).first()
    if todo_model is not None:
        return todo_model
    raise HTTPException(status_code=404, detail='Todo not found')

@router.post("/todo", status_code=status.HTTP_201_CREATED)
async def create_todo(db: db_dependency, todo_request: TodoRequest):
    todo_model = Todos(**todo_request.dict())
    db.add(todo_model)
    db.commit()

@router.put("/todo/{todo_id}", status_code=status.HTTP_204_NO_CONTENT)
async def update_todo(db: db_dependency, todo_reqeust: TodoRequest, todo_id: int = Path(gt=0)):
    todo_model = db.query(Todos).filter(Todos.id==todo_id).first()
    if todo_model is None:
        raise HTTPException(status_code=404, detail='Todo not found')
    todo_model.title = todo_reqeust.title
    todo_model.description = todo_reqeust.description
    todo_model.priority = todo_reqeust.priority
    todo_model.complete = todo_reqeust.complete
    db.add(todo_model)
    db.commit()

@router.delete("/todo/{todo_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_todo(db: db_dependency, todo_id: int = Path(gt=0)):
    todo_model = db.query(Todos).filter(todo_id==Todos.id).first()
    if todo_model is None:
        raise HTTPException(status_code=404, detail='Todo not found')
    db.query(Todos).filter(Todos.id==todo_id).delete()
    db.commit()

main 파일 정리하기

위의 변경 사항을 적용하고 나면, main 파일은 다음과 같이 단순화됩니다:

from fastapi import FastAPI
import models
from database import engine
from routers import auth, todos

app = FastAPI()

models.Base.metadata.create_all(bind=engine)

app.include_router(auth.router)
app.include_router(todos.router)

데이터베이스 모델 관계 설정하기

우리의 Todo 프로젝트는 one-to-many 관계입니다. 즉, 한 명의 사용자가 여러 개의 Todo 항목을 가질 수 있습니다. 물론, 한 명의 사용자만 있는 것은 아니며, 여러 사용자가 각각 여러 개의 Todo 항목을 가질 수 있습니다.

이를 위해 우리는 두 개의 테이블을 사용합니다: 하나는 사용자 정보를 담는 User 테이블이고, 다른 하나는 Todo 항목을 담는 Todos 테이블입니다. Todos 테이블에는 사용자 ID를 참조하는 외래 키(ForeignKey)가 있습니다. 이는 두 테이블 사이의 관계를 설정하는 데 사용됩니다.

그래서 데이터 모델은 이렇게 재정의할 것이다.

from database import Base
from sqlalchemy import Column, Integer, String, Boolean, ForeignKey


class Users(Base):
    __tablename__= 'users'

    id = Column(Integer, primary_key=True, index=True)
    email = Column(String, unique=True)
    username = Column(String, unique=True)
    first_name = Column(String)
    last_name = Column(String)
    hashed_password = Column(String)
    is_active = Column(Boolean, default=True)
    role = Column(String)




class Todos(Base):
    __tablename__ = 'todos'
    id = Column(Integer, primary_key=True, index=True)
    title = Column(String)
    description = Column(String)
    priority = Column(Integer)
    complete = Column(Boolean, default=False)
    owner_id = Column(Integer, ForeignKey("users.id"))
profile
Kotlin, Flutter, AI | Computer Science

0개의 댓글