
실습환경 구성
root@node3:~# mkdir todo && cd todo
root@node3:~/todo# apt update && apt install -y python3.10-venv
root@node3:~/todo# python -m venv venv # 뒤의 venv 는 가상환경의 이름(디렉토리 명)
root@node3:~/todo# ls
venv
root@node3:~/todo#
root@node3:~/todo# source venv/bin/activate # 가상환경 활성화 , 비활성화 -->deactivate
(venv) root@node3:~/todo#
(venv) root@node3:~/todo# apt install -y pip
#파이썬 기반 모듈들 설치
(venv) root@node3:~/todo# pip install fastapi uvicorn
#어떤 패키지 설치 되어있나 확인해보기
(venv) root@node3:~/todo# pip freeze > requirements.txt
(venv) root@node3:~/todo# cat requirements.txt
annotated-types==0.7.0
anyio==4.6.2.post1
click==8.1.7
exceptiongroup==1.2.2
fastapi==0.115.5
h11==0.14.0
idna==3.10
pydantic==2.9.2
pydantic_core==2.23.4
sniffio==1.3.1
starlette==0.41.2
typing_extensions==4.12.2
uvicorn==0.32.0
fastapi 의 경우 단일 진입점(entrypoint) 를 사용하므로 단일 경로에 대한 라우팅만 가능하다.
(venv) root@node3:~/todo# cat api.py
from fastapi import FastAPI
from fastapi.responses import FileResponse, RedirectResponse
app = FastAPI()
@app.get("/")
async def welcome() -> dict:
return {
"hello": "world"
}
(venv) root@node3:~/todo# uvicorn api:app --host 0.0.0.0 --port 8000 --reload
# api 파일에 있는 app 인스턴스를 동작한다. 외부에서는 서버의 모든 IP 와 포트 8000 을 통해서 접근되며 만약 파일의 내용이 변경된다면 해당 내용을 반영하여 uvicorn 을 재실행 시키게 된다.
# 테스트 환경에서는 –reload 를 사용하면 편리하지만 실제 운영환경에서는 –reload 를 사용하지 않는 것이 좋다다
root@node3:~/todo# uvicorn api:app --host 0.0.0.0 --port 8000 --reload
INFO: Will watch for changes in these directories: ['/root/todo']
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO: Started reloader process [29156] using statreload
INFO: Started server process [29158]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: 127.0.0.1:60690 - "GET / HTTP/1.1" 200 OK

blog , k8s 사이트 불러오기
#추가
@app.get("/blog")
async def blog():
return FileResponse('blog/index.html') # 만들어둔 파일로 응답
@app.get("/k8s")
async def k8s():
return RedirectResponse("https://kubernetes.io/")



파일이 잘못되면 저장후 갱신되서 오류가 발생
별도의 서버실행 명령어를 사용하는 것이 아니라 entrypoint 파일에서 서버 실행관련 함수를 실행시켜보자
from fastapi import FastAPI
from fastapi.responses import FileResponse, RedirectResponse
app = FastAPI()
@app.get("/")
async def welcome() -> dict:
return {
"hello" : "world"
}
if __name__ == "__main__" :
uvicorn:run("api:app", host="0.0.0.0", port=8000, reload=true)



swagger UI

