FastAPI에서 multipart/form-data 수신하기

유자·2026년 2월 25일

multi-modal-fraud-project

목록 보기
3/16

FastAPI File 수신 코드 단계별 빌드업

  1. Endpoint 기본 틀 잡기
    가장 먼저 Spring Boot가 던진 HTTP Request를 파이썬 서버가 받을 수 있도록 router(출입구)를 만듭니다.

    FastAPI에서는 @app.post 데코레이터를 사용하여 POST 방식의 endpoint를 생성합니다. 괄호 안의 경로는 spring boot 코드에서 목적지로 설정했던 URL의 마지막 경로와 정확히 일치해야 합니다.

@app.post("/predict")
async def predict_image():
    pass
  1. 매개변수로 UploadFile 선언하기
    이제 클라이언트(spring boot)가 보낸 파일을 파이썬의 객체로 받아냅니다.

    Spring에서 보낸 MultiValueMap의 키값("file")과 정확히 동일한 이름의 매개변수를 선언해야 합니다.
    FastAPI는 대용량 파일 수신 시 서버 메모리 초과(OOM)를 방지하기 위해 UploadFile이라는 내장 클래스를 제공합니다. 이 객체는 파일 크기가 1MB를 초과하면 메모리 적재를 멈추고 디스크에 임시 기록하는 spooling 기법을 사용하여 서버 resource를 안전하게 보호합니다.
    매개변수 타입으로 UploadFile을 지정하고, File(...) 객체를 할당하여 Request Body에 포함된 multipart/form-data에서 해당 파일만 정확히 Parsing 해오도록 지시합니다.

from fastapi import FastAPI, File, UploadFile

app = FastAPI()

@app.post("/predict")
async def predict_image(file: UploadFile = File(...)):
    pass
  1. Byte Stream 읽어오기
    받아온 파일 객체에서 실제 Binary Data를 추출합니다.

    Spring에서 getResource()로 직렬화하여 보낸 byte stream을 파이썬에서 읽어 들여야 합니다. UploadFile 객체의 read() 메서드를 비동기(await)로 호출하면 파일의 순수한 binary data를 메모리로 가져올 수 있습니다. 이 data stream이 향후 AI 모델에 input으로 들어갈 핵심 데이터가 됩니다.

from fastapi import FastAPI, File, UploadFile

app = FastAPI()

@app.post("/predict")
async def predict_image(file: UploadFile = File(...)):
    image_data = await file.read()

추가 설명:


"byte stream을 파이썬에서 읽어 들여야 합니다"
네트워크 선(LAN 선이나 Wi-Fi)을 통과할 때 파일은 '사진'의 형태를 유지할 수 없습니다. 스프링 서버는 getResource()를 이용해 사진을 무수히 많은 0과 1의 숫자 쪼가리들로 분해(직렬화)해서 파이썬으로 던집니다. 이렇게 0과 1이 물줄기처럼 연속적으로 흘러오는 상태를 byte stream이라고 부릅니다. 파이썬은 이 물줄기를 일단 받아냅니다.


"await file.read()로 순수한 binary data를 메모리로 가져옵니다"
FastAPI에서 맨 처음 받아내는 file: UploadFile 객체는 아직 '진짜 사진'이 아닙니다. 파일명, 확장자, 사이즈 같은 Metadata 정보만 들고 있는 상태입니다.

  • .read(): 진짜 내용물(0과 1의 조합)을 껍데기에서 추출해 파이썬 서버의 메모리(RAM) 공간에 올려놓는(Load) 명령어입니다.
  • await (비동기): 파일 용량이 커서 메모리로 가져오는데 0.1초라도 걸린다면, 파이썬 서버가 그동안 멈춰있지 않고 다른 사용자의 요청을 처리할 수 있게 "잠깐 비켜서 기다려"라고 지시하는 효율적인 문법입니다.


    "AI 모델에 input으로 들어갈 핵심 데이터가 됩니다"
    AI 모델(PyTorch, TensorFlow 등)은 웹 통신 규격인 UploadFile 객체나 MultiValueMap 같은 껍데기를 전혀 이해하지 못합니다.
    AI는 오직 순수한 숫자 배열(Matrix/Tensor)만 계산할 수 있습니다. 따라서 .read()를 통해 추출해 낸 순수한 binary data만이 픽셀 값으로 변환되어 AI 모델의 첫 번째 입력값(input)으로 사용될 자격을 갖게 됩니다.
  1. 전송 및 결과 반환
    마지막으로 추출한 데이터를 기반으로 분석을 수행하고, 그 결과를 다시 spring boot로 반환합니다.
    분석이 완료되었다고 가정하고(여기서는 임시로 파일명 반환), 파이썬의 Dictionary 형태로 결과를 return 합니다. FastAPI는 이 Dictionary를 자동으로 JSON 형식의 String으로 직렬화하여 응답합니다. Spring boot 쪽에서는 RestTemplate.postForObject()에서 String.class로 기다리고 있으므로, 이 JSON 문자열을 그대로 수신하여 사용자 화면에 출력하게 됩니다.
