[FastAPI 공식문서] FastAPI - (12) 폼 및 파일 요청

이영락·2024년 10월 22일

개발자 기본기

목록 보기
49/53

🏖️ Form 데이터 처리

FastAPI에서 Form 데이터를 처리하려면 Form 클래스를 사용합니다. Form 데이터는 일반적으로 HTML <form> 태그를 통해 전송되는 입력 데이터입니다. JSON 데이터와는 다르게 처리되므로, FastAPI에서 이를 제대로 인식하고 처리하기 위해 Form을 사용해야 합니다.


1. Form 데이터 처리하기 (Handling Form Data)

일반적인 API는 JSON 형식의 데이터를 받지만, 때로는 HTML 폼을 통해 데이터가 전송되는 경우가 있습니다. 이 경우, FastAPI에서는 Form 클래스를 사용하여 해당 데이터를 처리합니다.

예시:

from fastapi import FastAPI, Form
from typing import Annotated

app = FastAPI()

@app.post("/login/")
async def login(username: Annotated[str, Form()], password: Annotated[str, Form()]):
    return {"username": username}

이 예시에서는 usernamepassword를 HTML 폼 필드로 받아들입니다. 이는 JSON 본문과는 다른 방식으로 처리됩니다.

주의사항:

  • Form을 사용하려면 python-multipart 패키지를 설치해야 합니다.
  • Form()은 기본적으로 Body()와 동일한 클래스에서 상속되지만, Form 데이터를 명시적으로 처리할 수 있도록 설계되었습니다.

2. Form 필드 선언하기 (Declaring Form Fields)

Form 데이터를 받기 위해서는, BodyQuery처럼 Form을 사용하여 입력 매개변수를 선언할 수 있습니다.

예시:

from fastapi import FastAPI, Form
from typing import Annotated

app = FastAPI()

@app.post("/login/")
async def login(username: Annotated[str, Form()], password: Annotated[str, Form()]):
    return {"username": username}

이 예시에서는 OAuth2의 "password flow" 스펙에서 usernamepassword를 HTML 폼 필드로 받습니다. Form 클래스는 Body()와 동일한 방식으로 동작하며, 예시를 추가하거나 별칭을 설정할 수 있습니다.


3. Form과 Body의 차이 (Difference Between Form and Body)

HTML <form> 태그는 데이터를 전송할 때 JSON이 아닌 application/x-www-form-urlencoded 또는 multipart/form-data로 인코딩합니다. 이는 JSON과는 다른 방식이기 때문에 Form 클래스를 사용하여 해당 데이터를 처리해야 합니다.

기술적 세부사항:

  • application/x-www-form-urlencoded: 일반적인 HTML 폼 필드 전송 방식.
  • multipart/form-data: 파일을 포함하는 폼에서 사용되는 전송 방식.

Form 클래스를 명시적으로 사용하지 않으면, FastAPI는 해당 데이터를 QueryBody 파라미터로 해석하게 됩니다.


4. 폼 데이터와 JSON 데이터의 혼합 불가 (No Mixing of Form and JSON)

Form 데이터를 받는 경로에서는 JSON 데이터Form 데이터를 동시에 받을 수 없습니다. 그 이유는 HTTP 프로토콜 상에서 하나의 요청 본문은 단 하나의 미디어 유형으로 인코딩되기 때문입니다. Form 데이터를 사용하면 본문은 application/x-www-form-urlencoded로 인코딩되므로, JSON 본문을 동시에 사용할 수 없습니다.

주의사항:

  • Form 파라미터를 사용하는 경로에서는 Body()와 같이 JSON 형식의 데이터를 받을 수 없습니다.

5. Form 사용 방법 요약 (Recap)

  • Form 클래스는 FastAPI에서 폼 데이터를 처리하기 위한 방법입니다.
  • Form 필드를 선언하려면, Form()을 사용하여 해당 필드를 명시적으로 선언해야 합니다.
  • 폼 필드는 JSON 데이터와는 다른 방식으로 인코딩되어 전송되므로, 이를 처리하기 위해서는 Form 클래스를 반드시 사용해야 합니다.
  • JSON 데이터와 Form 데이터는 동시에 받을 수 없습니다. 이는 HTTP 프로토콜의 제한입니다.

