๐Ÿง  FastAPI์˜ ์ƒํƒœ ๊ด€๋ฆฌ์™€ ์˜์กด์„ฑ ์ฃผ์ž… ์™„๋ฒฝ ๊ฐ€์ด๋“œ

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

์ด์ „ ๊ธ€ ๋ณด๊ธฐ(FastAPI์—์„œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ์™„๋ฒฝ ์ดํ•ด)

โ€“ ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ๋ถ€ํ„ฐ Depends ํ™œ์šฉ๊นŒ์ง€, ์‹ค์ „ ์‚ฌ๋ก€ ์ค‘์‹ฌ ์„ค๋ช…

โ€œ์ฝ”๋“œ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก ์˜์กด์„ฑ์€ ๊ด€๋ฆฌ๊ฐ€ ์•„๋‹ˆ๋ผ ํ†ต์ œ์˜ ๋Œ€์ƒ์ด๋‹ค.โ€
FastAPI๋Š” ์†Œ๊ทœ๋ชจ ์‹ค์Šต๋ถ€ํ„ฐ ๋Œ€ํ˜• ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜๊นŒ์ง€ ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ๊ทธ ํ•ต์‹ฌ์ด ๋ฐ”๋กœ **์ƒํƒœ ๊ด€๋ฆฌ(State Management)**์™€ **์˜์กด์„ฑ ์ฃผ์ž…(Dependency Injection, DI)**์ด๋‹ค.


โœ… ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ๋ž€?

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

1. ์˜ˆ: ์ „์—ญ ๋ณ€์ˆ˜ ์‚ฌ์šฉ

from fastapi import FastAPI

app = FastAPI()

counter = 0  # ์ „์—ญ ๋ณ€์ˆ˜

@app.get("/increase")
def increase():
    global counter
    counter += 1
    return {"counter": counter}
  • counter๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ๋™์•ˆ ์œ ์ง€๋œ๋‹ค.
  • ํ•˜์ง€๋งŒ ์ด ๋ฐฉ์‹์€ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ/๋ฉ€ํ‹ฐํ”„๋กœ์„ธ์Šค ํ™˜๊ฒฝ์—์„œ๋Š” ์•ˆ์ •์ ์ด์ง€ ์•Š๋‹ค.

2. FastAPI ๋‚ด๋ถ€ ์ƒํƒœ ์ €์žฅ์†Œ ํ™œ์šฉ

FastAPI์˜ app.state ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ „์—ญ ๊ฐ์ฒด๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.

@app.on_event("startup")
def startup_event():
    app.state.message = "Hello, FastAPI!"

@app.get("/message")
def read_message():
    return {"message": app.state.message}
  • startup_event๋Š” ์„œ๋ฒ„ ์‹คํ–‰ ์‹œ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋จ
  • app.state๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•จ
  • ์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ DB ์—ฐ๊ฒฐ ๊ฐ์ฒด๋„ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Œ

๐Ÿงฉ ์˜์กด์„ฑ ์ฃผ์ž…(Dependency Injection)์˜ ๊ฐœ๋…

Dependency Injection์ด๋ž€, ํ•„์š”ํ•œ ์ž์›(๊ฐ์ฒด, ํ•จ์ˆ˜, ํด๋ž˜์Šค ๋“ฑ)์„ ์™ธ๋ถ€์—์„œ ์ฃผ์ž…๋ฐ›๋Š” ๋ฐฉ์‹์œผ๋กœ ์ฝ”๋“œ ์˜์กด์„ฑ์„ ์ค„์ด๋Š” ํŒจํ„ด์ด๋‹ค.

FastAPI๋Š” Depends()๋ฅผ ํ†ตํ•ด ์ด ๊ธฐ๋Šฅ์„ ์•„์ฃผ ๊น”๋”ํ•˜๊ฒŒ ๊ตฌํ˜„ํ•œ๋‹ค.


๐Ÿ”ง Depends ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

