๐Ÿš€ FastAPI์—์„œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ์™„๋ฒฝ ์ดํ•ดํ•˜๊ธฐ

๐Ÿ•’ ์•ฝ 2๋ถ„ ์ฝ๋Š” ๋ฐ ์†Œ์š”๋ฉ๋‹ˆ๋‹ค

์ด์ „ ๊ธ€ ๋ณด๊ธฐ(FastAPI์—์„œ์˜ Request์™€ Response ์™„๋ฒฝ ์ดํ•ด)

โ€“ async/await ๋ฌธ๋ฒ•๊ณผ ๋น„๋™๊ธฐ ํ•จ์ˆ˜์˜ ์ฐจ์ด๋ฅผ ์˜ˆ์ œ๋กœ ๋ฐฐ์šฐ๊ธฐ

“FastAPI๋Š” ์™œ ์ด๋ ‡๊ฒŒ ๋น ๋ฅผ๊นŒ?”
๊ทธ ๋น„๋ฐ€์€ ๋ฐ”๋กœ ๋น„๋™๊ธฐ(Async) ๊ธฐ๋ฐ˜์˜ ์•„ํ‚คํ…์ฒ˜์— ์žˆ๋‹ค.

FastAPI๋Š” ๋น„๋™๊ธฐ I/O ์ฒ˜๋ฆฌ์— ํŠนํ™”๋œ Python์˜ ์ตœ์‹  ๊ธฐ๋Šฅ์„ ์ ๊ทน ํ™œ์šฉํ•œ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋Œ€๊ทœ๋ชจ ํŠธ๋ž˜ํ”ฝ๋„ ๋น ๋ฅด๊ณ  ์•ˆ์ •์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.


๐Ÿ” ๋น„๋™๊ธฐ๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

์šฐ๋ฆฌ๊ฐ€ ํ”ํžˆ ์ž‘์„ฑํ•˜๋Š” Python ์ฝ”๋“œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋™๊ธฐ(Synchronous) ๋ฐฉ์‹์ด๋‹ค. ์ž‘์—…์ด ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰๋˜๊ณ , ํ•˜๋‚˜์˜ ์ž‘์—…์ด ๋๋‚˜์•ผ ๋‹ค์Œ ์ž‘์—…์ด ์‹œ์ž‘๋œ๋‹ค.

ํ•˜์ง€๋งŒ ์›น ์„œ๋ฒ„์—์„œ๋Š” ์ด๋Ÿฐ ๋ฐฉ์‹์ด ๋น„ํšจ์œจ์ ์ผ ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ๋‚˜ ์™ธ๋ถ€ API ์š”์ฒญ์ฒ˜๋Ÿผ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…์ด ์žˆ๋‹ค๋ฉด, ๊ทธ ์‹œ๊ฐ„ ๋™์•ˆ ์„œ๋ฒ„๊ฐ€ ์•„๋ฌด ์ผ๋„ ๋ชป ํ•˜๊ณ  ๊ฐ€๋งŒํžˆ ์žˆ๊ฒŒ ๋œ๋‹ค.

โœ… ๋น„๋™๊ธฐ๋Š” ์ด๋ ‡๊ฒŒ ํ•ด๊ฒฐํ•ด์ค˜

  • ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋Š” await ํ‚ค์›Œ๋“œ๋ฅผ ํ†ตํ•ด ๋‹ค๋ฅธ ์ž‘์—…์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋Œ€์‹ , ๊ทธ ์‹œ๊ฐ„ ๋™์•ˆ ๋‹ค๋ฅธ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค.
  • CPU๋ฅผ ํšจ์œจ์ ์œผ๋กœ ํ™œ์šฉํ•˜๊ณ , ๋ณ‘๋ ฌ์„ฑ์ด ํ–ฅ์ƒ๋œ๋‹ค.

๐Ÿงช async / await ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

FastAPI๋Š” ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด Python์˜ async/await ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•œ๋‹ค.
์šฐ์„  ๋ฌธ๋ฒ•๋ถ€ํ„ฐ ์‚ดํŽด๋ณด์ž.

1. ๊ธฐ๋ณธ ์˜ˆ์‹œ

import asyncio

async def say_hello():
    await asyncio.sleep(1)
    print("Hello after 1 second")
  • async def๋กœ ์„ ์–ธ๋œ ํ•จ์ˆ˜๋Š” ๋น„๋™๊ธฐ ํ•จ์ˆ˜์ด๋‹ค.
  • await๋Š” ๋น„๋™๊ธฐ ์ž‘์—…์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ํ‚ค์›Œ๋“œ.
  • asyncio.sleep(1)์€ ์‹ค์ œ๋กœ 1์ดˆ ๋™์•ˆ ๊ธฐ๋‹ค๋ฆฌ์ง€๋งŒ, ๊ทธ ์‚ฌ์ด์— ๋‹ค๋ฅธ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿงฌ FastAPI ์—์„œ ๋น„๋™๊ธฐ ํ•จ์ˆ˜ ์‚ฌ์šฉ

