[FastAPI] 2. SQLAlchemy๋ฅผ ์ด์šฉํ•œ ๊ฐ„๋‹จํ•œ CRUD API ๋งŒ๋“ค๊ธฐ

๋ฐ˜์‘ํ˜•

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ORM์— ๋Œ€ํ•œ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

ORM์€ Object Relation Mapping์˜ ์•ฝ์ž๋กœ ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค Entity์— ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๋ณดํ†ต ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ์—์„œ DB์— ์ ‘๊ทผํ•  ๋•Œ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋“œ๋ผ์ด๋ฒ„๋ฅผ ์ด์šฉํ•˜์—ฌ SQL Query๋ฅผ ๋˜์ ธ ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์ง€๋งŒ SQL Query๋Š” ์†Œํ”„ํŠธ์›จ์–ด ์—”์ง€๋‹ˆ์–ด์—๊ฒŒ ์žˆ์–ด ๋Ÿฌ๋‹ ์ปค๋ธŒ๋ฅผ ์ฆ๊ฐ€ ์‹œํ‚ค๊ณ , ์†Œํ”„ํŠธ์›จ์–ด ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์„ ์ €ํ•˜์‹œํ‚ค๋Š” ์›์ธ์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ ORM์„ ์ด์šฉํ•˜๋ฉด ๊ธฐ๋ณธ์ ์ธ CRUD๋ฅผ ํฌํ•จํ•œ ๊ฐ„๋‹จํ•œ ์ฟผ๋ฆฌ์— ๋Œ€ํ•ด SQL Query๋ฅผ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ฝ”๋“œ์— ์งˆ์˜ํ•˜์ง€ ์•Š์•„๋„ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ฝ”๋“œ ์•ˆ์—์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Python ์–ธ์–ด์—์„œ์˜ ๋Œ€ํ‘œ์ ์ธ ORM ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ SQLAlchemy๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

 

 

SQLAlchemy

SQLAlchemy๋Š” ๋ณธ๋ž˜ Flask์—์„œ Flask์šฉ SQLAlchemy ๋””ํŽœ๋˜์‹œ๊ฐ€ ์žˆ์„ ์ •๋„๋กœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ƒํƒœ๊ณ„๊ฐ€ ๊ต‰์žฅํžˆ ๋„“์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๊ณ  ํ•ด์„œ Flask์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. JPA์ฒ˜๋Ÿผ ์–ด๋Š ํ”„๋ ˆ์ž„์›Œํฌ๋“ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด๋“  ๋””ํŽœ๋˜์‹œ๋ฅผ ์˜ฌ๋ฆด ์ˆ˜ ์žˆ๋Š” ๊ณณ์ด๋ผ๋ฉด ์–ด๋–ค ๊ณณ์ด๋“  ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋ฉฐ JPA์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Connection Pool, Lazy loading ๋“ฑ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

[tool.poetry]
name = "fastapiexample"
version = "0.1.0"
description = ""
authors = ["Neon K.I.D <contact@neonkid.xyz>"]

[tool.poetry.dependencies]
python = "^3.8.5"
fastapi = "^0.63.0"
uvicorn = "^0.13.2"
SQLAlchemy = "^1.3.22"
psycopg2-binary = "^2.8.6"

[tool.poetry.dev-dependencies]

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

์šฐ๋ฆฌ๋Š” ์ด ๊ธ€์—์„œ PostgreSQL์„ ์—ฐ๋™ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ , SQLAlchemy์™€ Python์šฉ Postgres ๋“œ๋ผ์ด๋ฒ„์ธ psycopg2-binary ๋””ํŽœ๋˜์‹œ๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด๋„๋ก ํ•ฉ์‹œ๋‹ค.

 

 

 

 

 

Create Model

ํ•„์š”ํ•œ ๋””ํŽœ๋˜์‹œ๋ฅผ ๋ชจ๋‘ ์„ค์น˜ํ–ˆ๋‹ค๋ฉด ์ด์ œ ์‚ฌ์šฉํ•  ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ค์–ด๋ณด๋„๋ก ํ•˜์ฃ . ORM์ด๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์€ ํ…Œ์ด๋ธ”์€ class๋กœ ๋งŒ๋“ค์–ด ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

from sqlalchemy import Boolean, Column, String, Text
from sqlalchemy.ext.declarative import declarative_base

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

class Memo(Base):
    __tablename__ = 'memos'

    id = Column(String(120), primary_key=True, default=lambda: str(uuid.uuid4()))
    title = Column(String(80), default='No title', nullable=False, index=True)
    content = Column(Text, nullable=True)
    is_favorite = Column(Boolean, nullable=False, default=False)