Depends๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ, FastAPI๊ฐ€ ์ž๋™์œผ๋กœ ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์„œ ๋ฐ˜ํ™˜๊ฐ’์„ ์ฃผ์ž…ํ•ด์ค€๋‹ค.

1. ์˜ˆ์‹œ: ๊ณตํ†ต ์ธ์ฆ ๋กœ์ง ์žฌ์‚ฌ์šฉ

from fastapi import Depends, HTTPException

def verify_token(token: str = "my-token"):
    if token != "my-token":
        raise HTTPException(status_code=401, detail="Unauthorized")
    return token

@app.get("/secure-data")
def read_secure_data(token: str = Depends(verify_token)):
    return {"data": "This is secured"}
  • verify_token() ํ•จ์ˆ˜๊ฐ€ ๋จผ์ € ์‹คํ–‰๋˜๊ณ , ๊ทธ ๊ฒฐ๊ณผ๊ฐ€ token ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ฃผ์ž…๋จ
  • ์‹คํŒจ ์‹œ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ ๋ผ์šฐํŠธ ์ ‘๊ทผ์„ ์ฐจ๋‹จํ•จ
  • ์ด ๊ตฌ์กฐ๋Š” ์ธ์ฆ/๊ถŒํ•œ ๊ฒ€์‚ฌ, ๋กœ๊ทธ ์ฒ˜๋ฆฌ, DB ์—ฐ๊ฒฐ ์ฃผ์ž… ๋“ฑ์— ํ™œ์šฉ ๊ฐ€๋Šฅ

๐Ÿ” ๋‹ค์–‘ํ•œ DI ์˜ˆ์ œ

2. ํŒŒ๋ผ๋ฏธํ„ฐ ์žˆ๋Š” ์˜์กด์„ฑ

def get_settings(env: str = "dev"):
    return {"env": env}

@app.get("/settings")
def read_settings(settings: dict = Depends(get_settings)):
    return settings
  • ์˜์กด์„ฑ ํ•จ์ˆ˜๋„ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๊ณ , FastAPI๋Š” ์•Œ์•„์„œ ๊ฐ’์„ ๋ฐ”์ธ๋”ฉํ•ด์ค€๋‹ค.

3. ํด๋ž˜์Šค ๊ธฐ๋ฐ˜ ์˜์กด์„ฑ

ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•˜๋ฉด ๋” ๊ฐ•๋ ฅํ•œ ์ƒํƒœ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

class Config:
    def __init__(self):
        self.db_url = "sqlite:///./test.db"
        self.debug = True

def get_config():
    return Config()

@app.get("/config")
def read_config(config: Config = Depends(get_config)):
    return {"db_url": config.db_url, "debug": config.debug}
  • ๊ฐ์ฒด๋ฅผ ํ•œ ๋ฒˆ ์ƒ์„ฑํ•˜๊ณ  ์žฌ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ํ•„์š” ์‹œ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด ์ค„ ์ˆ˜๋„ ์žˆ๋‹ค.
  • ๋ณดํ†ต ์ด ํŒจํ„ด์€ ์„ค์ •, DB ์ปค๋„ฅ์…˜, ์„œ๋น„์Šค ๊ฐ์ฒด ๋“ฑ์—์„œ ์‚ฌ์šฉํ•œ๋‹ค.

๐Ÿ”„ ์˜์กด์„ฑ์˜ ์ค‘์ฒฉ๊ณผ ์žฌ๊ท€ ํ˜ธ์ถœ

์˜์กด์„ฑ์€ ์ค‘์ฒฉ ํ˜ธ์ถœ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค. ์ด๊ฑด ๊ทœ๋ชจ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก ๋งค์šฐ ์œ ์šฉํ•œ ํŒจํ„ด์ด๋‹ค.

def get_db():
    db = "DB ์ปค๋„ฅ์…˜ ๊ฐ์ฒด"  # ์˜ˆ์‹œ
    return db

