
mkdir {폴더 이름}
cd {폴더 이름}
vscode에서 터미널 창을 열고 위 명령어들을 입력하여 폴더를 만들고 생성한 폴더로 이동한다.
python3 -m venv {가상 환경 이름}
source {가상 환경 이름}/bin/activate
venv를 사용하여 가상 환경을 생성하고 source 명령어를 통해 가상 환경을 활성화한다.
pip install fastapi uvicorn
가상 환경이 활성화된 상태에서 FastAPI와 Uvicorn을 설치한다.
Uvicorn은 ASGI 서버로, FastAPI 애플리케이션을 실행하는 데 사용된다.
**from fastapi import FastAPI**
**app = FastAPI()**
**@app.get("/")**
**async def read_root():**
**return {"Hello": "World"}**
main.py를 생성하고 간단한 FastAPI 앱을 생성해본다.
uvicorn main:app --reload
uvicorn 명령어로 FastAPI 서버를 실행한다. --reload 옵션은 코드 변경 시 자동으로 서버를 재시작해준다.
다시 시작할때
1.폴더로 이동
cd /Users/apple/back
2.가상환경 가동
source env/bin/activate
(터미널 앞에 (env) 뜨면 OK)
3 서버 실행
uvicorn main:app --reload
웹 서비스 구조
클라이언트(브라우저)
↓ HTTP 요청
서버(FastAPI)
↓ 처리 후
클라이언트로 응답
라우트(Route)의 의미
URL → 어떤 함수를 실행할지 연결하는 것.
예:
@app.get("/")
def root():
return {"message": "Hello World"}
브라우저에서 http://127.0.0.1:8000/ 입력
서버는 root() 함수를 실행
JSON 형태로 반환
GET은 브라우저 주소창에서 직접 요청하는 방식이며, URL 입력만으로 요청이 가능하다. POST는 form, JavaScript fetch, curl 등을 통해 데이터를 함께 보내는 방식이다. POST 방식은 body 영역에 JSON 데이터를 포함한다.
다음과 같이 URL 일부를 변수처럼 받을 수 있다.
@app.get("/hello/{name}")
사용 예시:
@app.get("/hello/{name}")
def say_hello(name: str):
return {"message": f"Hello, {name}"}
hello/steven 과 같은 형태로 요청할 수 있다.
FastAPI는 비동기를 지원하며 async/await를 통해 고속 처리가 가능하다. 비동기 처리는 요청이 많거나 시간이 걸리는 작업을 병렬 처리하기에 유리하다. 필수는 아니지만 현대 웹서버에서 널리 사용된다.
동일 URL에 대해 GET, POST와 같은 서로 다른 HTTP Method는 허용되지만 동일 URL에 대해 GET이 두 개 정의되는 것은 프레임워크마다 다다르게 처리된다. Django는 허용하지 않고, FastAPI는 가능하기는 하지만 권장되지 않는다.
FastAPI는 요청(Request)으로 들어오는 데이터를 딕셔너리(dict) 로 받을 수도 있지만,
대부분의 실제 서비스에서는 다음과 같은 요구가 생긴다.
데이터 구조가 복잡해짐 (예: id, name, publisher, code, status…)
데이터 타입 검증 필요 (정수/문자/날짜 등)
문서화(Swagger) 자동화
재사용 가능 구조 필요
이를 위해 Pydantic의 BaseModel을 사용한다.
from pydantic import BaseModel
class ToDo(BaseModel):
id: int
item: str
status: str = "pending"
이렇게 하면 API 함수에서는 더 이상 dict를 받지 않고:
@router.post("/todo")
def add(todo: ToDo):
...
처럼 클래스 타입으로 데이터를 받는다.
1.타입 검증 자동
id에 숫자 아닌 값이 들어오면 FastAPI가 자동으로 422 오류를 반환한다.
2.문서 자동 생성
Swagger UI(/docs)에서 자동으로 Model Schema가 표시된다.
3.코드 가독성 향상
딕셔너리를 보면 구조가 파악이 안 되지만,
클래스 모델은 구조가 명확하다.
4.데이터 자동 파싱
JSON → Python 객체로 자동 변환.
FastAPI는 URL에서 값을 전달하는 두 방식이 있다.
Path Parameter (URL 구조 일부)
GET /todo/1
@router.get("/todo/{id}")
def get_item(id: int):
return ...
특징:
리소스를 식별할 때 사용 (id)
URL 내에 반드시 포함됨
Query Parameter (선택 입력)
GET /search?name=park&age=20
@router.get("/search")
def search(name: str, age: int = 0):
return ...
특징:
선택 입력 가능
기본값 지정 가능
필터링/검색 등에 사용됨
POST/PUT 요청은 데이터가 URL이 아닌 HTTP Body에 실려온다.
FastAPI는 이를 자동 파싱해서 BaseModel로 받을 수 있다.
예:
POST /todo
{
"id": 1,
"item": "Python 공부",
"status": "ready"
}
@router.post("/todo")
def add_item(todo: ToDo):
return {"msg": "OK", "data": todo}
FastAPI Router는 기능별로 파일을 나눌 수 있다.
예:
todo.py
from fastapi import APIRouter
from pydantic import BaseModel
router = APIRouter()
class ToDo(BaseModel):
id: int
item: str
todos = []
@router.post("/todo")
def add(todo: ToDo):
todos.append(todo)
return {"message": "추가됨", "todo": todo}
@router.get("/todo/{id}")
def get(id: int):
for t in todos:
if t.id == id:
return t
return {"error": "not found"}
@router.put("/todo/{id}")
def update(id: int, todo: ToDo):
for i, t in enumerate(todos):
if t.id == id:
todos[i] = todo
return {"message": "수정됨"}
return {"error": "not found"}
@router.delete("/todo/{id}")
def delete(id: int):
for i, t in enumerate(todos):
if t.id == id:
todos.pop(i)
return {"message": "삭제됨"}
return {"error": "not found"}
main.py
from fastapi import FastAPI
from todo import router
app = FastAPI()
app.include_router(router)
todo.py
from fastapi import APIRouter
router = APIRouter()
todos = []
@router.get("/plus/{a}/{b}")
async def plus(a: int, b: int):
result = a + b
return {"result": result}