FastAPI๋Š” ๋ผ์šฐํŠธ ํ•จ์ˆ˜์—์„œ async def๋ฅผ ์“ฐ๋Š” ๊ฑธ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ง€์›ํ•œ๋‹ค. ๊ทธ๋Ÿผ ์‹ค์ œ API ์˜ˆ์ œ๋ฅผ ๋ณด์ž.

from fastapi import FastAPI
import asyncio

app = FastAPI()

@app.get("/wait")
async def wait_response():
    await asyncio.sleep(2)
    return {"message": "2์ดˆ ๊ธฐ๋‹ค๋ ธ์–ด์š”!"}
  • ์ด API์— ์ ‘์†ํ•˜๋ฉด 2์ดˆ ํ›„์— ์‘๋‹ต์ด ๋Œ์•„์˜จ๋‹ค.
  • ์ค‘์š”ํ•œ ๊ฑด ๋‹ค๋ฅธ ํด๋ผ์ด์–ธํŠธ๋Š” ์ด ์‹œ๊ฐ„ ๋™์•ˆ์—๋„ ์š”์ฒญ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ.
  • ์ฆ‰, **๋™์‹œ์„ฑ(concurrency)**์ด ์ƒ๊ธด๋‹ค.

โš™ ์ผ๋ฐ˜ ํ•จ์ˆ˜ vs ๋น„๋™๊ธฐ ํ•จ์ˆ˜

์ด์ œ ๋™๊ธฐ ํ•จ์ˆ˜์™€ ๋น„๋™๊ธฐ ํ•จ์ˆ˜์˜ ์ฐจ์ด์ ์„ ๋น„๊ตํ•ด๋ณด์ž.

1. ๋™๊ธฐ ํ•จ์ˆ˜ (blocking)

import time

@app.get("/sync")
def sync_route():
    time.sleep(2)  # CPU๊ฐ€ ์ด ๋™์•ˆ ๋Œ€๊ธฐ
    return {"message": "๋™๊ธฐ ์‘๋‹ต"}
  • time.sleep()์€ CPU ์ž์ฒด๋ฅผ 2์ดˆ ๋™์•ˆ ๋ถ™์žก๊ณ  ์žˆ์–ด์„œ ๋‹ค๋ฅธ ์š”์ฒญ๋„ ๋ง‰ํžŒ๋‹ค.
  • ์œ ์ €๊ฐ€ ๋งŽ์•„์ง€๋ฉด ๋ณ‘๋ชฉ ํ˜„์ƒ์ด ์ƒ๊ธด๋‹ค.

2. ๋น„๋™๊ธฐ ํ•จ์ˆ˜ (non-blocking)

import asyncio

@app.get("/async")
async def async_route():
    await asyncio.sleep(2)  # CPU๋Š” ๋‹ค๋ฅธ ์ผ๋„ ๊ฐ€๋Šฅ
    return {"message": "๋น„๋™๊ธฐ ์‘๋‹ต"}
  • asyncio.sleep()์€ CPU๋ฅผ ์ ์œ ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ์ž‘์—…์„ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ์ž‘์—…: DB I/O, API ์š”์ฒญ, ํŒŒ์ผ ์ฝ๊ธฐ ๋“ฑ

๐Ÿšจ ์–ธ์ œ async๋ฅผ ์จ์•ผ ํ• ๊นŒ?

1. ๋น„๋™๊ธฐ I/O๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ

  • ์™ธ๋ถ€ API ํ˜ธ์ถœ
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ (async ๋“œ๋ผ์ด๋ฒ„ ์‚ฌ์šฉ ์‹œ)
  • ํŒŒ์ผ ์ž…์ถœ๋ ฅ
  • ๋Œ€๊ธฐ์‹œ๊ฐ„์ด ๊ธด ์ž‘์—…

2. ๋น„๋™๊ธฐ๋ฅผ ์“ฐ๋ฉด ์•ˆ ๋˜๋Š” ๊ฒฝ์šฐ

  • CPU ๋ฐ”์šด๋“œ ์ž‘์—… (ex. ๋ณต์žกํ•œ ์ˆ˜ํ•™ ๊ณ„์‚ฐ)
  • await๋ฅผ ์•ˆ ์“ฐ๋Š” ํ•จ์ˆ˜ โ†’ ๊ตณ์ด async def๋กœ ๋งŒ๋“ค ํ•„์š” ์—†์Œ

