[BackEnd] 시각화를 위한 API 서버 개발

Tech Stack : FastAPI, MySQL, Docker

🥅 Goal

  • Phase 1

  • Phase 2

🛠️ Tech Stack

👩🏻‍🏫 Subject

왜 FastAPI를 선택했는가

💨 What is Fast API and how to use???

FastAPI는 Python으로 작성된 최신 웹 프레임워크로, 빠르고 높은 성능을 자랑하며, 자동화된 API 문서화와 타입 힌팅을 강력하게 지원합니다. Starlette와 Pydantic을 기반으로 구축되어, 웹 애플리케이션과 API 서버를 빠르고 쉽게 개발할 수 있습니다.

https://fastapi.tiangolo.com/ko/

FastAPI의 주요 특징

  1. 고성능: Uvicorn과 함께 사용할 때 매우 빠르며, Node.js 및 Go와 유사한 성능을 제공합니다.

  2. 자동화된 문서화: Swagger UI와 ReDoc을 통해 API 문서화를 자동으로 생성합니다.

  3. 타입 힌팅 지원: Python의 타입 힌팅을 활용하여 코드의 가독성과 유지보수성을 높입니다.

  4. 데이터 검증: Pydantic을 사용하여 데이터 검증 및 직렬화를 간편하게 처리합니다.

  5. 비동기 지원: 비동기 기능을 기본적으로 지원하여 높은 동시성을 제공합니다.

다른 Python 프레임워크들과의 비교

FastAPI vs Flask

Flask:

  • 장점:

    • 매우 경량화되어 있고 간단한 사용법

    • 확장성이 뛰어나며, 다양한 플러그인 제공

  • 단점:

    • 내장된 비동기 지원 부족

    • 대규모 애플리케이션 관리에 어려움이 있을 수 있음

FastAPI:

  • 장점:

    • 비동기 기능 기본 지원

    • 자동화된 API 문서화

    • 타입 힌팅과 데이터 검증 기능 강화

  • 단점:

    • 상대적으로 생태계가 작음

    • 러닝 커브가 다소 있을 수 있음

FastAPI vs Django

Django:

  • 장점:

    • 풀 스택 프레임워크로, ORM, 인증, 어드민 패널 등 다양한 기능 제공

    • 대규모 프로젝트에 적합

  • 단점:

    • 복잡하고 무거움

    • 비동기 지원이 제한적

FastAPI:

  • 장점:

    • 경량화된 구조

    • 비동기 기능 기본 지원

    • 자동화된 API 문서화 및 데이터 검증

  • 단점:

    • 풀 스택 기능이 없으므로, 추가적인 라이브러리 필요

    • 전통적인 웹 개발보다는 API 서버에 더 적합

Directory Structure

.
├── app
│   ├── __init__.py
│   ├── main.py
│   ├── core
│   │   ├── config.py
│   ├── api
│   │   ├── __init__.py
│   │   ├── v1
│   │   │   ├── __init__.py
│   │   │   ├── endpoints
│   │   │   │   ├── __init__.py
│   │   │   │   ├── items.py
│   │   │   │   ├── users.py
│   ├── models
│   │   ├── __init__.py
│   │   ├── item.py
│   │   ├── user.py
│   ├── schemas
│   │   ├── __init__.py
│   │   ├── item.py
│   │   ├── user.py
│   ├── crud
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── item.py
│   │   ├── user.py
│   ├── db
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── session.py
│   ├── tests
│   │   ├── __init__.py
│   │   ├── test_items.py
│   │   ├── test_users.py
└── Dockerfile


CORS (Cross Origin Resource Sharing) 설정하기


Python ORM

  • SQLAlchemy

  • pyMysql

ORM 개념 및 Python ORM Library 비교 그리고 간단한 예제

ORM(Object-Relational Mapping)은 객체 지향 프로그래밍 언어에서 데이터베이스를 객체로 다루기 위해 사용하는 기술입니다. Python에서는 여러 ORM 라이브러리가 있지만, 가장 많이 사용되는 두 가지는 SQLAlchemy와 PyMySQL입니다.