라우팅
단일 FastAPI 인스턴스:
FastAPI는 하나의 엔트리 포인트로 애플리케이션을 실행하며, 기본적으로 단일 FastAPI 인스턴스에서 모든 라우트를 관리합니다.
uvicorn 같은 서버는 하나의 인스턴스만 실행할 수 있어, 단일 FastAPI 인스턴스를 사용해야 합니다.
라우트(Route) 정의:
FastAPI 인스턴스에 바로 라우트를 추가할 수 있습니다. 예를 들어, @app.get("/")와 같이 인스턴스에 직접 라우트를 정의하면 간단한 애플리케이션을 구축할 때 유용합니다.
APIRouter 사용 이유:
대규모 애플리케이션에서는 라우트를 모듈화하는 것이 좋습니다. APIRouter를 사용하면 라우트를 파일별로 나누어 관리할 수 있어 코드가 깔끔하고 유지보수가 용이해집니다.
여러 APIRouter를 정의한 후, 이를 단일 FastAPI 인스턴스에 포함시켜서(include_router) 사용합니다.
라우트 처리기(Route Handler):
각 라우트는 특정 경로와 요청 메서드(GET, POST 등)에 반응하여 요청을 처리하는 함수(라우트 처리기)와 연결됩니다.
HTTP Method를 통해 해당 자원에 대한 CRUD Operation을 적용하여 아래와 같이 사용한다.
Create: 데이터 생성 (POST)
Read: 데이터 조회 (GET)
Update: 데이터 수정 (PUT)
Delete: 데이터 삭제 (DELETE)
별도의 파일에 별도의 경로를 작성하고 데이터를 삽입 , 조회 해 본다.
fastapi 는 단일 엔트리포인트만 허용하는 단일 경로 route 를 제공한다. 따라서 별도의 파일에 경로를 기록했다고 하더라도 해당 파일을 api.py 에 includ 시킨다.
# api.py 파일
from fastapi import FastAPI
from todo import todo_router # 파일 이름과 라우터 이름을 todo로 수정
app = FastAPI()
@app.get("/")
async def welcome() -> dict:
return {
"message": "Hello World"
}
app.include_router(todo_router) # 라우터 이름을 todo_router로 변경
# 파일 이름: todo.py
from fastapi import APIRouter
todo_router = APIRouter()
todo_list = []
@todo_router.post("/todo")
async def add_todo(todo: dict) -> dict:
todo_list.append(todo)
return {
"message": "todo added successfully"
}
@todo_router.get("/todo")
async def retrieve_todos() -> dict:
return {
"todos": todo_list
}
실행


현재의 todo 라우트에는 스키마 모델이 생략되어 있어 마음대로 딕셔너리 값을 전달할 수 있다. (아무 데이터나 입력 가능)
pydantic 사용해 모델을 정의하고 사용할 수 있다!

FastAPI의 Pydantic 모델을 사용한 요청 바디 검증
요청 바디 검증: FastAPI는 요청 데이터가 정의된 데이터 구조에 따라 전송되도록 요청 바디를 검증할 수 있습니다. 이는 데이터의 유효성을 확인하고 악의적인 공격을 차단하는 데 유용합니다.
모델의 역할: FastAPI에서 모델은 데이터가 어떻게 전달되고 처리될지 정의하는 구조화된 클래스입니다. Pydantic의 BaseModel을 상속하여 생성되며, 각 필드에 대한 타입 힌트를 제공해 데이터 유효성 검사를 자동으로 수행합니다.
요청과 응답 타입 힌트: 모델은 요청 데이터와 응답 데이터의 타입 힌트를 제공하여 API의 요청과 응답이 명확한 데이터 구조를 따르도록 합니다.
#model.py 생성
from pydantic import BaseModel
class Todo(BaseModel):
id: int
item: str
# todo.py 파일수정
from fastapi import APIRouter
from model import Todo #class Todo
todo_router = APIRouter()
todo_list = []
@todo_router.post("/todo")
async def add_todo(todo: Todo) -> dict:
todo_list.append(todo)
return {
"message": "todo added successfully"
}
@todo_router.get("/todo")
async def retrieve_todos() -> dict:
return {
"todos": todo_list
}
결과