์ฆ‰, ๋น„๋™๊ธฐ๋Š” ๊ธฐ๋‹ค๋ฆผ์ด ๋งŽ์€ ์ž‘์—…์— ๊ฐ•ํ•˜๊ณ , ๋ณต์žกํ•œ ์—ฐ์‚ฐ์—๋Š” ์ ํ•ฉํ•˜์ง€ ์•Š๋‹ค.


โฑ FastAPI ๋น„๋™๊ธฐ ์˜ˆ์‹œ: ์™ธ๋ถ€ API ํ˜ธ์ถœ

์˜ˆ๋ฅผ ๋“ค์–ด ์™ธ๋ถ€ ๋‚ ์”จ API์—์„œ ์ •๋ณด๋ฅผ ๋ฐ›์•„์˜ค๋Š” API๋ฅผ ๋งŒ๋“ ๋‹ค๊ณ  ํ•ด๋ณด์ž.

import httpx
from fastapi import FastAPI

app = FastAPI()

@app.get("/weather")
async def get_weather():
    async with httpx.AsyncClient() as client:
        response = await client.get("https://api.weatherapi.com/...")  # ์˜ˆ์‹œ
        return response.json()
  • httpx.AsyncClient()๋Š” ๋น„๋™๊ธฐ HTTP ํด๋ผ์ด์–ธํŠธ
  • await client.get()์€ HTTP ์š”์ฒญ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์คŒ
  • ๋Œ€๊ทœ๋ชจ ํ˜ธ์ถœ์ด ํ•„์š”ํ•œ ์ƒํ™ฉ์—์„  ํšจ๊ณผ๊ฐ€ ๊ทน๋Œ€ํ™”๋จ

๐Ÿ’ก ๋น„๋™๊ธฐ DB ๋“œ๋ผ์ด๋ฒ„์™€์˜ ์—ฐ๋™

FastAPI์˜ ์ง„์งœ ํž˜์€ ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๋•Œ ๊ทน๋Œ€ํ™”๋œ๋‹ค.

  • ์˜ˆ: Databases, Tortoise ORM, SQLModel (async ์ง€์›)
  • ๋น„๋™๊ธฐ DB ๋“œ๋ผ์ด๋ฒ„(ex. asyncpg, aiomysql)์™€ ํ•จ๊ป˜ ์“ฐ๋ฉด ์ˆ˜์ฒœ ๊ฐœ์˜ ๋™์‹œ ์ ‘์†๋„ ์•ˆ์ •์ ์œผ๋กœ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
from databases import Database

database = Database("sqlite+aiosqlite:///./test.db")

@app.on_event("startup")
async def startup():
    await database.connect()

@app.on_event("shutdown")
async def shutdown():
    await database.disconnect()

๐Ÿงพ ์š”์•ฝ: FastAPI์—์„œ ๋น„๋™๊ธฐ์˜ ์žฅ์ ์€?

ํ•ญ๋ชฉ๋™๊ธฐ(Sync)๋น„๋™๊ธฐ(Async)
์ฝ”๋“œ ์„ ์–ธdefasync def
๋Œ€๊ธฐ ์ค‘ CPU ์‚ฌ์šฉ์ ์œ ํ•จํ•ด์ œํ•จ
์ฒ˜๋ฆฌ ์†๋„๋А๋ฆผ (๋™์‹œ ์š”์ฒญ ์ œํ•œ)๋น ๋ฆ„ (๋™์‹œ ์š”์ฒญ ๊ฐ€๋Šฅ)
์‚ฌ์šฉ ๋Œ€์ƒ๊ฐ„๋‹จํ•œ ์—ฐ์‚ฐI/O ์ค‘์‹ฌ ์ž‘์—…

FastAPI๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋น„๋™๊ธฐ๋ฅผ ์ง€์›ํ•˜๋ฏ€๋กœ, ์ฒ˜์Œ๋ถ€ํ„ฐ ๋น„๋™๊ธฐ ์Šคํƒ€์ผ๋กœ API๋ฅผ ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. ๋‹จ, async ํ•จ์ˆ˜ ์•ˆ์—์„œ๋Š” ๋ฐ˜๋“œ์‹œ await ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ๋งŒ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ๋„ ๊ธฐ์–ตํ•ด ๋‘์ž.

FastAPI ๊ณต์‹ ๋ฌธ์„œ : https://fastapi.tiangolo.com/ko/

fastapi-logo,fastapi-๊ฐœ๋ฐœ-ํ™˜๊ฒฝ-์„ค์ •, fastapi-์•ฑ-๋งŒ๋“ค๊ธฐ, fastapi-๋ผ์šฐํŒ…, fastapi-request-response, fastapi-๋น„๋™๊ธฐ-์ฒ˜๋ฆฌ