๊ฐ„๋‹จํ•œ ๋ฉ”๋ชจ์žฅ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด Memo๋ผ๋Š” Entity๋ฅผ ์ƒ์„ฑํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ฉ”๋ชจ์žฅ์˜ ๊ณ ์œ  ID์™€ ์ œ๋ชฉ, ๋‚ด์šฉ ๊ทธ๋ฆฌ๊ณ  ์ฆ๊ฒจ์ฐพ๊ธฐ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋Š” is_favorite๋ฅผ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  ์•ฑ ์‹คํ–‰์‹œ ํ…Œ์ด๋ธ”์„ ์ž๋™์œผ๋กœ ๋งŒ๋“ค๋„๋ก create_all ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์ค๋‹ˆ๋‹ค.

 

 

 

Create Connection

์„œ๋ฒ„๋ฅผ ์—ฐ๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ์„ธ์…˜์„ ๋งŒ๋“ค์–ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. 

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker


engine = create_engine('postgresql://{username}:{password}@{host}:{port}/{db_name}'.format(
    username='postgres', password='postgres', host='127.0.0.1', port='5432', db_name='nkmemo'
))
db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))

์„ธ์…˜์€ ์„œ๋ฒ„์—์„œ DB์—๊ฒŒ ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์œ„ํ•œ ํ†ต๋กœ ์—ญํ• ์„ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ฐ๊ฐ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์— ์•Œ๋งž๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ๊ณ , ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์ค๋‹ˆ๋‹ค.

 

autoCommit์„ ๋น„ํ™œ์„ฑํ™”ํ•˜๋Š” ์ด์œ ๋Š” ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์ž‘์—…์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ์—ฌ๋Ÿฌ ์ค„์˜ SQL ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ ํ•œ ๋ฒˆ์— ๋ฐ˜์˜์‹œํ‚ค๋„๋ก ํ•˜๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. ๊ฐ€๋ น ์˜ˆ๋ฅผ ๋“ค์–ด ๋‘ ํ…Œ์ด๋ธ”์ด ๋™์‹œ์— ๋ณ€๊ฒฝ ์ž‘์—…์ด ์ด๋ฃจ์–ด์ง„๋‹ค๊ณ  ํ–ˆ์„ ๋•Œ, ํ•˜๋‚˜์˜ ORM ์ฝ”๋“œ์—์„œ ํŠธ๋žœ์žญ์…˜์ด ๋ฐœ์ƒํ•˜๋”๋ผ๋„ ์ฝ”๋“œ์—์„œ commit ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๊นŒ์ง€๋Š” commit์ด ์ผ์–ด๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

autoFlush๋Š” DB์—์„œ flush๋ฅผ ์ž๋™ํ™”ํ•˜๊ฒ ๋ƒ๋Š” ์—ฌ๋ถ€์ธ๋ฐ, ์ด ์—ญ์‹œ commit์„ ์—ฌ๋Ÿฌ ์ค„๋กœ ๋ฐœ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•ด ์˜์†ํ™”๋ฅผ ์ž๋™ํ™” ์‹œํ‚ค์ง€ ์•Š๊ฒ ๋‹ค๋Š” ์กฐ๊ฑด์œผ๋กœ commit ํ•จ์ˆ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ๋งŒ ์˜์†ํ™” ํ•˜๊ฒ ๋‹ค๋Š” ์˜ต์…˜์ž…๋‹ˆ๋‹ค.

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

FastAPI์—์„œ ๊ฐ API๋งˆ๋‹ค DB์— ์ข…์†๋˜์—ˆ์„ ๋•Œ ํ•จ์ˆ˜๋ณ„๋กœ ์„ธ์…˜์„ ๋ถ€์—ฌํ•˜๊ณ , ์ž‘์—…์ด ๋๋‚˜๋ฉด close ๋  ์ˆ˜ ์žˆ๋„๋ก ์˜์กด ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

 

 

 

 

Apply function

์ด์ œ API ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
from typing import Optional, List

app = FastAPI()


class ResponseMemo(BaseModel):
    id: str
    title: str
    content: Optional[str] = None
    is_favorite: bool

    class Config:
        orm_mode = True


@app.get('/memos', response_model=List[ResponseMemo])
async def get_memos(db: Session = Depends(get_db)):
    memos = db.query(Memo).all()
    return memos