SQLAlchemy

SQLAlchemy는 Python의 대표적인 ORM 라이브러리로, 강력한 데이터베이스 접근 및 ORM 기능을 제공합니다. SQLAlchemy는 데이터베이스와의 상호작용을 ORM과 Core 두 가지 방식으로 제공합니다.

SQLAlchemy 설정 및 사용 예제

  1. install

pip install sqlalchemy
pip install pymysql
  1. setting

# config.py
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "mysql+pymysql://username:password@localhost/dbname"

engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
  1. define model

# models.py
from sqlalchemy import Column, Integer, String
from .config import Base

class Item(Base):
    __tablename__ = "items"
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String(50), index=True)
    description = Column(String(200), index=True)
  1. crud example

# crud.py
from sqlalchemy.orm import Session
from .models import Item

def get_item(db: Session, item_id: int):
    return db.query(Item).filter(Item.id == item_id).first()

def create_item(db: Session, item: Item):
    db.add(item)
    db.commit()
    db.refresh(item)
    return item
  1. example of using database session

# main.py
from fastapi import FastAPI, Depends
from .config import SessionLocal
from .models import Base, Item
from .crud import get_item, create_item

app = FastAPI()

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

@app.post("/items/", response_model=Item)
def create_new_item(name: str, description: str, db: Session = Depends(get_db)):
    item = Item(name=name, description=description)
    return create_item(db, item)

@app.get("/items/{item_id}", response_model=Item)
def read_item(item_id: int, db: Session = Depends(get_db)):
    db_item = get_item(db, item_id)
    if db_item is None:
        raise HTTPException(status_code=404, detail="Item not found")
    return db_item

PyMySQL

PyMySQL는 MySQL 데이터베이스와 상호작용할 수 있는 Python 라이브러리로, 직접적인 데이터베이스 접근을 제공하며 ORM 기능은 없습니다. 주로 SQLAlchemy와 함께 사용됩니다.

PyMySQL 설정 및 사용 예제

  1. install

pip install pymysql
  1. setting

import pymysql.cursors

connection = pymysql.connect(host='localhost',
                             user='username',
                             password='password',
                             database='dbname',
                             cursorclass=pymysql.cursors.DictCursor)
  1. crud example

# create_item.py
import pymysql

def create_item(name: str, description: str):
    connection = pymysql.connect(host='localhost',
                                 user='username',
                                 password='password',
                                 database='dbname',
                                 cursorclass=pymysql.cursors.DictCursor)
    try:
        with connection.cursor() as cursor:
            sql = "INSERT INTO items (name, description) VALUES (%s, %s)"
            cursor.execute(sql, (name, description))
        connection.commit()
    finally:
        connection.close()

def get_item(item_id: int):
    connection = pymysql.connect(host='localhost',
                                 user='username',
                                 password='password',
                                 database='dbname',
                                 cursorclass=pymysql.cursors.DictCursor)
    try:
        with connection.cursor() as cursor:
            sql = "SELECT * FROM items WHERE id=%s"
            cursor.execute(sql, (item_id,))
            result = cursor.fetchone()
            return result
    finally:
        connection.close()

비교 및 장단점

SQLAlchemy:

  • 장점:

    • 강력한 ORM 기능

    • 다중 데이터베이스 지원

    • 데이터베이스 이식성

    • 고급 쿼리 작성 가능

  • 단점:

    • 러닝 커브가 있음

    • 상대적으로 복잡함

PyMySQL:

  • 장점:

    • 경량화되고 간단함

    • 직접적인 SQL 쿼리 사용

  • 단점:

    • ORM 기능 없음

    • 수동으로 SQL 쿼리를 작성해야 함

디렉터리 구조 예시

SQLAlchemy를 사용하는 프로젝트 구조:

.
├── app
│   ├── __init__.py
│   ├── main.py
│   ├── config.py
│   ├── models.py
│   ├── crud.py
│   └── schemas.py
└── Dockerfile

