FastAPI는 빠르고 간단하게 API 서버를 만들 수 있게 해주는 파이썬 웹 프레임워크이다.
| 특징 | 설명 |
|---|---|
| 빠르다 | 이름처럼 빠른 속도를 자랑한다. (비동기 처리도 가능) |
| 자동 문서 생성 | API 문서를 자동으로 만들어준다. (/docs에 들어가면 Swagger UI가 짠!) |
| 타입 힌트 기반 | Python 타입 힌트로 유효성 검사와 문서까지 처리해준다. |
| JSON 쉽게 처리 | API는 주로 JSON 데이터를 주고받는데, FastAPI는 이것을 쉽게 해준다. |
| 테스트 용이 | Postman이나 Swagger로 바로바로 테스트 가능 |
타입 힌트(Type Hint)란?
파이썬에서 이 변수나 함수는 어떤 자료형(타입)을 써야 하는지 미리 알려주는 표시이다.
FastAPI를 사용하려면 먼저 FastAPI 패키지를 설치해야한다.
pip install fastapi
FastAPI 애플리케이션을 실행하기 위해 uvicorn이라는 ASGI 서버가 필요하다.
Uvicorn은 Python으로 작성된 가볍고 빠른 웹 서버로, 비동기 웹 애플리케이션과 API를 실행하는 데 사용된다.
개발할 때는 자동 리로드 기능으로 편리하게 작업할 수 있고, 운영 환경에서는 다중 프로세스로 높은 성능을 제공한다.
pip install "uvicorn[standard]"
uvicorn[standard]
1. 이 패키지는 uvicorn에 기본적으로 포함된 기능 외에도 추가적인 표준 미들웨어를 포함한다.
2. 표준 미들웨어는 보안, 로깅 및 기타 서버 관련 기능을 추가하는 데 도움이 된다.
3. ex) Gzip 압축, CORS(Cross-Origin Resource Sharing) 지원 등이 기본적으로 포함되어 있다.
uvicorn
1. 기본적으로 필요한 최소한의 기능만을 제공하는 패키지이다.
2. uvicorn[standard]에 비해 미들웨어가 적고, 기본적인 서버 기능만을 제공한다.
ASGI(Asynchronous Server Gateway Interface) 서버는 Python에서 비동기 웹 애플리케이션을 실행할 수 있게 해주는 서버이다.
| 구분 | 설명 |
|---|---|
| WSGI | 오래된 방식, 동기 방식 웹 서버 규칙 (예: Django, Flask) |
| ASGI | 최신 방식, 비동기 처리 가능 (예: FastAPI, Starlette 등) |
FastAPI 같은 최신 웹 프레임워크가 ASGI 서버와 잘 어울리며, 대표적인 서버로 Uvicorn과 Daphne가 있다.
→ 많은 요청을 동시에 처리하고, 실시간 기능이 필요한 앱을 쉽게 만들 수 있다.
myapp/
├── main.py
├── static/
│ └── script.js
├── templates/
│ └── index.html
main.py
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
app = FastAPI()
# http://localhost:8080/static
# 정적 파일 제공
app.mount("/static", StaticFiles(directory="static"), name="static")
# HTML 템플릿 제공
templates = Jinja2Templates(directory="templates")
@app.get("/", response_class=HTMLResponse)
async def get_page(request: Request):
return templates.TemplateResponse("index.html", {"request":request})
@app.get("/api/data")
async def get_data():
return {"message": "FastAPI에서 보내는 데이터입니다"}
script.js
document.addEventListener("DOMContentLoaded", () => {
fetch("/api/data")
.then((response) => response.json())
.then((data) => {
document.getElementById("result").textContent = data.message;
})
.catch((error) => {
document.getElementById("result").textContent = "데이터 불러오기 실패";
console.error(error);
});
});
index.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>FastAPI Frontend</title>
<script defer src="/static/script.js"></script>
</head>
<body>
<h1>프론트엔드 페이지</h1>
<p id="result">데이터를 불러오는 중...</p>
</body>
</html>
uvicorn main:app --reload
FastAPI는 자동으로 생성된 Swagger UI를 통해 API 문서를 제공한다.
(http://127.0.0.1:8000/docs 또는 http://127.0.0.1:8000/redoc 에서 확인 가능)
→ Swagger UI를 통해 API의 엔드포인트, 매개변수, 응답 형식 등을 살펴볼 수 있다.
REST(Representational State Transfer) : 어떻게 자원(웹에서 주고받는 데이터)을 주고받을지 정해놓은 규칙
| 규칙 | 설명 |
|---|---|
| URI는 자원을 표현 | /users, /posts/3 |
| HTTP 메서드로 동작 표현 | GET, POST, PUT, DELETE 등 |
| 상태 없이 동작 | 요청할 때마다 독립적이어야 함 (세션X) |
| JSON 형식으로 데이터 전달 | 대부분 JSON 사용 |
RESTful API : REST 아키텍처를 따르는 웹 서비스의 인터페이스.
클라이언트와 서버 간의 통신을 위한 규칙을 제공하며, 자원의 생성, 조회, 수정, 삭제(CRUD)와 같은 기본적인 데이터 조작을 위한 메서드를 제공한다.
FastAPI에서는 각 메서드에 대응하는 데코레이터를 제공하여 해당 엔드포인트에 대한 핸들러 함수를 등록할 수 있다.
→ 데코레이터를 사용하여 각각의 HTTP 메서드에 대한 동작을 정의할 수 있다.
server.py
from fastapi import FastAPI
from pydantic import BaseModel
users = {
0: {"userid": "apple", "name": "김사과"},
1: {"userid": "banana", "name": "반하나"},
2: {"userid": "orange", "name": "오렌지"}
}
application = FastAPI()
@application.get("/users/{id}")
def find_user(id: int):
user = users.get(id)
if user is None:
return {"error": "해당 id 없음"}
return user
@application.get("/users/{id}/{key}") # http://127.0.0.1:8000/users/2/userid # 경로를 통해 보냄
def find_user_by_key(id:int, key:str):
user = users.get(id)
if user is None or key not in user:
return {"error": "잘못된 id 또는 key"}
return user[key]
@application.get("/id-by-name") # 쿼리를 통해 보냄
def find_user_by_name(name: str):
for idx, user in users.items():
if user["name"]==name:
return user
return {"error": "데이터를 찾지 못함"}
class User(BaseModel):
userid: str
name: str
@application.post("/users/{id}")
def create_user(id: int, user: User):
if id in users:
return {"error": "이미 존재하는 키"}
users[id] = user.model_dump()
return {"success": "ok"}
# 사용자 수정
class UserForUpdate(BaseModel):
userid: str | None = None
name: str | None = None
@application.put("/users/{id}")
def update_user(id: int, user: UserForUpdate):
if id not in users:
return {"error": "id가 존재하지 않음"}
if user.userid is not None:
users[id]["userid"] = user.userid
if user.name is not None:
users[id]["name"] = user.name
return {"success": "ok"}
# 사용자 삭제
@application.delete("/users/{id}")
def delete_user(id: int):
if id not in users:
return {"error": "존재하지 않는 사용자"}
users.pop(id)
return {"success": "ok"}
실행
uvicorn server:application --reload
ad_client.py
import gradio as gr
import requests
url = "http://127.0.0.1:8000/create_ad"
def generate_ad(product_name, details, tone_and_manner):
try:
response = requests.post(url, json={
"product_name": product_name,
"details": details,
"tone_and_manner": ", ".join(tone_and_manner)
})
result = response.json()
ad = result["ad"]
datas = result["datas"]
processed_datas = [[d['product_name'], d['details'], d['tone_and_manner'], d['ad']] for d in datas]
return ad, processed_datas
except:
return "서버 연결 실패!", None
with gr.Blocks(title="광고 문구 생성기") as demo:
gr.Markdown("광고 문구를 생성해주는 AI 서비스앱")
with gr.Row():
product_input = gr.Textbox(label="제품 이름", placeholder="예: 천연 치약")
details_input = gr.Textbox(label="주요 내용", placeholder="자극 없는 천연 원료 치약")
tone_options = gr.CheckboxGroup(
label="광고 문구의 느낌",
choices=["기본", "재밌게", "과장스럽게", "참신하게", "고급스럽게", "센스있게", "신선하게", "친근하게", "전문성있게"],
value=["기본"]
)
generate_btn = gr.Button("광고 문구 생성하기")
output_ad = gr.Textbox(label="생성된 광고 문구", lines=3)
output_table = gr.DataFrame(label="세부 데이터", headers=["key", "value"], wrap=True)
generate_btn.click(fn=generate_ad, inputs=[product_input, details_input, tone_options], outputs=[output_ad, output_table])
demo.launch()
실행
python ad_client.py
ad_server.py
import os
from fastapi import FastAPI
from pydantic import BaseModel
from pymongo import MongoClient
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
client = OpenAI(api_key=os.getenv("API_KEY"))
url = os.getenv("DB_URL")
dbconn = MongoClient(url)
database = dbconn['aiproject']
collection = database['ad']
class AdGenerator:
def __init__(self, engine='gpt-4.1-nano-2025-04-14'):
self.engine = engine
def using_llm(self, prompt):
system_instruction = 'assistant는 마케팅 문구 작성 도우미로 동작한다. user의 내용을 참고하여 마케팅 문구를 작성해라'
messages = [{"role": "system", "content": system_instruction}, {"role": "user", "content": prompt}]
response = client.chat.completions.create(
model=self.engine,
messages=messages
)
return response.choices[0].message.content
def generate(self, product_name, details, tone_and_manner):
prompt = f'제품 이름: {product_name}\n주요내용: {details}\n 광고 문구의 스타일: {tone_and_manner} 위 내용을 참고하여 마케팅 문구를 만들어라'
result = self.using_llm(prompt=prompt)
return result
app = FastAPI()
class Product(BaseModel):
product_name: str
details: str
tone_and_manner: str
@app.post("/create_ad")
async def create_ad(product: Product):
ad_generator = AdGenerator()
ag = ad_generator.generate(product_name=product.product_name, details=product.details, tone_and_manner=product.tone_and_manner)
data_insert = {'product_name': product.product_name, 'details':product.details, 'tone_and_manner':product.tone_and_manner, 'ad': ag}
result = collection.insert_one(data_insert)
result = collection.find({})
datas = []
for data in result:
data.pop('_id', None)
datas.append(data)
return {'ad': ag, 'datas': datas}
실행
uvicorn ad_server:app --reload