def get_user(db = Depends(get_db)):
    return {"user_id": 123, "db": db}

@app.get("/me")
def read_me(user = Depends(get_user)):
    return user
  • read_me()๋Š” get_user()๋ฅผ ๋จผ์ € ํ˜ธ์ถœํ•˜๊ณ , ๊ทธ ๋‚ด๋ถ€์—์„œ get_db()๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
  • FastAPI๊ฐ€ ์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„๋ฅผ ์ž๋™ ๋ถ„์„ํ•ด์„œ ์‹คํ–‰ ์ˆœ์„œ๋ฅผ ์ •ํ•ด์ค€๋‹ค.
  • ์ด๋Ÿฐ ๊ธฐ๋Šฅ์€ ํƒ€ ํ”„๋ ˆ์ž„์›Œํฌ์—์„  ์ง์ ‘ ๊ตฌํ˜„ํ•˜๊ธฐ ๊ฝค ๋ฒˆ๊ฑฐ๋กญ๋‹ค.

๐Ÿ’พ ์ „์—ญ DI๋กœ ์ „ํ™˜ํ•˜๋Š” ํŒ

๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Depends๋ฅผ ์ „์—ญ ๋ชจ๋“ˆ์ฒ˜๋Ÿผ ์ •๋ฆฌํ•ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

  1. dependencies.py ํŒŒ์ผ ์ƒ์„ฑ
  2. ๊ณตํ†ต ์˜์กด์„ฑ ํ•จ์ˆ˜๋ฅผ ์ •์˜
  3. ๊ฐ ๋ผ์šฐํŠธ์—์„œ import ํ›„ Depends๋กœ ์ฃผ์ž…
# dependencies.py
def get_logger():
    return {"log": "Logging object"}

# main.py
from dependencies import get_logger

@app.get("/log")
def read_log(log = Depends(get_logger)):
    return log

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์„ฑ์ด ์˜ฌ๋ผ๊ฐ€๊ณ , ํ…Œ์ŠคํŠธ๋„ ํ›จ์”ฌ ์‰ฌ์›Œ์ง„๋‹ค.


๐Ÿงพ ๋งˆ๋ฌด๋ฆฌ ์š”์•ฝ

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

๊ธฐ๋Šฅ์„ค๋ช…์‹ค์ „ ์˜ˆ
์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ์•ฑ ์ „์ฒด์—์„œ ๊ณต์œ ํ•˜๋Š” ๋ฐ์ดํ„ฐ ์ €์žฅDB ์—ฐ๊ฒฐ, ์„ค์ • ๊ฐ์ฒด ๋“ฑ
Depends()ํ•จ์ˆ˜/๊ฐ์ฒด ์ฃผ์ž…์„ ์ž๋™ ์ฒ˜๋ฆฌ์ธ์ฆ, ๋กœ๊น…, ์„œ๋น„์Šค ๊ณ„์ธต
์ค‘์ฒฉ DI์˜์กด์„ฑ์„ ๋˜ ๋‹ค๋ฅธ ์˜์กด์„ฑ์กด์„ฑ์„ ๋˜ ๋‹ค๋ฅธ ์˜์กด์„ฑ์œผ๋กœ ์žฌ๊ท€์  ๊ตฌ์„ฑget_user(Depends(get_db))

์ด์ œ ์ด ๊ตฌ์กฐ๋ฅผ ์ตํžˆ๋ฉด, ์‹ค๋ฌด์—์„œ ์ˆ˜์‹ญ ๊ฐœ ๋ผ์šฐํŠธ๋ฅผ ๊ตฌ์„ฑํ•˜๋ฉด์„œ๋„ ์ผ๊ด€๋œ ๋ฐฉ์‹์œผ๋กœ ๋ฆฌ์†Œ์Šค๋ฅผ ์ฃผ์ž…ํ•˜๊ณ  ์œ ์ง€๋ณด์ˆ˜๋ฅผ ํŽธํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.

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

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