FastAPI는 HTML 폼 데이터(Form)와 파일 업로드(File/UploadFile)를 처리합니다. 이는 다른 콘텐츠 타입(multipart/form-data)을 사용하므로 JSON 본문과 구별됩니다. 둘 다 특수 매개변수 타입으로 선언됩니다.
from fastapi import Form
@app.post("/login")
def login(username: str = Form(), password: str = Form()): # JSON이 아닌 폼 필드
# 데이터는 application/x-www-form-urlencoded 또는 multipart/form-data로 옴
return {"username": username}
Form()을 사용해 HTML 폼 필드(예: 로그인 폼)를 받습니다. 참고: 라우트는 JSON 본문 또는 폼 데이터 중 하나만 사용할 수 있으며, 둘 다는 안 됩니다. 서로 다른 콘텐츠 타입이기 때문입니다. (python-multipart 설치 필요.)
from fastapi import File, UploadFile
@app.post("/upload")
async def upload(file: UploadFile): # 단일 파일
contents = await file.read() # 파일의 바이트를 읽음
file.filename # 원래 이름
file.content_type # MIME 타입
# 저장:
with open(f"uploads/{file.filename}", "wb") as f:
f.write(contents)
return {"filename": file.filename, "size": len(contents)}
UploadFile은 권장되는 타입입니다. spooled 파일(큰 파일은 메모리가 아닌 디스크로 감)이며, async 메서드(read, write, seek)와 메타데이터를 가집니다. 아주 작은 파일을 제외하고는 원시 bytes보다 이것을 사용하세요.
@app.post("/upload-many")
async def upload_many(files: list[UploadFile]): # 다중 파일
return [f.filename for f in files]
@app.post("/profile")
async def profile(name: str = Form(), avatar: UploadFile = File()):
# 하나의 multipart 요청에서 폼 필드와 파일 업로드를 결합
...
if file.content_type not in ["image/jpeg", "image/png"]: # 타입 검사
raise HTTPException(400, "Invalid file type")
# 또한: 파일 크기 제한, 파일명 정제(sanitize), 콘텐츠 스캔 — 업로드는 신뢰할 수 없는 입력
폼 데이터와 파일 업로드를 처리하는 것은 흔하고 실용적인 필요입니다. 로그인 폼, 프로필 사진 업로드, 문서 제출, 파일 처리 API가 모두 이를 요구하며, FastAPI가 기본으로 사용하는 JSON 본문과는 다르게 동작합니다.
폼 필드를 위한 Form()과 특히 업로드를 위한 UploadFile(권장 방식 — 모든 것을 메모리에 로드하는 대신 큰 파일을 디스크로 spool하므로, RAM을 소진하지 않고 큰 파일을 처리하는 데 중요함)을 이해하는 것이 이러한 기능에 필요합니다.
마찬가지로 중요한 것은 업로드에 따르는 보안 책임입니다: 업로드된 파일은 신뢰할 수 없는 입력이므로, 타입과 크기를 검증하고, 파일명을 정제하며, 저장/제공 방식에 주의하는 것이 취약점을 피하는 데 필수적입니다.
폼 필드를 받고, 단일 및 다중 파일 업로드를 처리하며, 폼과 파일을 결합하고, 업로드를 안전하게 검증하는 방법을 아는 것은 사용자가 제출하는 콘텐츠를 받는 실제 API를 구축하기 위한 실용 지식입니다.