from fastapi import FastAPI, File, UploadFile

app = FastAPI()

@app.post("/predict")
async def predict_image(file: UploadFile = File(...)):
    image_data = await file.read()
    
    # AI 모델 분석 로직이 들어갈 자리
    
    return {"filename": file.filename, "message": "File received successfully"}

FastAPI Multipart Parsing 원리

  1. python-multipart 의존성
    FastAPI는 기본적으로 JSON 형태의 Request Body는 자체적으로 Parsing 할 수 있지만, multipart/form-data 규격을 해석하기 위해서는 내부적으로 python-multipart 라는 외부 라이브러리에 의존합니다. 따라서 이 라이브러리가 설치되어 있지 않으면 FastAPI는 boundary 경계선을 인식하지 못하고 에러를 발생시킵니다.

  2. Spooled Temporary File 메커니즘
    FastAPI에서 단순 bytes가 아닌 UploadFile 클래스를 적극적으로 사용하는 이유는 메모리 관리 때문입니다. 만약 수백 MB에 달하는 대용량 이미지 파일이 들어올 경우, 이를 모두 메모리(RAM)에 올리면 서버가 다운될 수 있습니다. UploadFile은 내부적으로 SpooledTemporaryFile을 사용하여, 일정 크기(기본 1MB) 이하의 파일은 메모리에서 빠르게 처리하고, 기준치를 초과하는 파일은 디스크에 임시 저장하여 서버의 과부하를 막아줍니다.

  3. Spring HttpMessageConverter와의 호환성
    앞서 Spring Boot의 HttpMessageConverter가 MultiValueMap을 조립하며 생성한 고유한 boundary 문자열과 헤더의 Content-Type 정보는 고스란히 FastAPI로 전달됩니다. FastAPI의 의존성 주입 시스템은 이 헤더를 감지하고, python-multipart를 가동해 boundary를 역으로 추적합니다. 이를 통해 Text 파트와 File 파트를 분리해 내고, 개발자가 선언한 file: UploadFile 변수에 파일의 메타데이터와 byte stream만 정확하게 맵핑해 주는 것입니다.


cf)

FastAPI File(...)의 이해: Request Validation 및 메타데이터 설정

점 세 개(...)가 들어가 있어서 마치 "여기에 알아서 내용을 채워 넣으세요"라는 placeholder처럼 보이지만, 사실 저 기호는 python의 실제 문법이자 FastAPI에서 아주 중요한 역할을 하는 핵심 코드입니다.

FastAPI의 File() 함수는 Client(Spring Boot)가 보낸 데이터를 File 객체로 Parsing 하라는 지시어입니다. 이 괄호 안에는 이 파일의 필수 여부API 문서(Swagger UI)에 들어갈 설명을 설정할 수 있습니다.

1. ... (Ellipsis): 필수 값 (Required) 선언

파이썬에서 ...은 Ellipsis(생략 기호)라는 특수한 내장 객체입니다. FastAPI에서는 이를 "이 파라미터는 무조건 들어와야 하는 필수(Required) 값이다"라는 의미로 사용합니다.

async def predict_image(file: UploadFile = File(...)):
  • 동작 원리: Spring Boot가 Request를 보낼 때 file을 빼먹고 보내면, FastAPI 서버 내부 로직이 실행되기도 전에 프레임워크가 알아서 422 Validation Error를 발생시키고 요청을 차단합니다.

2. None: 선택적 파일 받기 (Optional)

만약 파일 첨부가 필수가 아닌 선택 사항인 게시판을 만든다면 어떻게 할까요? 괄호 안에 ... 대신 None을 넣으면 됩니다.

from typing import Optional

async def predict_image(file: Optional[UploadFile] = File(None)):
  • 동작 원리: Client가 파일을 보내지 않아도 에러가 나지 않습니다. 대신 파이썬 코드 안에서 if file is not None: 같은 조건문으로 파일이 들어왔을 때만 처리하도록 분기 처리를 해주어야 합니다.

3. 메타데이터 추가: API 문서화 (Swagger UI)

FastAPI의 강력한 장점 중 하나인 자동 API 문서(Swagger UI)에 보여질 설명을 괄호 안에 직접 추가할 수 있습니다.

async def predict_image(
    file: UploadFile = File(..., description="분석할 원본 이미지 파일 (JPEG/PNG)")
):
  • 동작 원리: 이렇게 description을 추가하면, 나중에 /docs 주소로 접속했을 때 보이는 API 테스터 화면에 해당 설명이 그대로 출력됩니다.

    프론트엔드 개발자나 다른 팀원들이 이 API를 사용할 때 어떤 파일을 올려야 하는지 한눈에 알 수 있습니다.

0개의 댓글