이처럼 FastAPI에서 Form 데이터를 처리할 때는 Form 클래스를 사용하여 명확하게 선언하고, JSON 데이터와는 별도로 처리해야 합니다.


🏖️ Form 데이터와 Pydantic 모델

FastAPI에서는 Form 데이터를 처리할 때 Pydantic 모델을 사용할 수 있습니다. 이를 통해 입력된 Form 데이터를 효과적으로 검증하고, 모델화하여 관리할 수 있습니다.


1. Pydantic 모델을 사용한 Form 데이터 처리 (Form Data with Pydantic Models)

FastAPI는 Pydantic 모델을 사용하여 Form 데이터를 처리할 수 있도록 지원합니다. 이를 통해, 요청 본문 데이터를 체계적으로 선언하고 검증할 수 있습니다.

예시:

from fastapi import FastAPI, Form
from pydantic import BaseModel
from typing import Annotated

app = FastAPI()

class FormData(BaseModel):
    username: str
    password: str

@app.post("/login/")
async def login(data: Annotated[FormData, Form()]):
    return data
  • FormData 모델은 usernamepassword 필드를 가집니다.
  • login 함수는 이 모델을 사용하여 HTML Form에서 전달된 데이터를 처리합니다.

2. 추가 Form 필드 금지 (Forbidding Extra Form Fields)

어떤 경우에는 Form에 명시된 필드 외의 추가적인 필드가 전송되는 것을 방지해야 할 수 있습니다. 이를 위해 Pydantic모델 구성을 통해 추가 필드 금지 설정을 할 수 있습니다.

예시:

from fastapi import FastAPI, Form
from pydantic import BaseModel
from typing import Annotated

app = FastAPI()

class FormData(BaseModel):
    username: str
    password: str
    model_config = {"extra": "forbid"}

@app.post("/login/")
async def login(data: Annotated[FormData, Form()]):
    return data

위 코드에서는 model_configextra: "forbid" 옵션을 설정하여, 추가적인 Form 필드가 전송되지 않도록 했습니다.

예시 오류 응답:

만약 클라이언트가 username, password 외의 필드(예: extra)를 전송하면, 다음과 같은 에러 응답이 반환됩니다.

{
    "detail": [
        {
            "type": "extra_forbidden",
            "loc": ["body", "extra"],
            "msg": "Extra inputs are not permitted",
            "input": "Mr. Poopybutthole"
        }
    ]
}

3. 문서에서 확인하기 (Check in the Docs)

FastAPI/docs 경로에서 자동 생성된 API 문서에서 Pydantic 모델을 사용하여 선언된 Form 필드들을 확인할 수 있습니다. 이를 통해 API의 입력 데이터 구조를 명확하게 확인하고 테스트할 수 있습니다.


4. 요약 (Summary)

  • Form 데이터Pydantic 모델을 사용하여 선언 및 검증할 수 있습니다.
  • model_config를 사용하여 추가적인 Form 필드가 전송되지 않도록 제한할 수 있습니다.
  • FastAPI는 이를 통해 효과적인 데이터 검증문서화 기능을 제공합니다.

🏖️ 파일 요청 (File Requests)

FastAPI를 사용하면 클라이언트가 업로드하는 파일을 처리할 수 있습니다. 파일들은 "폼 데이터"로 전송되며, FastAPI는 이 데이터를 처리할 수 있는 여러 방법을 제공합니다.


1. File 요청을 처리하기 위한 설치 (Installing Requirements for File Handling)

파일을 처리하기 위해서는 python-multipart 패키지를 설치해야 합니다. 이를 통해, FastAPI는 multipart/form-data 형식으로 전송되는 데이터를 처리할 수 있습니다.

설치 방법:

$ pip install python-multipart

2. File 임포트 (Importing File and UploadFile)

FastAPI에서 파일을 처리하기 위해서는 FileUploadFile 클래스를 임포트해야 합니다.