์œ„์—์„œ ๋งŒ๋“ค์—ˆ๋˜ ์ข…์† ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ FastAPI์—์„œ ์ œ๊ณตํ•˜๋Š” Depends ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด API๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค DB ์„ธ์…˜์„ ์ƒ์„ฑํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ด๋ ‡๊ฒŒ ๊ตฌํ˜„ํ•˜๋ฉด API ํ˜ธ์ถœ์‹œ ์„ธ์…˜์ด ์ƒ์„ฑ๋˜๊ณ , ์‚ฌ์šฉ์ด ๋๋‚˜๋ฉด ์ž๋™ ์„ธ์…˜์ด ํ•ด์ œ๋ฉ๋‹ˆ๋‹ค.

 

๋ง๋ถ™์—ฌ์„œ pydantic์—์„œ ์ œ๊ณตํ•˜๋Š” orm_mode๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฐ˜ํ™˜ ๋ชจ๋ธ์„ ๋งŒ๋“ค ๊ฒฝ์šฐ, ORM JSONEncoder์— ์˜ํ•ด ์ž๋™์œผ๋กœ json์œผ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋„๋กœ JSONResponse ๋“ฑ์˜ ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

 

๋งˆ์ง€๋ง‰์œผ๋กœ ์ „์ฒด์˜ ๋ฉ”๋ชจ ๋‚ด์šฉ์„ ๊ฐ€์ ธ์˜ค๊ธฐ ๋•Œ๋ฌธ์— response_model์— List๋ฅผ ์ด์šฉํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

from pydantic import BaseModel
from typing import Optional


class RequestMemo(BaseModel):
    title: str
    content: Optional[str] = None
    is_favorite: Optional[bool] = False


class ResponseMemo(BaseModel):
    id: str
    title: str
    content: Optional[str] = None
    is_favorite: bool

    class Config:
        orm_mode = True


@app.post('/memos', response_model=ResponseMemo)
async def register_memo(req: RequestMemo, db: Session = Depends(get_db)):
    memo = Memo(**req.dict())

    db.add(memo)

    # ์ด ์ฝ”๋“œ๋ฅผ ์“ฐ์ง€ ์•Š์œผ๋ฉด DB์— ๋ฐ˜์˜๋˜์ง€ ์•Š์Œ
    db.commit()

    return memo

id๋Š” ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋˜๊ณ , ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ๋ฐ›์•„์•ผํ•  ๋‚ด์šฉ์€ ์ œ๋ชฉ๊ณผ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค. ์ œ๋ชฉ์€ ํ•„์ˆ˜๊ฐ’์œผ๋กœ ๋‚ด์šฉ์„ ์„ ํƒ๊ฐ’์œผ๋กœ ํ•ด์ค๋‹ˆ๋‹ค.

 

POST ๋ฉ”์†Œ๋“œ ์ž‘์„ฑ์‹œ, ์šฐ๋ฆฌ๋Š” ์œ„์—์„œ autoCommit์„ ์„ค์ •ํ•ด์ฃผ์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ์ˆ˜๋™์œผ๋กœ commit ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์•ผ๋งŒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ์ดํ„ฐ๊ฐ€ ์˜์†๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ commit ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์ฃผ๋„๋ก ํ•ฉ์‹œ๋‹ค.

@app.put('/memos/{item_id}', response_model=ResponseMemo)
async def mod_memo(item_id: str, req: RequestMemo, db: Session = Depends(get_db)):
    memo = db.query(Memo).filter_by(id=item_id)
    req_dict = req.dict()
    req_dict['id'] = item_id
    
    req = {k: v for k, v in req_dict.items()}

    for key, value in req.items():
        setattr(memo, key, value)

    db.commit()

    return memo

PUT ๋ฉ”์†Œ๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๋Š” ๊ธฐ์กด ๋ฉ”๋ชจ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š”์ง€๋ฅผ filter_by ํ˜น์€ filter ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์ƒ‰ํ•œ ํ›„ ์ง„ํ–‰ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

from starlette.responses import Response
from starlette.status import HTTP_204_NO_CONTENT


@app.delete('/memos/{item_id}')
async def del_memo(item_id: str, db: Session = Depends(get_db)):
    memo = db.query(Memo).filter_by(id=item_id).first()

    db.delete(memo)
    db.commit()

    return Response(status_code=HTTP_204_NO_CONTENT)

Session์—์„œ ์ œ๊ณตํ•˜๋Š” delete ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•  ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ filter, filter_by ๋“ฑ์„ ํ†ตํ•ด ๋ชจ๋ธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์€ ๋‹ค์Œ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

 

 

 

 

Test

ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๊ธฐ ์ „์—, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ฝ”๋“œ์— ์ž…๋ ฅํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ƒ์„ฑํ•œ ํ›„ ์ง„ํ–‰ํ•ด์ฃผ์„ธ์š”. ํ…Œ์ŠคํŠธ์—๋Š” Postman ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‚ฌ์šฉํ•ด๋ดค์Šต๋‹ˆ๋‹ค.

 

