FastAPI์ ๊ฐ๋ ฅํ ์์กด์ฑ ์ฃผ์ ์์คํ ์ ์ฌ์ธต ๋ถ์ํฉ๋๋ค. ๊ฒฌ๊ณ ํ API ๊ฐ๋ฐ์ ์ํ ๊ณ ๊ธ ๊ธฐ์ , ์ปค์คํ ์์กด์ฑ, ์ค์ฝํ ๋ฐ ํ ์คํธ ์ ๋ต์ ํ์ตํ์ธ์.
FastAPI ์์กด์ฑ ์์คํ : ๊ณ ๊ธ ์์กด์ฑ ์ฃผ์
FastAPI์ ์์กด์ฑ ์ฃผ์ (DI) ์์คํ ์ ๋ชจ๋ํ, ํ ์คํธ ์ฉ์ด์ฑ ๋ฐ ์ฌ์ฌ์ฉ์ฑ์ ์ด์งํ๋ ์ค๊ณ์ ์ด์์ ๋๋ค. ๊ธฐ๋ณธ์ ์ธ ์ฌ์ฉ๋ฒ์ ๊ฐ๋จํ์ง๋ง, ๊ณ ๊ธ DI ๊ธฐ์ ์ ์๋ฌํ๋ฉด ์๋นํ ๋ฅ๋ ฅ๊ณผ ์ ์ฐ์ฑ์ ์ป์ ์ ์์ต๋๋ค. ์ด ๊ธ์์๋ FastAPI์ ๊ณ ๊ธ ์์กด์ฑ ์ฃผ์ ์ ๋ํด ์ฌ์ธต์ ์ผ๋ก ๋ค๋ฃจ๋ฉฐ, ์ฌ์ฉ์ ์ ์ ์์กด์ฑ, ์ค์ฝํ, ํ ์คํธ ์ ๋ต ๋ฐ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ํฌํจํฉ๋๋ค.
๊ธฐ๋ณธ ์๋ฆฌ ์ดํด
๊ณ ๊ธ ์ฃผ์ ๋ก ๋์ด๊ฐ๊ธฐ ์ ์ FastAPI ์์กด์ฑ ์ฃผ์ ์ ๊ธฐ๋ณธ ์ฌํญ์ ๋น ๋ฅด๊ฒ ์ ๋ฆฌํด ๋ณด๊ฒ ์ต๋๋ค.
- ํจ์๋ก์์ ์์กด์ฑ: ์์กด์ฑ์ ์ผ๋ฐ์ ์ธ ํ์ด์ฌ ํจ์๋ก ์ ์ธ๋ฉ๋๋ค.
- ์๋ ์ฃผ์ : FastAPI๋ ํ์ ํํธ์ ๊ธฐ๋ฐํ์ฌ ์ด ์์กด์ฑ๋ค์ ๊ฒฝ๋ก ์์ ์ผ๋ก ์๋ ์ฃผ์ ํฉ๋๋ค.
- ๊ณ์ฝ์ผ๋ก์์ ํ์ ํํธ: ํ์ ํํธ๋ ์์กด์ฑ ๋ฐ ๊ฒฝ๋ก ์์ ํจ์์ ๋ํ ์์ ์ ๋ ฅ ํ์ ์ ์ ์ํฉ๋๋ค.
- ๊ณ์ธต์ ์์กด์ฑ: ์์กด์ฑ์ ๋ค๋ฅธ ์์กด์ฑ์ ์์กดํ ์ ์์ผ๋ฉฐ, ์์กด์ฑ ํธ๋ฆฌ๋ฅผ ์์ฑํฉ๋๋ค.
๋ค์์ ๊ฐ๋จํ ์์์ ๋๋ค.
from fastapi import FastAPI, Depends
app = FastAPI()
def get_db():
db = {"items": []}
try:
yield db
finally:
# Close the connection if needed
pass
@app.get("/items/")
async def read_items(db: dict = Depends(get_db)):
return db["items"]
์ด ์์์์, get_db๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ์ ์ ๊ณตํ๋ ์์กด์ฑ์
๋๋ค. FastAPI๋ get_db๋ฅผ ์๋์ผ๋ก ํธ์ถํ๊ณ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ read_items ํจ์์ ์ฃผ์
ํฉ๋๋ค.
๊ณ ๊ธ ์์กด์ฑ ๊ธฐ์
1. ํด๋์ค๋ฅผ ์์กด์ฑ์ผ๋ก ์ฌ์ฉํ๊ธฐ
ํจ์๊ฐ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ๋์ง๋ง, ํด๋์ค๋ ์์กด์ฑ์ผ๋ก ์ฌ์ฉ๋ ์ ์์ด ๋ ๋ณต์กํ ์ํ ๊ด๋ฆฌ์ ๋ฉ์๋๋ฅผ ํ์ฉํฉ๋๋ค. ์ด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ, ์ธ์ฆ ์๋น์ค ๋๋ ์ด๊ธฐํ ๋ฐ ์ ๋ฆฌ๊ฐ ํ์ํ ๊ธฐํ ๋ฆฌ์์ค๋ฅผ ๋ค๋ฃฐ ๋ ํนํ ์ ์ฉํฉ๋๋ค.
from fastapi import FastAPI, Depends
app = FastAPI()
class Database:
def __init__(self):
self.connection = self.create_connection()
def create_connection(self):
# Simulate a database connection
print("Creating database connection...")
return {"items": []}
def close(self):
# Simulate closing a database connection
print("Closing database connection...")
def get_db():
db = Database()
try:
yield db.connection
finally:
db.close()
@app.get("/items/")
async def read_items(db: dict = Depends(get_db)):
return db["items"]
์ด ์์์์ Database ํด๋์ค๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ๋ก์ง์ ์บก์ํํฉ๋๋ค. get_db ์์กด์ฑ์ Database ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ ์ฐ๊ฒฐ์ yieldํฉ๋๋ค. finally ๋ธ๋ก์ ์์ฒญ์ด ์ฒ๋ฆฌ๋ ํ ์ฐ๊ฒฐ์ด ์ฌ๋ฐ๋ฅด๊ฒ ๋ซํ๋๋ก ๋ณด์ฅํฉ๋๋ค.
2. ์์กด์ฑ ์ค๋ฒ๋ผ์ด๋ฉ
FastAPI๋ ์์กด์ฑ์ ์ค๋ฒ๋ผ์ด๋ํ ์ ์๋๋ก ํ์ฉํ๋ฉฐ, ์ด๋ ํ ์คํธ ๋ฐ ๊ฐ๋ฐ์ ๋งค์ฐ ์ค์ํฉ๋๋ค. ์ค์ ์์กด์ฑ์ ๋ชจ์(mock) ๊ฐ์ฒด๋ ์คํ (stub)์ผ๋ก ๋์ฒดํ์ฌ ์ฝ๋๋ฅผ ๊ฒฉ๋ฆฌํ๊ณ ์ผ๊ด๋ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฅํ ์ ์์ต๋๋ค.
from fastapi import FastAPI, Depends
app = FastAPI()
# Original dependency
def get_settings():
# Simulate loading settings from a file or environment
return {"api_key": "real_api_key"}
@app.get("/items/")
async def read_items(settings: dict = Depends(get_settings)):
return {"api_key": settings["api_key"]}
# Override for testing
def get_settings_override():
return {"api_key": "test_api_key"}
app.dependency_overrides[get_settings] = get_settings_override
# To revert back to the original:
# del app.dependency_overrides[get_settings]
์ด ์์์์ get_settings ์์กด์ฑ์ get_settings_override๋ก ์ค๋ฒ๋ผ์ด๋๋ฉ๋๋ค. ์ด๋ฅผ ํตํด ํ
์คํธ ๋ชฉ์ ์ผ๋ก ๋ค๋ฅธ API ํค๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
3. ์์ฒญ ์ค์ฝํ ๋ฐ์ดํฐ์ `contextvars` ์ฌ์ฉํ๊ธฐ
contextvars๋ ์ปจํ
์คํธ ๋ก์ปฌ ๋ณ์๋ฅผ ์ ๊ณตํ๋ ํ์ด์ฌ ๋ชจ๋์
๋๋ค. ์ด๋ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด, ์์ฒญ ID ๋๋ ์ถ์ ๋ฐ์ดํฐ์ ๊ฐ์ ์์ฒญ๋ณ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๋ฐ ์ ์ฉํฉ๋๋ค. FastAPI์ ์์กด์ฑ ์ฃผ์
๊ณผ ํจ๊ป contextvars๋ฅผ ์ฌ์ฉํ๋ฉด ์ ํ๋ฆฌ์ผ์ด์
์ ์ฒด์์ ์ด ๋ฐ์ดํฐ์ ์ก์ธ์คํ ์ ์์ต๋๋ค.
import contextvars
from fastapi import FastAPI, Depends, Request
app = FastAPI()
# Create a context variable for the request ID
request_id_var = contextvars.ContextVar("request_id")
# Middleware to set the request ID
@app.middleware("http")
async def add_request_id(request: Request, call_next):
request_id = str(uuid.uuid4())
request_id_var.set(request_id)
response = await call_next(request)
response.headers["X-Request-ID"] = request_id
return response
# Dependency to access the request ID
def get_request_id():
return request_id_var.get()
@app.get("/items/")
async def read_items(request_id: str = Depends(get_request_id)):
return {"request_id": request_id}
์ด ์์์์ ๋ฏธ๋ค์จ์ด๋ ๊ฐ ๋ค์ด์ค๋ ์์ฒญ์ ๋ํด ๊ณ ์ ํ ์์ฒญ ID๋ฅผ ์ค์ ํฉ๋๋ค. get_request_id ์์กด์ฑ์ contextvars ์ปจํ
์คํธ์์ ์์ฒญ ID๋ฅผ ๊ฒ์ํฉ๋๋ค. ์ด๋ฅผ ํตํด ์ ํ๋ฆฌ์ผ์ด์
์ ๋ฐ์์ ์์ฒญ์ ์ถ์ ํ ์ ์์ต๋๋ค.
4. ๋น๋๊ธฐ ์์กด์ฑ
FastAPI๋ ๋น๋๊ธฐ ์์กด์ฑ์ ์๋ฒฝํ๊ฒ ์ง์ํฉ๋๋ค. ์ด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ ๋๋ ์ธ๋ถ API ํธ์ถ๊ณผ ๊ฐ์ ๋น๋ธ๋กํน I/O ์์
์ ํ์์ ์
๋๋ค. ๋จ์ํ ์์กด์ฑ ํจ์๋ฅผ async def ํจ์๋ก ์ ์ํ๋ฉด ๋ฉ๋๋ค.
from fastapi import FastAPI, Depends
import asyncio
app = FastAPI()
async def get_data():
# Simulate an asynchronous operation
await asyncio.sleep(1)
return {"message": "Hello from async dependency!"}
@app.get("/items/")
async def read_items(data: dict = Depends(get_data)):
return data
์ด ์์์์ get_data ์์กด์ฑ์ ์ง์ฐ์ ์๋ฎฌ๋ ์ด์
ํ๋ ๋น๋๊ธฐ ํจ์์
๋๋ค. FastAPI๋ ๋น๋๊ธฐ ์์กด์ฑ์ ๊ฒฐ๊ณผ๋ฅผ read_items ํจ์์ ์ฃผ์
ํ๊ธฐ ์ ์ ์๋์ผ๋ก ๊ธฐ๋ค๋ฆฝ๋๋ค(awaits).
5. ๋ฆฌ์์ค ๊ด๋ฆฌ(๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ, ํ์ผ ํธ๋ค)๋ฅผ ์ํ ์ ๋๋ ์ดํฐ ์ฌ์ฉ
์ ๋๋ ์ดํฐ(yield ์ฌ์ฉ)๋ฅผ ์ฌ์ฉํ๋ฉด ์๋ ๋ฆฌ์์ค ๊ด๋ฆฌ๊ฐ ๊ฐ๋ฅํ์ฌ, ์ค๋ฅ๊ฐ ๋ฐ์ํ๋๋ผ๋ finally ๋ธ๋ก์ ํตํด ๋ฆฌ์์ค๊ฐ ์ ์ ํ๊ฒ ๋ซํ๊ฑฐ๋ ํด์ ๋จ์ ๋ณด์ฅํฉ๋๋ค.
from fastapi import FastAPI, Depends
app = FastAPI()
def get_file_handle():
try:
file_handle = open("my_file.txt", "r")
yield file_handle
finally:
file_handle.close()
@app.get("/file_content/")
async def read_file_content(file_handle = Depends(get_file_handle)):
content = file_handle.read()
return {"content": content}
์์กด์ฑ ์ค์ฝํ ๋ฐ ๋ผ์ดํ์ฌ์ดํด
์์กด์ฑ ์ค์ฝํ๋ฅผ ์ดํดํ๋ ๊ฒ์ ์์กด์ฑ์ ๋ผ์ดํ์ฌ์ดํด์ ๊ด๋ฆฌํ๊ณ ๋ฆฌ์์ค๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ํ ๋น ๋ฐ ํด์ ๋๋๋ก ๋ณด์ฅํ๋ ๋ฐ ์ค์ํฉ๋๋ค. FastAPI๋ ๋ค๋ฅธ DI ํ๋ ์์ํฌ(์: Spring์ `@RequestScope`, `@ApplicationScope`)์ฒ๋ผ ๋ช ์์ ์ธ ์ค์ฝํ ์ฃผ์์ ์ง์ ์ ๊ณตํ์ง๋ ์์ง๋ง, ์์กด์ฑ์ ์ ์ํ๊ณ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐฉ์์ ์กฐํฉ์ผ๋ก ์ ์ฌํ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ต๋๋ค.
์์ฒญ ์ค์ฝํ
์ด๊ฒ์ ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ์ค์ฝํ์
๋๋ค. ๊ฐ ์์ฒญ์ ์์กด์ฑ์ ์๋ก์ด ์ธ์คํด์ค๋ฅผ ๋ฐ์ต๋๋ค. ์ด๋ ์ผ๋ฐ์ ์ผ๋ก ์ด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์์์ ๋ณด์ฌ์ง ๊ฒ์ฒ๋ผ ์์กด์ฑ ํจ์ ๋ด์์ ์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ yieldํจ์ผ๋ก์จ ๋ฌ์ฑ๋ฉ๋๋ค. contextvars๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๋ ์์ฒญ ์ค์ฝํ๋ฅผ ๋ฌ์ฑํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
์ ํ๋ฆฌ์ผ์ด์ ์ค์ฝํ (์ฑ๊ธํค)
์์กด์ฑ์ ๋จ์ผ ์ธ์คํด์ค๊ฐ ์์ฑ๋์ด ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ผ์ดํ์ฌ์ดํด ๋์ ๋ชจ๋ ์์ฒญ์ ๊ฑธ์ณ ๊ณต์ ๋ฉ๋๋ค. ์ด๋ ์ข ์ข ์ ์ญ ๋ณ์ ๋๋ ํด๋์ค ์์ค ์์ฑ์ ์ฌ์ฉํ์ฌ ์ํ๋ฉ๋๋ค.
from fastapi import FastAPI, Depends
app = FastAPI()
# Singleton instance
GLOBAL_SETTING = {"api_key": "global_api_key"}
def get_global_setting():
return GLOBAL_SETTING
@app.get("/items/")
async def read_items(setting: dict = Depends(get_global_setting)):
return setting
๊ฐ๋ณ ์ํ๋ฅผ ๊ฐ์ง ์ ํ๋ฆฌ์ผ์ด์ ์ค์ฝํ ์์กด์ฑ์ ์ฌ์ฉํ ๋๋ ์ฃผ์ํด์ผ ํฉ๋๋ค. ํ ์์ฒญ์์ ๋ณ๊ฒฝ๋ ๋ด์ฉ์ด ๋ค๋ฅธ ์์ฒญ์ ์ํฅ์ ๋ฏธ์น ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์ ๋์ ์์ฒญ์ด ์๋ ๊ฒฝ์ฐ ๋๊ธฐํ ๋ฉ์ปค๋์ฆ(์ ๊ธ ๋ฑ)์ด ํ์ํ ์ ์์ต๋๋ค.
์ธ์ ์ค์ฝํ (์ฌ์ฉ์๋ณ ๋ฐ์ดํฐ)
์์กด์ฑ์ ์ฌ์ฉ์ ์ธ์ ๊ณผ ์ฐ๊ฒฐํฉ๋๋ค. ์ด๋ ์ธ์ ๊ด๋ฆฌ ๋ฉ์ปค๋์ฆ(์: ์ฟ ํค ๋๋ JWT ์ฌ์ฉ)์ ํ์๋ก ํ๋ฉฐ, ์ผ๋ฐ์ ์ผ๋ก ์ธ์ ๋ฐ์ดํฐ์ ์์กด์ฑ์ ์ ์ฅํ๋ ๊ฒ์ ํฌํจํฉ๋๋ค.
from fastapi import FastAPI, Depends, Cookie
from typing import Optional
import uuid
app = FastAPI()
# In a real app, store sessions in a database or cache
sessions = {}
async def get_user_id(session_id: Optional[str] = Cookie(None)) -> str:
if session_id is None or session_id not in sessions:
session_id = str(uuid.uuid4())
sessions[session_id] = {"user_id": str(uuid.uuid4())} # Assign a random user ID
return sessions[session_id]["user_id"]
@app.get("/profile/")
async def read_profile(user_id: str = Depends(get_user_id)):
return {"user_id": user_id}
์์กด์ฑ ํ ์คํธ
์์กด์ฑ ์ฃผ์ ์ ์ฃผ์ ์ด์ ์ค ํ๋๋ ํฅ์๋ ํ ์คํธ ์ฉ์ด์ฑ์ ๋๋ค. ๊ตฌ์ฑ ์์๋ฅผ ๋ถ๋ฆฌํจ์ผ๋ก์จ ํ ์คํธ ์ค์ ์์กด์ฑ์ ๋ชจ์ ๊ฐ์ฒด ๋๋ ์คํ ์ผ๋ก ์ฝ๊ฒ ๊ต์ฒดํ ์ ์์ต๋๋ค.
1. ํ ์คํธ์์ ์์กด์ฑ ์ค๋ฒ๋ผ์ด๋ฉ
์์ ์ค๋ช
ํ๋ฏ์ด, FastAPI์ dependency_overrides ๋ฉ์ปค๋์ฆ์ ํ
์คํธ์ ์ด์์ ์
๋๋ค. ์์ธก ๊ฐ๋ฅํ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ ๋ชจ์ ์์กด์ฑ์ ์์ฑํ๊ณ ์ด๋ฅผ ์ฌ์ฉํ์ฌ ํ
์คํธ ์ค์ธ ์ฝ๋๋ฅผ ๊ฒฉ๋ฆฌํ์ธ์.
from fastapi.testclient import TestClient
from fastapi import FastAPI, Depends
app = FastAPI()
# Original dependency
def get_external_data():
# Simulate fetching data from an external API
return {"data": "Real external data"}
@app.get("/data/")
async def read_data(data: dict = Depends(get_external_data)):
return data
# Test
from unittest.mock import MagicMock
def get_external_data_mock():
return {"data": "Mocked external data"}
def test_read_data():
app.dependency_overrides[get_external_data] = get_external_data_mock
client = TestClient(app)
response = client.get("/data/")
assert response.status_code == 200
assert response.json() == {"data": "Mocked external data"}
# Clean up overrides
app.dependency_overrides.clear()
2. ๋ชจ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ
unittest.mock๊ณผ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ชจ์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ๊ทธ ๋์์ ์ ์ดํ๊ธฐ ์ํ ๊ฐ๋ ฅํ ๋๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค. ๋ชจ์ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ๋ณต์กํ ์์กด์ฑ์ ์๋ฎฌ๋ ์ด์
ํ๊ณ ์ฝ๋๊ฐ ์ด๋ค๊ณผ ์ฌ๋ฐ๋ฅด๊ฒ ์ํธ ์์ฉํ๋์ง ํ์ธํ ์ ์์ต๋๋ค.
import unittest
from unittest.mock import MagicMock
# (Define the FastAPI app and get_external_data as above)
class TestReadData(unittest.TestCase):
def test_read_data_with_mock(self):
# Create a mock for the get_external_data dependency
mock_get_external_data = MagicMock(return_value={"data": "Mocked data from unittest"})
# Override the dependency with the mock
app.dependency_overrides[get_external_data] = mock_get_external_data
client = TestClient(app)
response = client.get("/data/")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {"data": "Mocked data from unittest"})
# Assert that the mock was called
mock_get_external_data.assert_called_once()
# Clean up overrides
app.dependency_overrides.clear()
3. ๋จ์ ํ ์คํธ๋ฅผ ์ํ ์์กด์ฑ ์ฃผ์ (FastAPI ์ปจํ ์คํธ ์ธ๋ถ)
API ์๋ํฌ์ธํธ ํธ๋ค๋ฌ ์ธ๋ถ์ ํจ์๋ฅผ ๋จ์ ํ
์คํธํ ๋๋ ์์กด์ฑ ์ฃผ์
์์น์ ์ฌ์ ํ ์ ์ฉ๋ฉ๋๋ค. FastAPI์ Depends์ ์์กดํ๋ ๋์ , ํ
์คํธ ์ค์ธ ํจ์์ ์์กด์ฑ์ ์๋์ผ๋ก ์ฃผ์
ํ์ธ์.
# Example function to test
def process_data(data_source):
data = data_source.fetch_data()
# ... process the data ...
return processed_data
class MockDataSource:
def fetch_data(self):
return {"example": "data"}
# Unit test
def test_process_data():
mock_data_source = MockDataSource()
result = process_data(mock_data_source)
# Assertions on the result
์์กด์ฑ ์ฃผ์ ์ ํตํ ๋ณด์ ๊ณ ๋ ค ์ฌํญ
์์กด์ฑ ์ฃผ์ ์ ์ ์ตํ์ง๋ง, ์ ์คํ๊ฒ ๊ตฌํ๋์ง ์์ผ๋ฉด ์ ์ฌ์ ์ธ ๋ณด์ ๋ฌธ์ ๋ฅผ ์ผ๊ธฐํ ์ ์์ต๋๋ค.
1. ์์กด์ฑ ํผ๋ (Dependency Confusion)
์ ๋ขฐํ ์ ์๋ ์์ค์์ ์์กด์ฑ์ ๊ฐ์ ธ์ค๋์ง ํ์ธํ์ธ์. ํจํค์ง ๋ฌด๊ฒฐ์ฑ์ ๊ฒ์ฆํ๊ณ , ์ทจ์ฝ์ ์ค์บ๋ ๊ธฐ๋ฅ์ด ์๋ ํจํค์ง ๊ด๋ฆฌ์๋ฅผ ์ฌ์ฉํ์ธ์. ์ด๋ ์ผ๋ฐ์ ์ธ ์ํํธ์จ์ด ๊ณต๊ธ๋ง ๋ณด์ ์์น์ด์ง๋ง, DI๋ ๋ค์ํ ์์ค์์ ๊ตฌ์ฑ ์์๋ฅผ ์ฃผ์ ํ ์ ์์ผ๋ฏ๋ก ์ด ๋ฌธ์ ๊ฐ ๋์ฑ ์ฌํ๋ฉ๋๋ค.
2. ์ ์ฑ ์์กด์ฑ ์ฃผ์
์ ์ ํ ์ ํจ์ฑ ๊ฒ์ฌ ์์ด ์ธ๋ถ ์ ๋ ฅ์ ํ์ฉํ๋ ์์กด์ฑ์ ์ฃผ์ํ์ธ์. ๊ณต๊ฒฉ์๋ ์์๋ ์์กด์ฑ์ ํตํด ์ ์ฑ ์ฝ๋๋ ๋ฐ์ดํฐ๋ฅผ ์ฃผ์ ํ ์ ์์ต๋๋ค. ๋ชจ๋ ์ฌ์ฉ์ ์ ๋ ฅ์ ์ ํํ๊ณ ๊ฐ๋ ฅํ ์ ํจ์ฑ ๊ฒ์ฌ ๋ฉ์ปค๋์ฆ์ ๊ตฌํํ์ธ์.
3. ์์กด์ฑ์ ํตํ ์ ๋ณด ์ ์ถ
์์กด์ฑ์ด ๋ฏผ๊ฐํ ์ ๋ณด๋ฅผ ์๋์น ์๊ฒ ๋ ธ์ถํ์ง ์๋๋ก ํ์ธํ์ธ์. ์ ์ฌ์ ์ธ ์ ๋ณด ์ ์ถ ์ทจ์ฝ์ ์ ์๋ณํ๊ธฐ ์ํด ์์กด์ฑ์ ์ฝ๋์ ๊ตฌ์ฑ์ ๊ฒํ ํ์ธ์.
4. ํ๋์ฝ๋ฉ๋ ๋น๋ฐ
๋น๋ฐ(API ํค, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ํธ ๋ฑ)์ ์์กด์ฑ ์ฝ๋์ ์ง์ ํ๋์ฝ๋ฉํ๋ ๊ฒ์ ํผํ์ธ์. ํ๊ฒฝ ๋ณ์ ๋๋ ๋ณด์ ๊ตฌ์ฑ ๊ด๋ฆฌ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋น๋ฐ์ ์ ์ฅํ๊ณ ๊ด๋ฆฌํ์ธ์.
import os
from fastapi import FastAPI, Depends
app = FastAPI()
def get_api_key():
api_key = os.environ.get("API_KEY")
if not api_key:
raise ValueError("API_KEY environment variable not set.")
return api_key
@app.get("/secure_endpoint/")
async def secure_endpoint(api_key: str = Depends(get_api_key)):
# Use api_key for authentication/authorization
return {"message": "Access granted"}
์์กด์ฑ ์ฃผ์ ์ ํตํ ์ฑ๋ฅ ์ต์ ํ
์์กด์ฑ ์ฃผ์ ์ ํ๋ช ํ๊ฒ ์ฌ์ฉํ์ง ์์ผ๋ฉด ์ฑ๋ฅ์ ์ํฅ์ ๋ฏธ์น ์ ์์ต๋๋ค. ๋ค์์ ๋ช ๊ฐ์ง ์ต์ ํ ์ ๋ต์ ๋๋ค.
1. ์์กด์ฑ ์์ฑ ๋น์ฉ ์ต์ํ
๊ฐ๋ฅํ๋ค๋ฉด ๋ชจ๋ ์์ฒญ๋ง๋ค ๋น์ฉ์ด ๋ง์ด ๋๋ ์์กด์ฑ์ ์์ฑํ๋ ๊ฒ์ ํผํ์ธ์. ์์กด์ฑ์ด ๋ฌด์ํ(stateless)์ด๊ฑฐ๋ ์ฌ๋ฌ ์์ฒญ์์ ๊ณต์ ๋ ์ ์๋ค๋ฉด, ์ฑ๊ธํค ์ค์ฝํ๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ ์์กด์ฑ ์ธ์คํด์ค๋ฅผ ์บ์ฑํ๋ ๊ฒ์ ๊ณ ๋ คํ์ธ์.
2. ์ง์ฐ ์ด๊ธฐํ
ํ์ํ ๋๋ง ์์กด์ฑ์ ์ด๊ธฐํํ์ธ์. ์ด๋ ํนํ ๋ง์ ์์กด์ฑ์ ๊ฐ์ง ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฝ์ฐ ์์ ์๊ฐ๊ณผ ๋ฉ๋ชจ๋ฆฌ ์๋น๋ฅผ ์ค์ผ ์ ์์ต๋๋ค.
3. ์์กด์ฑ ๊ฒฐ๊ณผ ์บ์ฑ
๊ฒฐ๊ณผ๊ฐ ์ฌ์ฌ์ฉ๋ ๊ฐ๋ฅ์ฑ์ด ์๋ค๋ฉด ๋น์ฉ์ด ๋ง์ด ๋๋ ์์กด์ฑ ๊ณ์ฐ ๊ฒฐ๊ณผ๋ฅผ ์บ์ํ์ธ์. ์บ์ฑ ๋ฉ์ปค๋์ฆ(์: Redis, Memcached)์ ์ฌ์ฉํ์ฌ ์์กด์ฑ ๊ฒฐ๊ณผ๋ฅผ ์ ์ฅํ๊ณ ๊ฒ์ํ์ธ์.
4. ์์กด์ฑ ๊ทธ๋ํ ์ต์ ํ
์ ์ฌ์ ์ธ ๋ณ๋ชฉ ํ์์ ์๋ณํ๊ธฐ ์ํด ์์กด์ฑ ๊ทธ๋ํ๋ฅผ ๋ถ์ํ์ธ์. ๊ฐ๋ฅํ๋ค๋ฉด ์์กด์ฑ ๊ตฌ์กฐ๋ฅผ ๋จ์ํํ๊ณ ์์กด์ฑ ์๋ฅผ ์ค์ด์ธ์.
5. I/O ๋ฐ์ด๋ ์์ ์ ์ํ ๋น๋๊ธฐ ์์กด์ฑ
๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ ๋๋ ์ธ๋ถ API ํธ์ถ๊ณผ ๊ฐ์ ๋ธ๋กํน I/O ์์ ์ ์ํํ ๋๋ ๋น๋๊ธฐ ์์กด์ฑ์ ์ฌ์ฉํ์ธ์. ์ด๋ ๋ฉ์ธ ์ค๋ ๋๋ฅผ ์ฐจ๋จํ๋ ๊ฒ์ ๋ฐฉ์งํ๊ณ ์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์ ์๋ต์ฑ์ ํฅ์์ํต๋๋ค.
FastAPI ์์กด์ฑ ์ฃผ์ ์ ์ํ ๋ชจ๋ฒ ์ฌ๋ก
- ์์กด์ฑ์ ๋จ์ํ๊ฒ ์ ์ง: ๋จ์ผ ์์ ์ ์ํํ๋ ์๊ณ ์ง์ค์ ์ธ ์์กด์ฑ์ ๋ชฉํ๋ก ํ์ธ์. ์ด๋ ๊ฐ๋ ์ฑ, ํ ์คํธ ์ฉ์ด์ฑ ๋ฐ ์ ์ง ๋ณด์์ฑ์ ํฅ์์ํต๋๋ค.
- ํ์ ํํธ ์ฌ์ฉ: ํ์ ํํธ๋ฅผ ํ์ฉํ์ฌ ์์กด์ฑ์ ์์ ์ ๋ ฅ ๋ฐ ์ถ๋ ฅ ํ์ ์ ๋ช ํํ๊ฒ ์ ์ํ์ธ์. ์ด๋ ์ฝ๋ ๋ช ํ์ฑ์ ๋์ด๊ณ FastAPI๊ฐ ์ ์ ํ์ ๊ฒ์ฌ๋ฅผ ์ํํ ์ ์๋๋ก ํฉ๋๋ค.
- ์์กด์ฑ ๋ฌธ์ํ: ๊ฐ ์์กด์ฑ์ ๋ชฉ์ ๊ณผ ์ฌ์ฉ๋ฒ์ ๋ฌธ์ํํ์ธ์. ์ด๋ ๋ค๋ฅธ ๊ฐ๋ฐ์๊ฐ ์ฝ๋๋ฅผ ์ฌ์ฉํ๊ณ ์ ์ง ๋ณด์ํ๋ ๋ฐฉ๋ฒ์ ์ดํดํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
- ์์กด์ฑ์ ์ฒ ์ ํ ํ ์คํธ: ์์กด์ฑ์ด ์์๋๋ก ์๋ํ๋์ง ํ์ธํ๊ธฐ ์ํด ๋จ์ ํ ์คํธ๋ฅผ ์์ฑํ์ธ์. ์ด๋ ๋ฒ๊ทธ๋ฅผ ๋ฐฉ์งํ๊ณ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ๋ฐ์ ์ธ ์ ๋ขฐ์ฑ์ ํฅ์์ํค๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
- ์ผ๊ด๋ ๋ช ๋ช ๊ท์น ์ฌ์ฉ: ์ฝ๋ ๊ฐ๋ ์ฑ์ ๋์ด๊ธฐ ์ํด ์์กด์ฑ์ ์ผ๊ด๋ ๋ช ๋ช ๊ท์น์ ์ฌ์ฉํ์ธ์.
- ์ํ ์์กด์ฑ ํผํ๊ธฐ: ์ํ ์์กด์ฑ์ ๋ณต์กํ๊ณ ๋๋ฒ๊ทธํ๊ธฐ ์ด๋ ค์ด ์ฝ๋๋ก ์ด์ด์ง ์ ์์ต๋๋ค. ์ํ ์์กด์ฑ์ ์ ๊ฑฐํ๊ธฐ ์ํด ์ฝ๋๋ฅผ ๋ฆฌํฉํ ๋งํ์ธ์.
- ์์กด์ฑ ์ฃผ์ ์ปจํ ์ด๋ ๊ณ ๋ ค (์ ํ ์ฌํญ): FastAPI์ ๋ด์ฅ ์์กด์ฑ ์ฃผ์ ์ ๋๋ถ๋ถ์ ๊ฒฝ์ฐ์ ์ถฉ๋ถํ์ง๋ง, ๋ ๋ณต์กํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฝ์ฐ ์ ์ฉ ์์กด์ฑ ์ฃผ์ ์ปจํ ์ด๋(์: `inject`, `autowire`) ์ฌ์ฉ์ ๊ณ ๋ คํด ๋ณด์ธ์.
๊ฒฐ๋ก
FastAPI์ ์์กด์ฑ ์ฃผ์
์์คํ
์ ๋ชจ๋ํ, ํ
์คํธ ์ฉ์ด์ฑ ๋ฐ ์ฌ์ฌ์ฉ์ฑ์ ์ด์งํ๋ ๊ฐ๋ ฅํ ๋๊ตฌ์
๋๋ค. ํด๋์ค๋ฅผ ์์กด์ฑ์ผ๋ก ์ฌ์ฉํ๊ณ , ์์กด์ฑ์ ์ค๋ฒ๋ผ์ด๋ํ๋ฉฐ, contextvars๋ฅผ ์ฌ์ฉํ๋ ๋ฑ์ ๊ณ ๊ธ ๊ธฐ์ ์ ์๋ฌํจ์ผ๋ก์จ ๊ฒฌ๊ณ ํ๊ณ ํ์ฅ ๊ฐ๋ฅํ API๋ฅผ ๊ตฌ์ถํ ์ ์์ต๋๋ค. ์์กด์ฑ ์ค์ฝํ์ ๋ผ์ดํ์ฌ์ดํด์ ์ดํดํ๋ ๊ฒ์ ๋ฆฌ์์ค๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๋ฐ ์ค์ํฉ๋๋ค. ์ ํ๋ฆฌ์ผ์ด์
์ ์ ๋ขฐ์ฑ๊ณผ ๋ณด์์ ๋ณด์ฅํ๊ธฐ ์ํด ์์กด์ฑ์ ํญ์ ์ฒ ์ ํ ํ
์คํธํ๋ ๊ฒ์ ์ฐ์ ์ํ์ญ์์ค. ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋ฐ๋ฅด๊ณ ์ ์ฌ์ ์ธ ๋ณด์ ๋ฐ ์ฑ๋ฅ ์ํฅ์ ๊ณ ๋ คํจ์ผ๋ก์จ FastAPI ์์กด์ฑ ์ฃผ์
์์คํ
์ ๋ชจ๋ ์ ์ฌ๋ ฅ์ ํ์ฉํ ์ ์์ต๋๋ค.