from fastapi import FastAPI, File, UploadFile

app = FastAPI()

@app.post("/files/")
async def create_file(file: bytes = File()):
    return {"file_size": len(file)}

@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}
  • File(): 파일을 바이트(bytes)로 처리하며, 작은 파일을 메모리 내에서 처리할 때 적합합니다.
  • UploadFile: 파일 객체를 사용하여 대용량 파일 처리에 적합하며, 메타데이터비동기 인터페이스를 제공합니다.

3. File 매개변수 정의 (Defining File Parameters)

파일 매개변수는 BodyForm과 같은 방식으로 정의할 수 있습니다.

@app.post("/files/")
async def create_file(file: bytes = File()):
    return {"file_size": len(file)}
  • File()을 사용하여 경로 작동 함수의 매개변수를 정의하면, FastAPI는 이 매개변수를 파일로 처리합니다.
  • File()을 사용하면 매개변수가 쿼리 매개변수로 잘못 해석되는 것을 방지할 수 있습니다.

4. File vs UploadFile

파일 업로드 시 FileUploadFile 중 어느 것을 사용할지 결정할 수 있습니다.

File:

  • 파일을 bytes로 처리합니다.
  • 작은 파일에 적합하며, 모든 파일 내용을 메모리에 저장합니다.

UploadFile:

  • SpooledTemporaryFile을 사용하여 대용량 파일에 적합합니다.
  • 파일 메타데이터(파일명, MIME 타입 등)에 접근할 수 있습니다.
  • 비동기 file-like 인터페이스를 제공합니다.
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}

5. UploadFile 어트리뷰트와 메소드

UploadFile은 파일에 대한 다양한 정보와 메소드를 제공합니다:

  • filename: 업로드된 파일의 이름 (예: "myimage.jpg")
  • content_type: 파일 형식 (MIME type, 예: "image/jpeg")
  • file: SpooledTemporaryFile 객체

비동기 메소드:

  • write(data): 파일에 데이터를 씁니다.
  • read(size): 파일에서 지정한 크기의 데이터를 읽습니다.
  • seek(offset): 파일 포인터를 지정한 위치로 이동시킵니다.
  • close(): 파일을 닫습니다.

예시:

@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    contents = await file.read()
    return {"file_contents": contents}

6. 다중 파일 업로드 (Multiple File Uploads)

여러 파일을 동시에 업로드하려면 List[bytes] 또는 List[UploadFile]을 사용합니다.

예시:

from typing import List
from fastapi import FastAPI, File, UploadFile

app = FastAPI()

@app.post("/uploadfiles/")
async def create_upload_files(files: List[UploadFile]):
    return {"filenames": [file.filename for file in files]}

여기서는 UploadFile 리스트를 받아 여러 파일을 처리할 수 있습니다.


7. HTML Form 예시 (HTML Form Example)

파일 업로드를 테스트하기 위해 간단한 HTML 양식을 사용할 수 있습니다.

HTML Form:

@app.get("/")
async def main():
    content = """
    <body>
    <form action="/uploadfiles/" enctype="multipart/form-data" method="post">
    <input name="files" type="file" multiple>
    <input type="submit">
    </form>
    </body>
    """
    return HTMLResponse(content=content)

이 폼을 통해 다중 파일을 선택하여 업로드할 수 있습니다.


8. 요약 (Summary)

  • FileUploadFile은 FastAPI에서 파일 업로드를 처리하는 데 사용됩니다.
  • File()은 작은 파일을 처리할 때 유리하고, UploadFile은 대용량 파일에 적합합니다.
  • UploadFile을 통해 메타데이터비동기 인터페이스를 사용할 수 있습니다.
  • 여러 파일을 업로드할 수 있으며, List를 사용하여 처리합니다.

파일 업로드와 관련된 FastAPI의 기능은 강력하고 유연하여, 다양한 파일 업로드 시나리오를 처리하는 데 매우 유용합니다.

🏖️ 폼 및 파일 요청 (Form and File Requests)

