Backend/FastAPI

[Fast API] 기초 설정 및 데이터베이스 연결

나죽못고나강뿐 2024. 6. 25. 20:40
📕 목차

1. What is Fast API
2. 설치
3. MySQL 연결
4. API 생성

1. What is Fast API

 

📌 Fast API

파이썬 3.6부터 제공하는 경량 프레임 워크라고 한다.

 

보통 파이썬에서 경량 프레임워크 하면 Flask, 보다 많은 기능이 필요하면 Django를 사용했는데

조작법도 단순하고 성능면으로 우수한 Fast API가 등장했다(고 한다)

 

📌 특징
  • API 문서 자동 생성
  • 의존성 주입 위주의 설계를 통한 DB 등에 대한 관리 용이
  • 비동기 처리
  • Pydantic을 사용한 유효성 검사

 

 

GitHub - psychology50/Fast-API: python fast api 실습용

python fast api 실습용. Contribute to psychology50/Fast-API development by creating an account on GitHub.

github.com

아래는 그냥 혼자 만져보면서 실습해본 내용.

파이썬으로 개발하는 게 오랜만이라 살짝 쫄았는데, 말도 안 되게 편하다.

장고랑 스프링 부트 쓰다가 Fast API 써보니 학습 곡선 찢어버리고 개발 생산성 극대화.. 

 


2. 설치

 

📌 가상환경 설치
python -m venv 가상환경_이름
source 가상환경_이름/Scripts/activate

예전에 장고했던 짬밥이 있어서, 바로 가상환경부터 만들어주고 시작.

이렇게 안 하면 프로젝트가 운영 환경이랑 자꾸 충돌한다.

 

📌 fastapi 설치
pip3 install fastapi
pip3 install uvicorn

uvicorn은 async/swait 기반의 비동기 프로그래밍을 지원해준다.

퍼포먼스가 가장 좋다고 알려진 ASGI라는데, 솔직히 대충 찾아봐서 거기까진 모르겠다.

 

📌 실행

root에 main.py를 만들고 대충 코드를 작성해주자.

import uvicorn

from fastapi import FastAPI

app = FastAPI()

if __name__ == "__main__":
    uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)
uvicorn main:app

위 명령어를 터미널에 입력해주면 실행이 된다.

 


3. MySQL 연결

 

📌 패키지 설치
python3 -m pip install sqlalchemy
python3 -m pip install pymysql

sqlalchemy는 파이썬 ORM 패키지 중 가장 많이 사용된다고 한다. (대충 주워온 내용이라 팩트 체크 안 함)

연결 정보를 수정하기 위해pymysql도 설치해주었다.

 

📌 config 작성
from pathlib import Path
from dotenv import load_dotenv
import os

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

BASE_DIR = Path(__file__).resolve().parent.parent
load_dotenv(os.path.join(BASE_DIR, ".env"))
DB_URL = f'mysql+pymysql://{os.environ["DB_USERNAME"]}:{os.environ["DB_PASSWORD"]}@{os.environ["DB_HOST"]}:{os.environ["DB_PORT"]}/{os.environ["DB_NAME"]}'

class DatabaseConfig:
    def __init__(self):
        self.engine = create_engine(DB_URL, pool_recycle=500) # SQLAlchemy 엔진 초기화. 500초마다 재연결

    def create_session(self): # 세션 생성. 데이터베이스에 대한 모든 쿼리 및 트랜잭션 처리를 위함
        Session = sessionmaker(bind=self.engine, autocommit=False, autoflush=False)
        session = Session()
        return session
    
    def connect_db(self): # 데이터베이스 연결
        return self.engine.connect()

루트 디렉토리에 .env 파일을 만들고 환경 변수를 입력해준 다음, 해당 정보를 기반으로 DB_URL 상수를 만들었다.

 

클래스를 만들어서 연결 정보를 설정하는 게 좋은 건지는 잘 모르겠다.

사용해야 할 때마다 인스턴스를 만들어야 하는 건데..그럼 싱글톤으로 만들어야 하나.

 