프로젝트 구조
BACK/
├── main.py
└── todo.py
main.py는 FastAPI 앱을 실행하는 진입점이며,
todo.py는 라우터(router)와 실제 API 기능을 담당한다.
main.py
from fastapi import FastAPI
FastAPI 프레임워크에서 FastAPI 객체를 가져오는 import 구문이다.
이 객체는 “서버 전체”를 의미한다.
from todo import router
todo.py 안에 정의된 APIRouter 인스턴스(router) 를 불러온다.
즉, 실제 API 경로 등록을 todo.py에서 담당하고, main.py에서는 등록만 한다.
app = FastAPI()
FastAPI 애플리케이션 인스턴스를 생성한다.
Flask와 비교하면 app = Flask(name) 같은 역할을 한다.
app.include_router(router)
todo.py에서 만든 router 안의 모든 라우팅(/plus/..., /todo 등)을
FastAPI 앱에 포함시킨다.
즉, main.py는:
서버 실행
라우터 등록
두 가지 역할만 수행한다.
todo.py
from fastapi import APIRouter
라우터 기능을 사용하기 위해 APIRouter 클래스를 가져온다.
router = APIRouter()
라우터 인스턴스를 생성한다.
@router.get() 같은 데코레이터를 사용해 API 경로를 등록할 수 있다.
todos = []
전역 리스트.
todo 데이터를 저장하는 임시 DB 역할을 한다.
@router.get("/plus/{a}/{b}")
async def plus(a: int, b: int):
result = a + b
return {"result": result}
/plus/3/5
이런 식으로 호출하면 a=3, b=5가 함수로 전달된다.
@router.get("/plus/{a}/{b}")
HTTP GET 요청 처리
URL path parameter 방식
{a}, {b}를 변수로 추출하여 함수에 전달
async def plus(a: int, b: int)
비동기 함수(async)
FastAPI는 async/await를 자연스럽게 지원해 높은 성능을 낸다.
a, b는 정수형(int)으로 자동 변환된다.
내부 로직
result = a + b
두 숫자를 더해 result에 저장한다.
반환값
return {"result": result}
FastAPI는 dict를 자동으로 JSON으로 변환해 응답한다.
main.py
from fastapi import FastAPI
from menu import router
app = FastAPI()
app.include_router(router)
model.py
from pydantic import BaseModel
class Menu(BaseModel):
id: int
name: str
price: int
menu.py
from fastapi import APIRouter, HTTPException, Path
from model import Menu
router = APIRouter()
menus = []
@router.post("/menu")
async def add_menu(menu: Menu):
menus.append(menu)
return {"message": "Menu added successfully"}
@router.get("/menu")
async def get_all_menus():
return {"menus": menus}
@router.get("/menu/{menu_id}")
async def get_single_menu(menu_id: int = Path(...)):
for m in menus:
if m.id == menu_id:
return {"menu": m}
raise HTTPException(status_code=404, detail="Menu not found")
@router.put("/menu/{menu_id}")
async def update_menu(menu_data: Menu, menu_id: int = Path(...)):
for m in menus:
if m.id == menu_id:
m.name = menu_data.name
m.price = menu_data.price
return {"message": "Menu updated successfully"}
raise HTTPException(status_code=404, detail="Menu not found")
@router.delete("/menu/{menu_id}")
async def delete_menu(menu_id: int = Path(...)):
for m in menus:
if m.id == menu_id:
menus.remove(m)
return {"message": "Menu deleted successfully"}
raise HTTPException(status_code=404, detail="Menu not found")

전체화면들

all menu:추가한 모든 메뉴보기가능

get menu:특정번호의 메뉴가 먼지 보기가능

put menu:특정번호의 메뉴 수정가능

add menu: 메뉴 추가가능