PyCharm Professional์„ ์‚ฌ์šฉํ•˜์‹ ๋‹ค๋ฉด Database ๊ธฐ๋Šฅ์„ ์ด์šฉํ•˜์‹œ๋Š” ๊ฒƒ๋„ ์ข‹์„ ๊ฒƒ ๊ฐ™๋„ค์š”.

 

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜๋ฉด ์œ„์™€ ๊ฐ™์ด ์ฃผ์†Œ์™€ ํฌํŠธ๋ฅผ ์•Œ๋ ค์ฃผ๋ฉฐ,

 

์œ„์™€ ๊ฐ™์ด ํ…Œ์ด๋ธ”์„ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

 

๋จผ์ € GET ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์•„๋ฌด๋Ÿฐ ๋‚ด์šฉ์ด ์—†์œผ๋ฏ€๋กœ ์•„๋ฌด๊ฒƒ๋„ ์ถœ๋ ฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

๋ฉ”๋ชจ๋ฅผ ํ•˜๋‚˜ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•ด POST ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋‚ด์šฉ์ด ์ถœ๋ ฅ๋˜๊ณ  ๋ฉ”๋ชจ์— ๋Œ€ํ•œ ID๊ฐ€ ๋ถ€์—ฌ๋ฉ๋‹ˆ๋‹ค.

 

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด๊ฐ„ ๋ชจ์Šต์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

PUT ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ๋‚ด์šฉ์„ ์ˆ˜์ •ํ•  ๋•Œ ์—ญ์‹œ ์ž˜ ๋™์ž‘ํ•˜๋ฉด ์œ„์™€ ๊ฐ™์ด ๊ฒฐ๊ณผ๊ฐ€ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

 

๋งˆ์ง€๋ง‰์œผ๋กœ DELETE ๋ฉ”์†Œ๋“œ์˜ ๊ฒฝ์šฐ ์•„๊นŒ ์ „๋‹ฌ๋ฐ›์€ ID๋ฅผ ์ฃผ์†Œ ๋’ค์— ๋„ฃ๊ณ  ์‹คํ–‰ํ–ˆ์„ ๋•Œ ์•„๋ฌด๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค๋ฉด 204 No Content ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ์•„๋ฌด๋Ÿฐ ๋‚ด์šฉ์—†์ด ์ถœ๋ ฅํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—๋„ ์ž˜ ์‚ญ์ œ๊ฐ€ ๋˜๋Š” ๋ชจ์Šต์ด์ฃ .

 

 

 

 

 

 

๋งˆ์น˜๋ฉฐ...

์—ฌ๊ธฐ๊นŒ์ง€ FastAPI์™€ SQLAlchemy๋ฅผ ์ด์šฉํ•˜์—ฌ DB ์—ฐ๋™ ํ›„ API๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ดค์Šต๋‹ˆ๋‹ค. 

 

SQLAlchemy์˜ ๋‚ด์šฉ์€ ์ด๋ณด๋‹ค ๋” ๋ฐฉ๋Œ€ํ•ฉ๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ•˜๋‚˜์˜ ํ…Œ์ด๋ธ”์„ ์ด์šฉํ•˜์˜€์ง€๋งŒ ๋‘ ๊ฐœ ์ด์ƒ์˜ ๋ณตํ•ฉ ํ…Œ์ด๋ธ”์„ ์ด์šฉํ•œ๋‹ค๋ฉด ์„ค๊ณ„๋ฅผ ํ•  ๋•Œ ์ข€ ๋” ์‹ฌ์˜คํ•˜๊ณ  ๋น ๋ฅธ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•œ ์„ค๊ณ„๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

 

๊ฐœ์ธ์ ์œผ๋กœ Marshmallow๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ๋Š” orm_mode๊ฐ€ ๋ณ„๋„๋กœ ์ œ๊ณต๋˜์–ด ์žˆ์ง€ ์•Š์•„ JSON์ด๋‚˜ dict๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ ์œ„ํ•œ ์ถ”์ƒ ํ•จ์ˆ˜๋ฅผ ๋ณ„๋„๋กœ ์žฌ๊ตฌํ˜„ ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ˜น์€ JSONEncoder๋ฅผ ๋ณ„๋„๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ๋„ ํ•˜๋‚˜์˜ ๋ฐฉ๋ฒ•์ด์ฃ .

 

