[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의 주요 특징
고성능: Uvicorn과 함께 사용할 때 매우 빠르며, Node.js 및 Go와 유사한 성능을 제공합니다.
자동화된 문서화: Swagger UI와 ReDoc을 통해 API 문서화를 자동으로 생성합니다.
타입 힌팅 지원: Python의 타입 힌팅을 활용하여 코드의 가독성과 유지보수성을 높입니다.
데이터 검증: Pydantic을 사용하여 데이터 검증 및 직렬화를 간편하게 처리합니다.
비동기 지원: 비동기 기능을 기본적으로 지원하여 높은 동시성을 제공합니다.
다른 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 설정 및 사용 예제
install
pip install sqlalchemy
pip install pymysql
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()
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)
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
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 설정 및 사용 예제
install
pip install pymysql
setting
import pymysql.cursors
connection = pymysql.connect(host='localhost',
user='username',
password='password',
database='dbname',
cursorclass=pymysql.cursors.DictCursor)
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 인젝션 공격을 방지하고, 동일한 쿼리를 반복적으로 실행할 때 성능을 최적화합니다.
주요 특징
보안: SQL 인젝션 공격을 방지합니다. 사용자 입력을 안전하게 처리할 수 있습니다.
성능 향상: 쿼리를 미리 컴파일하고, 실행 계획을 캐싱하여 반복적인 쿼리 실행 시 성능을 개선합니다.
재사용성: 동일한 쿼리를 여러 번 실행할 수 있어 코드의 재사용성을 높입니다.
SQLAlchemy에서의 Prepared Statement 예제
SQLAlchemy는 ORM과 Core 두 가지 방식으로 쿼리를 작성할 수 있습니다. 여기서는 Core를 사용한 Prepared Statement 예제를 보여줍니다.
install
pip install sqlalchemy
pip install pymysql
setting
from sqlalchemy import create_engine, text
SQLALCHEMY_DATABASE_URL = "mysql+pymysql://username:password@localhost/dbname"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
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()
메서드를 사용하여 간단히 구현할 수 있습니다.
install
pip install pymysql
setting
import pymysql.cursors
connection = pymysql.connect(host='localhost',
user='username',
password='password',
database='dbname',
cursorclass=pymysql.cursors.DictCursor)
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