FastAPI를 사용하면 FileForm을 함께 처리하여 클라이언트가 전송하는 파일과 폼 데이터를 동시에 처리할 수 있습니다. 이를 통해 파일 업로드와 폼 필드 데이터를 하나의 요청으로 받을 수 있습니다.


1. 필수 설치 (Required Installation)

파일과 폼 데이터를 함께 처리하려면 python-multipart 패키지를 설치해야 합니다. 이 패키지는 multipart/form-data 형식으로 전송되는 데이터를 처리하는 데 필요합니다.

설치 명령:

$ pip install python-multipart

2. File 및 Form 데이터 처리

FastAPI에서 FileForm 매개변수를 함께 처리하는 방법은 매우 간단합니다. 두 개의 파일과 하나의 폼 필드를 처리하는 예시는 다음과 같습니다.

예시 코드:

from fastapi import FastAPI, File, Form, UploadFile

app = FastAPI()

@app.post("/files/")
async def create_file(
    file: bytes = File(), fileb: UploadFile = File(), token: str = Form()
):
    return {
        "file_size": len(file),
        "token": token,
        "fileb_content_type": fileb.content_type,
    }

이 코드에서:

  • file: bytes로 처리되는 파일. 메모리 내에서 처리되며, 작은 파일에 적합합니다.
  • fileb: UploadFile로 처리되는 파일. 메타데이터를 포함한 대용량 파일에 적합합니다.
  • token: Form 필드에서 전송되는 폼 데이터입니다.

3. File 및 Form 매개변수 정의

BodyQuery와 마찬가지로 FileForm 매개변수는 경로 작동 함수에서 정의됩니다.

파일과 폼 필드 처리 예시:

from fastapi import FastAPI, File, Form, UploadFile

app = FastAPI()

@app.post("/files/")
async def create_file(
    file: bytes = File(), fileb: UploadFile = File(), token: str = Form()
):
    return {
        "file_size": len(file),
        "token": token,
        "fileb_content_type": fileb.content_type,
    }
  • File(): 파일 매개변수를 처리하며, 업로드된 파일을 bytes로 받을 수 있습니다.
  • UploadFile: 파일을 UploadFile로 선언하면 파일의 메타데이터 및 비동기 파일 처리가 가능합니다.
  • Form(): 폼 필드 데이터를 받는 매개변수로, 주로 텍스트 데이터를 처리하는 데 사용됩니다.

4. 파일과 폼 필드 동시 처리 (Handling Files and Forms Simultaneously)

파일과 폼 데이터는 폼 데이터 형식으로 전송되며, FastAPI는 이를 자동으로 multipart/form-data로 처리하여 각 파일과 폼 필드를 적절하게 분리하여 전달합니다.

  • bytes로 선언된 파일은 메모리에 저장된 바이트 형태로 처리됩니다.
  • UploadFile로 선언된 파일은 파일 객체로 처리됩니다.
  • Form 필드는 폼 데이터로 처리되어 텍스트 데이터를 처리할 수 있습니다.

5. 경고 (Warning)

폼 데이터와 파일 데이터를 처리할 때, 한 가지 중요한 점이 있습니다:

  • 다수의 FileForm 매개변수를 함께 선언할 수 있지만, 본문이 application/json 형식이 아니라 multipart/form-data로 인코딩되기 때문에 Body 필드를 JSON으로 받을 수 없습니다. 이는 FastAPI의 한계가 아니라 HTTP 프로토콜에 따른 제한 사항입니다.

6. 요약 (Summary)

FastAPI에서는 FileForm 매개변수를 함께 사용하여 파일과 폼 데이터를 동시에 처리할 수 있습니다.

  • 파일을 bytesUploadFile로 처리할 수 있고, 폼 필드는 Form을 통해 받을 수 있습니다.
  • multipart/form-data로 인코딩된 요청을 처리하므로, JSON 본문을 함께 사용할 수 없다는 점을 유의해야 합니다.

하나의 요청으로 파일과 폼 데이터를 처리해야 할 경우, FileForm을 함께 사용하여 유연하고 효율적인 데이터 처리가 가능합니다.

profile
AI Engineer / 의료인공지능

0개의 댓글