delete menu:메뉴 삭제기능
프로젝트 구조
back/
├── main.py
├── menu.py
├── model.py
└── env/
model.py
from pydantic import BaseModel
class Menu(BaseModel):
id: int
name: str
price: int
클라이언트가 보내는 JSON을 Python 객체로 변환
데이터를 검증(validation)
Swagger 문서에 자동으로 스키마 생성
menu.py
from fastapi import APIRouter, HTTPException
from model import Menu
router = APIRouter()
menu_list = []
실제 API 엔드포인트 구현
클라이언트 요청 처리
데이터 추가/조회/업데이트/삭제 등 CRUD 담당
model.py 에서 만든 Menu 모델 사용
POST /menu (새 메뉴 추가)
@router.post("/menu")
async def add_menu(menu: Menu):
menu_list.append(menu)
return {"message": "Menu added"}
@router.xxx
router 에 xxx 함수는 HTTP PUT 요청이 들어왔을 때 처리하는 함수임.
JSON → Menu 모델 → Python 객체
리스트에 저장
성공 메시지 반환
GET /menu (전체 메뉴 조회)
@router.get("/menu")
async def get_menu():
return {"menus": menu_list}
리스트 그대로 반환
Pydantic이 자동으로 JSON 변환
PUT /menu/{id} (업데이트)
@router.put("/menu/{menu_id}")
async def update_menu(menu_id: int, new_data: MenuItem):
for menu in menu_list:
if menu.id == menu_id:
menu.name = new_data.name
menu.price = new_data.price
return {"message": "Updated"}
raise HTTPException(status_code=404, detail="Menu not found")
/menu/{menu_id}
URL 패턴: /menu/1, /menu/2 처럼 숫자가 들어오면 menu_id 로 매핑된다.
{menu_id} 안의 이름이 함수 파라미터 이름과 같아야 함.
async def update_menu(menu_id: int, new_data: MenuItem):
async def
비동기 함수. FastAPI는 내부적으로 비동기 처리를 지원하기 때문에 이렇게 작성하는 걸 권장.
menu_id: int
path parameter. URL 의 {menu_id} 부분이 여기로 들어온다. 타입힌트 int라서 정수로 변환.
new_data: MenuItem
request body. 클라이언트가 JSON으로 보낸 데이터가 MenuItem 모델로 자동 변환된다.
**{
"name": "새 메뉴",
"price": 9000
}
for menu in menu_list:**
menu_list 안에 들어 있는 모든 메뉴를 하나씩 꺼내서 순회.
여기서 for 를 쓰는 이유:
여러 개 메뉴 중에서 id가 일치하는 하나를 찾기 위해 전부 검사해야 하기 때문
조건이 참일 때만이 아니라, 목록을 하나씩 돌면서 검사 → 그래서 for + if 같이 사용.
if menu.id == menu_id:
리스트에서 꺼낸 menu 하나가, 요청으로 들어온 menu_id 와 같은지 비교.
참이면, 이 메뉴가 우리가 수정해야 할 대상이라는 뜻.
menu.name = new_data.name
찾은 menu 객체의 name 필드를, 요청으로 들어온 new_data.name 값으로 덮어쓴다.
즉, 실제 데이터 수정.
menu.price = new_data.price
마찬가지로 price 도 새 값으로 변경.
return {"message": "Updated"}
수정이 성공했으니, 바로 클라이언트에게 응답을 돌려줌.
파이썬 딕셔너리를 리턴하면 FastAPI가 자동으로 JSON으로 만들어서 응답:
{
"message": "Updated"
}
주의: return 이 실행되면 함수는 즉시 끝난다.
그래서 찾으면 바로 return, 못 찾으면 아래 raise 까지 내려가게 됨.
DELETE /menu/{id}
@router.delete("/menu/{menu_id}")
async def delete_menu(menu_id: int):
for menu in menu_list:
if menu.id == menu_id:
menu_list.remove(menu)
return {"message": "Deleted"}
raise HTTPException(status_code=404, detail="Menu not found")
@router.delete("/menu/{menu_id}")
/menu/1, /menu/2 같은 URL로 DELETE 요청이 들어오면 이 함수 실행.
async def delete_menu(menu_id: int):
비동기 함수.(위에서 말한)
menu_id 하나만 받는다. 삭제는 body가 필요 없어서 모델 인자가 없다.
for menu in menu_list:
마찬가지로, 리스트 안의 메뉴를 하나씩 순회.
if menu.id == menu_id:
삭제해야 할 메뉴인지 검사.
menu_list.remove(menu)
list.remove(값)
리스트에서 해당 값과 같은 첫 번째 요소를 제거.
여기서는 menu 객체 자체를 리스트에서 빼버리는 것 = 삭제.
return {"message": "Deleted"}
정상적으로 삭제됐다는 응답.
개발자의 코딩 속도 차이는 초반에는 크게 보일 수 있지만 5년 정도 지나면 거의 동일한 수준이 된다. 이후 실력 차이는 설계 능력에서 나타난다. 현재 단계에서 중요한 것은 코드를 완성하고 흐름을 이해하는 것이며, 코딩 속도에 대한 걱정은 할 필요가 없다.