๐Ÿ’ป ์‹ค์ „ ํ”„๋กœ๊ทธ๋ž˜๋ฐ | FastAPI๋กœ ๋งŒ๋“œ๋Š” ๊ฐ„๋‹จํ•œ Todo List ์•ฑ


์•ˆ๋…•ํ•˜์„ธ์š”! ์˜ค๋Š˜์€ FastAPI๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•œ Todo List ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. FastAPI๋Š” ์ง€๋‚œ ๋ช‡ ๋…„๊ฐ„ Python ๊ฐœ๋ฐœ์ž๋“ค ์‚ฌ์ด์—์„œ ๋งค์šฐ ์ธ๊ธฐ๊ฐ€ ๋†’์•„์ง„ ์›น ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, API ๊ตฌ์ถ•์— ์ตœ์ ํ™”๋œ ์„ฑ๋Šฅ๊ณผ ํŽธ๋ฆฌํ•œ ์‚ฌ์šฉ์„ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

FastAPI๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

FastAPI๋Š” ๋น„๋™๊ธฐ์„ฑ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋†’์€ ์„ฑ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” Python์˜ ์ƒˆ๋กœ์šด ์›น ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. ๋” ๋‚˜์€ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ์ œ๊ณตํ•˜๋ฉฐ, Pydantic์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Todo List ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์ถ•

์ด์ œ FastAPI๋กœ Todo List ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“œ๋Š” ๊ณผ์ •์„ ๋‹จ๊ณ„๋ณ„๋กœ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ํ”„๋กœ์ ํŠธ๋Š” CRUD(Create, Read, Update, Delete) ๊ธฐ๋Šฅ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ ์„ค์ •

  • ๋จผ์ € ํ”„๋กœ์ ํŠธ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ๊ฐ€์ƒ ํ™˜๊ฒฝ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • ํ•„์ˆ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ FastAPI์™€ Uvicorn, Jinja2 ๋“ฑ์„ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.
mkdir TODO
cd TODO
python -m venv venv
source venv/bin/activate  # Windows์—์„œ๋Š” 'venv\Scripts\activate'
pip install fastapi uvicorn jinja2 python-multipart

FastAPI ์„œ๋ฒ„ ๊ตฌ์ถ•

๋‹ค์Œ์œผ๋กœ, FastAPI์˜ ๊ธฐ๋ณธ ์„œ๋ฒ„๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. main.py ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜์—ฌ ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

from fastapi import FastAPI
app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

uvicorn main:app --reload

์ด์ œ ๊ธฐ๋ณธ์ ์œผ๋กœ API๊ฐ€ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. http://localhost:8000์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ™ˆํŽ˜์ด์ง€ ํ…œํ”Œ๋ฆฟ ์ž‘์„ฑ

Jinja2 ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•ด์„œ Todo ๋ชฉ๋ก์„ ๋ณด์—ฌ์ค„ HTML ํŒŒ์ผ์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TODO LIST</title>
</head>
<body>
    <nav class="navbar bg-body-tertiary">
        <div class="container-fluid">
            <h1 class="navbar-brand">My To Do list</h1>
        </div>
    </nav>
    <br>
    <table class="table">
        <tr>
            <th>ToDo</th>
            <th>Options</th>
        </tr>
        {% for id in tododict %}
        <tr>
            <td>{{ tododict[id] }}</td>
            <td><a href="/delete/{{ id }}"><button class="btn btn-danger">Delete</button></a></td>
        </tr>
        {% endfor %}
        <tr>
            <form method="post" action="/add">
                <td><input type="text" name="newtodo" required></td>
                <td style="text-align: center;"><button type="submit" class="btn btn-primary">Add New</button></td>
            </form>
        </tr>
    </table>
</body>
</html>

CRUD ๊ธฐ๋Šฅ ๊ตฌํ˜„

๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์€ CRUD ๊ธฐ๋Šฅ์„ ํ†ตํ•œ ๋ฐ์ดํ„ฐ ์กฐ์ž‘์ž…๋‹ˆ๋‹ค. database.json ํŒŒ์ผ์„ ์ด์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ์ฝ์–ด์˜ต๋‹ˆ๋‹ค.

from fastapi import FastAPI, Request
from fastapi.responses import RedirectResponse
from fastapi.templating import Jinja2Templates
import json

app = FastAPI()
templates = Jinja2Templates(directory="templates")

@app.get("/")
async def root(request: Request):
    with open('database.json') as f:
        data = json.load(f)
    return templates.TemplateResponse("todolist.html", {"request": request, "tododict": data})

@app.get("/delete/{id}")
async def delete_todo(request: Request, id: str):
    with open('database.json') as f:
        data = json.load(f)
    del data[id]
    with open('database.json', 'w') as f:
        json.dump(data, f)
    return RedirectResponse("/", 303)

@app.post("/add")
async def add_todo(request: Request):
    with open('database.json') as f:
        data = json.load(f)
    formdata = await request.form()
    newdata = {str(len(data) + 1): formdata["newtodo"]}
    data.update(newdata)
    with open('database.json', 'w') as f:
        json.dump(data, f)
    return RedirectResponse("/", 303)

๊ฒฐ๋ก 

FastAPI๋ฅผ ํ†ตํ•ด ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‰ฝ๊ฒŒ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด ๊ณผ์ •์—์„œ API ๋ฐ ์›น ๊ธฐ์ˆ ์— ๋Œ€ํ•œ ๊นŠ์€ ์ดํ•ด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ ์˜ˆ์ œ๋ฅผ ๋ฐœ์ „์‹œ์ผœ ์‚ฌ์šฉ์ž ์ธ์ฆ ์‹œ์Šคํ…œ์ด๋‚˜ ๋” ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜์—ฌ ๊ฐ•๋ ฅํ•œ ์›น ์„œ๋น„์Šค๋ฅผ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋Œ“๊ธ€

๊ฐ€์žฅ ๋งŽ์ด ๋ณธ ๊ธ€