ํ•˜์ง€๋งŒ Pydantic์€ ์ด๋Ÿฌํ•œ JSON, dict ๋ณ€ํ™˜์— ๋Œ€ํ•œ ๋กœ์ง์„ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ณ„๋„๋กœ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„ SQLAlchemy๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ฐ์ฒด์— ๋งž๊ฒŒ ์ž๋™์œผ๋กœ ์ฝ”๋“œ ํ•œ ์ค„๋งŒ ์ž‘์„ฑํ•ด์„œ ๋ฐ˜ํ™˜ ๋ชจ๋ธ์„ JSON์œผ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๋กœ์ง์„ ์ œ๊ณตํ•ด์ฃผ๋Š” ์ ์€ ๊ต‰์žฅํžˆ ํŽธํ–ˆ์Šต๋‹ˆ๋‹ค.

 

๋˜ํ•œ Depends ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•ด์ฃผ์–ด DB ์„ธ์…˜์— ๋Œ€ํ•œ ๊ด€๋ฆฌ๊ฐ€ ์ข€ ๋” ํŽธํ–ˆ๋Š”๋ฐ์š”. ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ด์šฉํ•˜๊ฑฐ๋‚˜ decorator ๋“ฑ์„ ์ด์šฉํ•ด์„œ๋„ ๋น„์Šทํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์ž๊ฐ€ App context์˜ ๋ผ์ดํ”„ ์‚ฌ์ดํด์— ๋งž์ถฐ์„œ ๊ณ ๋ คํ•ด์•ผ ํ•˜๋Š” ์ ์ด ๋Š˜ ํฐ ๊ณผ์ œ์˜€๋Š”๋ฐ, ์ด๋ฅผ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ํ•จ์ˆ˜๋กœ ์ œ๊ณตํ•ด์ฃผ๋Š” ๊ฒƒ์€ ๊ฐœ๋ฐœ์ž๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ ๊ฐœ๋ฐœ์—์„œ๋งŒ ์‹ ๊ฒฝ์“ฐ๋„๋ก ๋…ธ๋ ฅํ–ˆ๋‹ค๋Š” ์ ์— ๋ˆˆ์— ๋„์—ˆ์Šต๋‹ˆ๋‹ค.

 

ํ•œ ๊ฐ€์ง€ ๋” ๋ฐ”๋ผ๋Š” ์ ์ด ์žˆ๋‹ค๋ฉด ์•„์ง๊นŒ์ง€ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•ด ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๊ฐ€ ๋ฏธํกํ•˜๋‹ค๋Š” ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  ORM ๋ ˆ๋ฒจ์—์„œ๋„ ๊ทธ ์ง€์›์ด ๋ฏธํกํ•˜๋‹ค๋Š” ์ ์ด ์žˆ์ง€๋งŒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ธ์Šคํ„ด์Šค์—์„œ๋„ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์€ ์ฐธ ์•„์‰ฌ์šด ์ด์•ผ๊ธฐ ์ž…๋‹ˆ๋‹ค. Java ์ง„์˜์—์„œ๋Š” R2DBC๋ผ๋Š” ์ปค๋„ฅ์…˜์„ ์ด์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ ˆ๋ฒจ์—์„œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์ง€์›ํ•˜์ง€๋งŒ ์•„์ง๊นŒ์ง€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฏธ๋“ค์›จ์–ด์—์„œ ์ง€์›ํ•˜์ง€ ์•Š์•„ ๋งŽ์€ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ด์ ์„ ๋ˆ„๋ฆด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

 

Python ์ง„์˜์—์„œ๋„ SQLAlchemy๊ฐ€ 1.4๋ถ€ํ„ฐ ๋น„๋™๊ธฐ ์ง€์›์„ ํ•˜๊ณ  ์žˆ๊ณ , ํ˜„์žฌ ๋ฒ ํƒ€ ๋ฒ„์ „์ด ๋ฆด๋ฆฌ์ฆˆ๋œ ์ƒํƒœ์ž…๋‹ˆ๋‹ค. ๋น„๋™๊ธฐ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ๋งŽ์ด ๋ณดํŽธํ™” ๋˜์—ˆ๊ธด ํ–ˆ์ง€๋งŒ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€์˜ ์—ฐ๊ฒฐ์—์„œ ๋น„๋™๊ธฐ๊ฐ€ ์ง€์›๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์— ์žˆ์–ด์„œ ์•„์‰ฌ์šด ์ ์ด ๋งŽ์ด ์žˆ๋Š”๋ฐ์š”. ์ฐจํ›„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ๋„ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๊ฐ€ ์•ˆ์ •์ ์œผ๋กœ ๋  ์ˆ˜ ์žˆ๋Š” ๋‚ ์ด ์™”์œผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

 

 

 

 

๋ฐ˜์‘ํ˜•
TAGS.

Tistory Comments