경로 매개변수의 주요 기능
리소스 식별: 경로 매개변수는 API 라우팅에서 특정 리소스를 식별하기 위해 사용됩니다. 예를 들어, todo_id를 통해 특정 todo 항목을 식별할 수 있습니다.
유연한 처리: 경로 매개변수는 웹 애플리케이션에서 다양한 요청을 처리할 수 있도록 API 엔드포인트를 유연하게 만듭니다. 이는 단일 엔드포인트로 여러 리소스를 다룰 수 있게 합니다.
동작 추출: 특정 작업(예: ID를 통해 todo 항목을 조회하거나 삭제)을 수행하기 위해 별도의 라우트를 생성하고 이를 경로 매개변수를 통해 처리할 수 있습니다.
# todo.py
from fastapi import APIRouter, HTTPException
from model import Todo
todo_router = APIRouter()
todo_list = []
@todo_router.post("/todo")
async def add_todo(todo: Todo) -> dict:
# 새로운 todo 항목을 딕셔너리 형태로 추가
todo_list.append(todo.dict())
return {
"message": "todo added successfully"
}
@todo_router.get("/todo")
async def retrieve_todos() -> dict:
return {
"todos": todo_list
}
@todo_router.get("/todo/{todo_id}")
async def get_single_todo(todo_id: int) -> dict:
for todo in todo_list:
if todo["id"] == todo_id: # 딕셔너리 형태로 조회
return {
"todo": todo
}
raise HTTPException(status_code=404, detail="존재하지 않는 페이지 입니다 ")


Path 클래스 추가
Path 클래스는 FastAPI에서 경로 매개변수를 정의하고 검증하는 데 사용됩니다. 경로 매개변수는 URL 경로의 일부로 포함된 값을 의미하며, API 호출 시 사용자에게 입력을 받는 데이터입니다.
필수/선택 설정: Path 클래스의 첫 번째 인수로 None(선택 입력) 또는 ...(필수 입력)를 지정할 수 있습니다. ...을 사용하면 해당 매개변수를 반드시 입력해야 함을 의미합니다.
유효성 검증: 숫자 매개변수에 대해서 gt(greater than), le(less than or equal) 등의 검증 조건을 추가할 수 있어 값이 특정 범위 내에 있는지 검사할 수 있습니다. 이를 통해 잘못된 값이 들어오는 것을 방지할 수 있습니다.
문서화 지원: title과 같은 옵션을 통해 Swagger 문서에서 매개변수의 역할을 설명할 수 있어, 자동 문서화가 가능해집니다.
# Path 추가
@todo_router.get("/todo/{todo_id}")
async def get_single_todo(todo_id: int = Path(..., title="특정 todo 를 확인하기 위한 ID",ge=1,le=1000)) -> dict:
for todo in todo_list:
if todo["id"] == todo_id: # 딕셔너리 형태로 조회
return {
"todo": todo
}

FastAPI 자동 문서화
FastAPI 는 모델의 JSON 스키마 정의를 생성하고 라우트, 요청 바디의 유형, 경로 및 쿼리 매개변수, 응답 모델 등을 자동으로 문서화 한다
model.py 파일에 아래 코드 추가
class Config:
json_schema_extra = {
"example": {
"id": 1,
"item": "첫번쨰 아이템"
}
}

기존 아이템을 변경하거나 삭제하는 라우트 추가
# model.py에 아래 코드 추가
# todo 의 item 을 변경하기 위한 모델
class TodoItem(BaseModel):
item: str
class Config:
json_schema_extra = {
"example": {
"item": "변경할 아이템 작성"
}
}


# todo.py 파일에 아래 코드 추가
# 특정 아이템 삭제
@todo_router.delete("/todo/{todo_id}")
async def delete_single_todo(todo_id: int = Path(..., title="삭제할 아이템의 ID")) -> dict:
for index, todo in enumerate(todo_list):
if todo["id"] == todo_id:
todo_list.pop(index)
return {
"message": f"todo ID {todo_id}가 삭제되었습니다"
}
raise HTTPException(status_code=404, detail="해당 ID의 todo가 존재하지 않습니다")

응답 모델과 오류처리
API 응답은 서버가 클라이언트 요청에 대해 전달하는 결과를 의미하며, 보통 JSON이나 XML 형식으로 제공됩니다. 응답은 크게 응답 헤더와 응답 바디로 구성됩니다.
응답 헤더: 요청 상태와 응답 바디에 대한 정보를 포함합니다. 예를 들어 Content-Type 헤더는 응답의 데이터 형식이 무엇인지 알려줍니다.
응답 바디: 서버가 반환하는 실제 데이터로, Content-Type에 설정된 형식(JSON, XML 등)으로 전달됩니다.
템플릿
jinja2