PyMySQL을 사용하는 프로젝트 구조:

.
├── app
│   ├── __init__.py
│   ├── main.py
│   ├── create_item.py
│   └── get_item.py
└── Dockerfile

이 구조는 각 기능을 모듈로 나누어 유지보수성과 가독성을 높입니다. SQLAlchemy를 사용할 때는 ORM을 통한 데이터베이스 접근을 중심으로 하고, PyMySQL을 사용할 때는 직접적인 SQL 쿼리를 사용하는 예를 보여줍니다.


여러 데이터 베이스 연결하기

| 하루 정도 걸림, 여러개 세션 만들고 관리하기

  • pymysql


Prepared Statement with MySQL in Python

개념 및 사용 예제

Prepared Statement는 SQL 쿼리의 실행 계획을 미리 컴파일하여 여러 번 실행할 때 성능을 향상시키고 보안을 강화하는 기능입니다. SQL 인젝션 공격을 방지하고, 동일한 쿼리를 반복적으로 실행할 때 성능을 최적화합니다.

주요 특징

  1. 보안: SQL 인젝션 공격을 방지합니다. 사용자 입력을 안전하게 처리할 수 있습니다.

  2. 성능 향상: 쿼리를 미리 컴파일하고, 실행 계획을 캐싱하여 반복적인 쿼리 실행 시 성능을 개선합니다.

  3. 재사용성: 동일한 쿼리를 여러 번 실행할 수 있어 코드의 재사용성을 높입니다.

SQLAlchemy에서의 Prepared Statement 예제

SQLAlchemy는 ORM과 Core 두 가지 방식으로 쿼리를 작성할 수 있습니다. 여기서는 Core를 사용한 Prepared Statement 예제를 보여줍니다.

  1. install

pip install sqlalchemy
pip install pymysql
  1. setting

from sqlalchemy import create_engine, text

SQLALCHEMY_DATABASE_URL = "mysql+pymysql://username:password@localhost/dbname"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
  1. Prepared Statement

# Using prepared statements with SQLAlchemy
with engine.connect() as connection:
    statement = text("SELECT * FROM items WHERE name = :name")
    result = connection.execute(statement, {"name": "example_item"})
    for row in result:
        print(row)

PyMySQL에서의 Prepared Statement 예제

PyMySQL에서는 Prepared Statement를 cursor.execute() 메서드를 사용하여 간단히 구현할 수 있습니다.

  1. install

pip install pymysql
  1. setting

import pymysql.cursors

connection = pymysql.connect(host='localhost',
                             user='username',
                             password='password',
                             database='dbname',
                             cursorclass=pymysql.cursors.DictCursor)
  1. prepared statement usage

# Using prepared statements with PyMySQL
try:
    with connection.cursor() as cursor:
        sql = "SELECT * FROM items WHERE name = %s"
        cursor.execute(sql, ('example_item',))
        result = cursor.fetchall()
        for row in result:
            print(row)
finally:
    connection.close()

Prepared Statement의 장단점

장점:

  • 보안 강화: SQL 인젝션 공격을 방지.

  • 성능 향상: 쿼리 계획을 캐싱하여 반복적인 쿼리 실행 시 성능 개선.

  • 재사용성: 동일한 쿼리를 여러 번 실행할 수 있어 코드의 재사용성 향상.

단점:

  • 복잡성 증가: 간단한 쿼리의 경우, Prepared Statement를 사용하는 것이 과도한 복잡성을 초래할 수 있음.

  • 초기 컴파일 비용: Prepared Statement를 처음 준비할 때 약간의 성능 오버헤드가 발생할 수 있음.

Prepared Statement는 특히 사용자 입력을 포함하는 쿼리를 실행할 때 매우 유용합니다. 이를 통해 SQL 인젝션을 방지하고, 코드의 안전성과 효율성을 높일 수 있습니다.


반환 형태 템플릿 만들기

  • 생산성 향상

🔥 Challenge

데드라인 맞추기

확장성과 생산성 고려하여 설계하기

Last updated