8분

프록시를 우회하지 않고 대용량 파일 업로드하기

OpenMake가 하나의 거대한 요청을 사용자별 청크 업로드 프로토콜로 바꾸면서 기존 에이전트 작업 파일 계약을 그대로 유지한 과정을 정리했습니다.

  • 에이전트 작업
  • 셀프호스팅
  • 파일 업로드
  • Cloudflare
실행 중인 작업과 완료된 작업을 보여 주는 OpenMake 에이전트 작업 화면
사용자가 보는 에이전트 작업 흐름은 그대로 두고, 큰 바이너리 첨부의 전송 방식만 바꿨습니다.

SHIPPED / EVIDENCE

이번에 배포한 것

클라이언트 기준을 넘는 바이너리 첨부는 이제 24MB 청크로 전송됩니다. API는 소유자·순서·개수·전체 크기를 검증하고 디스크에서 파일을 조립한 뒤, 기존 멀티파트 업로드와 같은 저장 파일 경로로 넘깁니다.

클라이언트 청크
24MB
서버 상한
32MB
세션 보존
24시간
운영 검증
130MB

01

애플리케이션 상한이 실제 상한은 아니었습니다

OpenMake의 자율 에이전트 작업은 큰 파일을 입력으로 받을 수 있습니다. 하지만 공개 요청이 API에 도착하기 전에 프록시에서 거절된다면 애플리케이션의 파일 상한은 의미가 없습니다.

Cloudflare 공식 문서상 Free와 Pro 영역의 최대 업로드 크기는 100MB입니다. 단일 멀티파트 요청은 OpenMake의 검증이나 진행 표시가 시작되기도 전에 엣지에서 HTTP 413으로 실패했습니다.

02

제약조건이 프로토콜을 결정했습니다

가장 빠른 우회책은 프록시를 거치지 않는 별도 업로드 호스트입니다. 그러나 기존 공개 경로의 DNS·TLS·인증·배포 경계를 유지하고 요청 형태를 바꾸는 쪽을 선택했습니다.

  • 작은 파일은 기존 멀티파트 경로를 그대로 사용합니다.
  • 모든 업로드 동작을 인증하고 한 세션을 한 사용자에게 귀속합니다.
  • 브라우저가 끊긴 조각을 재시도할 수 있도록 같은 인덱스 쓰기를 안전하게 만듭니다.
  • 조립 후 기존 storedPath 계약을 재사용해 추출·샌드박스 주입·정리 흐름이 갈라지지 않게 합니다.

03

4단계 프로토콜과 일회성 claim

브라우저는 먼저 파일명, MIME 형식, 바이트 크기와 예상 청크 수를 선언합니다. 이어서 application/octet-stream 원본 청크를 보내고, 서버에 완료를 요청한 뒤, uploadId 참조로 작업을 생성합니다.

완료 요청은 멱등입니다. 반면 claim은 한 번만 가능합니다. 조립 파일이 작업 디렉터리로 이동하면 임시 세션을 지우므로 같은 업로드를 두 작업에 중복 첨부할 수 없습니다.

shell
POST /api/agent-task-uploads
PUT  /api/agent-task-uploads/:id/chunks/0
PUT  /api/agent-task-uploads/:id/chunks/1
POST /api/agent-task-uploads/:id/complete
POST /api/agent-tasks  { files: [{ uploadId: id }] }

04

소유권과 무결성은 저장 경계에서 확인합니다

서버가 발급한 UUID마다 meta.json, 번호가 붙은 청크, 조립 파일을 담는 디렉터리가 생깁니다. 메타데이터에는 소유자와 선언값을 기록합니다. 쓰기·완료·claim·중단 요청은 매번 이 메타데이터를 읽어 인증 사용자와 비교합니다.

경로처럼 생긴 ID, 빈 청크, 상한을 넘는 청크, 범위 밖 인덱스, 누락된 조각, 선언 크기와 다른 합계를 거절합니다. 모든 청크를 붙인 뒤에만 partial 파일을 원본 이름으로 바꾸며, 24시간 동안 claim되지 않은 세션은 새 업로드가 시작될 때 기회적으로 정리합니다.

05

브라우저는 제품 동작이 아니라 전송 방식만 바꿉니다

바이너리 합계가 60MB를 넘으면 웹 클라이언트가 파일을 24MB씩 전송합니다. 이 크기는 API 원본 파서의 32MB 상한 아래에 있어 여유를 남깁니다. 작은 입력은 추가 왕복 없이 기존 멀티파트를 계속 사용합니다.

완료 후 작업 생성 요청은 uploadId만 담은 작은 JSON이 됩니다. 이후 문서 추출과 에이전트 실행 경로는 이전과 같습니다.

06

유닛·API·브라우저 경계를 모두 검증했습니다

기능 커밋에는 청크 스토어 유닛 테스트 7개와 에이전트 작업 테스트 130개 통과, TypeScript와 ESLint 오류 0건이 기록돼 있습니다. 원본 복원, 멱등 완료, 사용자 격리, UUID 경로 방어, 청크 누락, 크기 불일치, 인덱스 오류, 완료 전 claim을 검사했습니다.

운영 API 검증에서는 130MB 파일을 여섯 청크로 chat.openmake.cc에 전송하고 작업 생성 후 저장본 SHA-256이 원본과 같은지 확인했습니다. 브라우저 검증에서는 65MB PDF를 첨부해 청크 세 번, 업로드 참조 생성 성공, 작업 실행 요청 수락까지 관찰했습니다.

07

받아들인 트레이드오프

디스크 기반 설계는 단순하고 애플리케이션 재시작에도 파일이 남습니다. 하지만 여러 인스턴스로 확장하려면 공유 업로드 볼륨이나 세션 고정이 필요합니다. 조립은 서버 청크 하나씩 직렬로 처리해 최고 처리량보다 예측 가능한 메모리 사용을 택했습니다.

선언 크기로 누락과 추가 바이트는 찾지만, 공개 프로토콜은 아직 파일 체크섬을 받지 않습니다. 운영 테스트에서는 외부에서 바이트 동일성을 증명했지만, 다음 버전에서는 이 보장을 완료 단계 자체에 넣어야 합니다.

08

다음 단계

새로고침 뒤 이미 받은 인덱스를 브라우저가 조회할 수 있는 상태 API가 다음으로 유용합니다. 파일 체크섬, 다중 인스턴스를 위한 오브젝트 스토리지, 더 분명한 일시정지·재개 진행 표시를 추가하면 현재의 재시도 가능한 전송을 완전한 재개형 업로드로 확장할 수 있습니다.

근거 자료

근거 자료

개발일지로 돌아가기