참고로 .env 파일은 다음과 같이 작성하면 된다.

DB_USERNAME=hello
DB_PASSWORD=1q2w3e4r!
DB_HOST=localhost
DB_PORT=3306
DB_NAME=test

 

📌 User Entity 작성
# models/user.py
from sqlalchemy import Column, TEXT, INT, BIGINT, DATETIME, BOOLEAN, VARCHAR, CHAR
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = 'user'

    id = Column(BIGINT, primary_key=True, autoincrement=True)
    username = Column(VARCHAR, nullable=False)
    name = Column(VARCHAR, nullable=False)
    password = Column(VARCHAR, nullable=True)
    password_updated_at = Column(DATETIME, nullable=True)
    profile_image_url = Column(VARCHAR, nullable=True)
    phone = Column(VARCHAR, nullable=False)
    role = Column(CHAR, nullable=False)
    profile_visibility = Column(CHAR, nullable=False)
    locked = Column(BOOLEAN, nullable=False)

    account_book_notify = Column(BOOLEAN, nullable=False)
    feed_notify = Column(BOOLEAN, nullable=False)
    chat_notify = Column(BOOLEAN, nullable=False)

    created_at = Column(DATETIME, nullable=False)
    updated_at = Column(DATETIME, nullable=False)
    deleted_at = Column(DATETIME, nullable=True)

사실 나는 기존에 하던 프로젝트의 테이블을 가져왔다.

어차피 로컬이니까 괜찮겄지 뭐 허허

 

📌 인스턴스 생성
from config.database_config import DatabaseConfig

engine = DatabaseConfig()
session = engine.create_session()

나중에 세션을 사용해야 하는 곳에서 위와 같이 선언해놓고 시작하면 된다.

 


4. API 생성

 

📌 DTO

초간단 API를 만들기 전에 DTO를 만들어주자.

 

from pydantic import BaseModel

class UserInfo(BaseModel):
    id: int
    username: str
    name: str
    role: str
    profile_visibility: str
    phone: str

    # 정적 팩토리 메서드
    @staticmethod
    def from_(user):
        return UserInfo(
            id=user.id,
            username=user.username,
            name=user.name,
            role=user.role,
            profile_visibility=user.profile_visibility,
            phone=user.phone
        )

변수명 앞에 `__`를 붙이면 private로 사용 가능한 건 알지만, 연습용으로 대충 만드는 거니까 무시했다.

지저분하게 생성자 쓰기 싫어서 정적 팩토리 메서드 하나 만들어줬다.

 

📌 API
from fastapi import FastAPI

from apis.user.user_api import user_req

app = FastAPI()

app.include_router(user_req)

`@app.get(...)`으로 만들어도 되지만, APIRouter가 재밌어보여서 써봤다.

 

# apis.user.user_api.py
from fastapi import APIRouter, status
from typing import List, Dict, Any
from config.database_config import DatabaseConfig
from models.user import User

from schemas.user_dto import UserInfo

engine = DatabaseConfig()
session = engine.create_session()

user_req = APIRouter(prefix='/users')

@user_req.get(path = "", tags=['user'], status_code=status.HTTP_200_OK, response_model=List[UserInfo])
async def getUsers() -> List[UserInfo]:
    users = session.query(User).all()
    
    user_dto = []
    for user in users:
        user_dto.append(UserInfo.from_(user))
    
    return user_dto

접두사로 `/users` 넣어주고 getter 메서드 빠르게 만들어주었다.

Fast API는 스웨거 문서를 알아서 만들어주더라....미쳤당. 그래서 어노테이션 안에 설명도 열심히 넣어봤다.

 

📌 요청 해보기

엄청나게 빠른 개발이 가능하다..

 

생각보다 너무 재밌는데, 이거나 공부해볼까

Python이 오랜만이라 컨벤션 찾아가면서 작성하느라 시간이 좀 걸렸지, 순수 코드 작성은 처음 만져보는데 불구하고 10분 정도밖에 안 걸렸다.