LabHub
배우기 러닝패스 코스

FastAPI — 型がそのまま契約だ

全行をためずにJSON Linesを出力する

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

지연 생성·줄 경계·공개 필드·출력 상한을 HTTP 스트림과 연결합니다.

왜 중요한가

관리자가 모든 주문을 내려받자 서버 메모리가 급증했다. export 함수가 JSON을 만들기 전에 모든 행을 리스트에 모았기 때문이다. 줄 단위 출력으로 바꿨지만 본문 속 개행이 실제 줄 경계를 깨고 내부 원가도 그대로 나갔다. 스트리밍은 반환 타입 하나를 바꾸는 일이 아니라 지연 평가와 표현 계약을 함께 정하는 일이다.

단계

  1. /root/work/fa-jsonl-export-lab/service.py에서 validate_row(row)는 dict이며 id가 bool 제외 양의 int, name이 비어 있지 않은 str일 때 row를 반환합니다. 나머지는 ValueError입니다. 추가 내부 필드는 허용합니다.

처음 한 번 준비하세요. 기존 파일은 덮어쓰지 않습니다.

mkdir -p /root/work/fa-jsonl-export-lab
test -e /root/work/fa-jsonl-export-lab/service.py || cp /opt/fixtures/ten_labs/fa-jsonl-export-lab/service.py /root/work/fa-jsonl-export-lab/service.py
cd /root/work/fa-jsonl-export-lab
  1. /root/work/fa-jsonl-export-lab/service.py에서 project(row)는 validate_row 후 id와 name만 가진 새 dict를 반환합니다. 원본 내부 필드는 그대로 보존합니다.

  2. /root/work/fa-jsonl-export-lab/service.py에서 encode_line(row)는 project 결과를 ensure_ascii=False, separators=(',',':'), sort_keys=True로 JSON 인코딩하고 마지막에 ' ' 한 개를 붙인 str입니다. name 안 개행은 JSON 이스케이프여야 합니다.

  3. /root/work/fa-jsonl-export-lab/service.py에서 validate_max(value)는 bool 제외 int 1~1000만 그대로 반환하고 그 외 ValueError입니다.

  4. /root/work/fa-jsonl-export-lab/service.py에서 take_rows(rows, maximum)는 islice 등으로 최대 maximum개만 지연 반환하는 iterator입니다. 호출 시 maximum을 검증하며 한 번 next하면 입력을 한 번만 소비합니다.

  5. /root/work/fa-jsonl-export-lab/service.py에서 json_lines(rows, maximum=100)는 take_rows에서 받은 행마다 encode_line을 yield합니다. 전부 합친 문자열이나 리스트를 반환하지 않습니다.

  6. /root/work/fa-jsonl-export-lab/service.py에서 decode_lines(text)는 splitlines의 각 비어 있지 않은 줄을 json.loads 후 validate_row하고 리스트로 반환합니다. 빈 문자열은 [], 비어 있는 중간 줄은 ValueError입니다.

  7. /root/work/fa-jsonl-export-lab/service.py에서 create_app(rows)는 GET /export에서 json_lines(rows, 100)을 application/x-ndjson StreamingResponse로 반환합니다. rows는 다시 순회 가능한 리스트입니다. 내부 필드가 없고 각 행의 내용과 순서를 보존해야 합니다.

참고

행 계약을 검증한다

/root/work/fa-jsonl-export-lab/service.py에서 validate_row(row)는 dict이며 id가 bool 제외 양의 int, name이 비어 있지 않은 str일 때 row를 반환합니다. 나머지는 ValueError입니다. 추가 내부 필드는 허용합니다.

처음 한 번 준비하세요. 기존 파일은 덮어쓰지 않습니다.

mkdir -p /root/work/fa-jsonl-export-lab
test -e /root/work/fa-jsonl-export-lab/service.py || cp /opt/fixtures/ten_labs/fa-jsonl-export-lab/service.py /root/work/fa-jsonl-export-lab/service.py
cd /root/work/fa-jsonl-export-lab

