JSON에 바이트(Bytes)를 Base64로 담기
JSON에 바이트(Bytes)를 Base64로 담기
JSON은 UTF-8로 인코딩된 문자열만 담을 수 있어서, 원시 바이너리 데이터를 그대로 넣을 수 없어요. 그런데 앱이 JSON 형태로 통신하면서도 바이너리 데이터를 주고받아야 하는 상황이 있다면, base64로 인코딩해서 해결할 수 있어요. Pydantic 모델의 bytes 필드 설정만으로 입출력 양쪽을 모두 처리하는 방법을 살펴볼게요.
Base64 vs 파일 업로드
먼저 base64를 쓰기 전에, 정말 이 방식이 필요한지 한 번 따져 보는 게 좋아요. 바이너리 데이터를 올릴 때는 Request Files로 업로드하고, 내려보낼 때는 Custom Response - FileResponse로 보내는 게 더 효율적일 수 있어요.
base64는 바이너리 데이터를 문자열로 인코딩할 수 있지만, 원본보다 더 많은 문자를 사용해요. 그래서 보통 일반 파일보다 비효율적이에요. 바이너리 데이터를 반드시 JSON에 넣어야 하고, 파일 방식을 쓸 수 없을 때만 base64를 쓰는 게 맞아요.
Pydantic bytes 필드로 입력 받기
bytes 타입 필드를 가진 Pydantic 모델을 선언하고, model_config에서 val_json_bytes로 base64를 지정하면 입력 JSON 데이터를 검증할 때 base64 문자열을 디코드해서 원래 바이트로 만들어줘요.
from fastapi import FastAPI
from pydantic import BaseModel
class DataInput(BaseModel):
description: str
data: bytes
model_config = {"val_json_bytes": "base64"}
class DataOutput(BaseModel):
description: str
data: bytes
model_config = {"ser_json_bytes": "base64"}
class DataInputOutput(BaseModel):
description: str
data: bytes
model_config = {
"val_json_bytes": "base64",
"ser_json_bytes": "base64",
}
app = FastAPI()
@app.post("/data")
def post_data(body: DataInput):
content = body.data.decode("utf-8")
return {"description": body.description, "content": content}
@app.get("/data")
def get_data() -> DataOutput:
data = "hello".encode("utf-8")
return DataOutput(description="A plumbus", data=data)
@app.post("/data-in-out")
def post_data_in_out(body: DataInputOutput) -> DataInputOutput:
return body
/docs에서 확인해 보면 data 필드가 base64로 인코딩된 바이트를 기대한다고 표시돼요. 아래처럼 요청을 보내면,
{
"description": "Some data",
"data": "aGVsbG8="
}
(aGVsbG8=는 hello를 base64로 인코딩한 값이에요.) Pydantic이 그 문자열을 디코드해서 모델의 data 필드에 원래 바이트를 담아줘요. 응답은 이렇게 돌아와요.
{
"description": "Some data",
"content": "hello"
}
Pydantic bytes 필드로 출력하기
응답 쪽도 비슷해요. model_config에서 ser_json_bytes를 지정하면, 응답 JSON을 만들 때 바이트를 base64로 직렬화해요. 위 코드의 DataOutput 모델이 그 예시예요.
입력과 출력 모두 처리하기
입력 검증(val_json_bytes)과 출력 직렬화(ser_json_bytes)를 같은 모델에 둘 다 지정하면, JSON 데이터를 받고 보낼 때 base64로 처리하는 모델 하나로 양방향을 모두 다룰 수 있어요. 위 코드의 DataInputOutput이 바로 그 경우예요.
더 알아보기 (Learn more)
- Request Files — 파일 업로드로 바이너리 데이터를 다루는 기본 방법
- Custom Response - FileResponse — 파일을 응답으로 내려보내는 방법
- Pydantic 모델 설정 (Models) — 모델 구성과 검증 이해하기