bool과 숫자를 구분하고 빈 이름을 오류로 처리합니다.

저장 후 bash /opt/lab/checks/fa-jsonl-export-lab/01-contract.sh로 확인하세요.

공개 행만 만든다

/root/work/fa-jsonl-export-lab/service.py에서 project(row)는 validate_row 후 id와 name만 가진 새 dict를 반환합니다. 원본 내부 필드는 그대로 보존합니다.

내보내기 경로도 일반 API와 같은 공개 필드 정책을 적용해야 합니다.

저장 후 bash /opt/lab/checks/fa-jsonl-export-lab/02-contract.sh로 확인하세요.

줄 경계를 보존하며 인코딩한다

/root/work/fa-jsonl-export-lab/service.py에서 encode_line(row)는 project 결과를 ensure_ascii=False, separators=(',',':'), sort_keys=True로 JSON 인코딩하고 마지막에 ' ' 한 개를 붙인 str입니다. name 안 개행은 JSON 이스케이프여야 합니다.

문자열 덧붙이기로 JSON을 만들면 따옴표와 개행에서 형식이 깨집니다.

저장 후 bash /opt/lab/checks/fa-jsonl-export-lab/03-contract.sh로 확인하세요.

출력 개수 상한을 검증한다

/root/work/fa-jsonl-export-lab/service.py에서 validate_max(value)는 bool 제외 int 1~1000만 그대로 반환하고 그 외 ValueError입니다.

무한 입력을 실수로 끝까지 읽지 않도록 호출자에게 상한을 요구합니다.

저장 후 bash /opt/lab/checks/fa-jsonl-export-lab/04-contract.sh로 확인하세요.

필요한 행만 소비한다

/root/work/fa-jsonl-export-lab/service.py에서 take_rows(rows, maximum)는 islice 등으로 최대 maximum개만 지연 반환하는 iterator입니다. 호출 시 maximum을 검증하며 한 번 next하면 입력을 한 번만 소비합니다.

list(rows)로 바꾸는 순간 무한 입력과 대용량 입력을 처리할 수 없습니다.

저장 후 bash /opt/lab/checks/fa-jsonl-export-lab/05-contract.sh로 확인하세요.

행을 지연 직렬화한다

/root/work/fa-jsonl-export-lab/service.py에서 json_lines(rows, maximum=100)는 take_rows에서 받은 행마다 encode_line을 yield합니다. 전부 합친 문자열이나 리스트를 반환하지 않습니다.

객체 선택과 표현 변환을 각각 지연 단계로 유지합니다.

저장 후 bash /opt/lab/checks/fa-jsonl-export-lab/06-contract.sh로 확인하세요.

내려받은 줄을 다시 검증한다

/root/work/fa-jsonl-export-lab/service.py에서 decode_lines(text)는 splitlines의 각 비어 있지 않은 줄을 json.loads 후 validate_row하고 리스트로 반환합니다. 빈 문자열은 [], 비어 있는 중간 줄은 ValueError입니다.

빈 파일과 형식이 깨진 빈 레코드를 구분합니다.

저장 후 bash /opt/lab/checks/fa-jsonl-export-lab/07-contract.sh로 확인하세요.

HTTP 다운로드를 완성한다

/root/work/fa-jsonl-export-lab/service.py에서 create_app(rows)는 GET /export에서 json_lines(rows, 100)을 application/x-ndjson StreamingResponse로 반환합니다. rows는 다시 순회 가능한 리스트입니다. 내부 필드가 없고 각 행의 내용과 순서를 보존해야 합니다.

Content-Type만 스트리밍으로 적고 내부에서는 전체를 모으지 않았는지 생성기 시험과 함께 확인합니다.

저장 후 bash /opt/lab/checks/fa-jsonl-export-lab/08-contract.sh로 확인하세요.