대용량 파일 업로드를 정신 놓지 않고 처리하는 방법
Lukas Schneider
DevOps Engineer · Leapcell

대용량 파일 업로드를 최적화해야 하는 이유
프론트엔드 개발에서 이미지, 비디오, 오디오 파일 등 파일 업로드가 필요한 시나리오를 자주 접하게 됩니다. 파일 업로드 중 파일이 너무 커서 다음과 같은 문제가 발생할 수 있습니다.
- 업로드 시간이 오래 걸리고 사용자 경험이 저하됩니다.
- 과도한 서버 부하 및 리소스 소모를 유발합니다.
- 불안정한 네트워크 환경에서 업로드 실패가 발생하여 재업로드가 필요하고 시간과 대역폭이 낭비됩니다.
- 높은 브라우저 메모리 사용량으로 인해 성능과 안정성에 영향을 미칩니다.
이러한 문제를 해결하려면 대용량 파일 업로드를 최적화해야 합니다.
최근 인터넷 애플리케이션에서 사용자는 파일 업로드에 대한 요구 사항이 점점 더 높아지고 있습니다. 예를 들면 다음과 같습니다.
- 소셜 플랫폼에서 고해상도 이미지 및 비디오 공유.
- 교육 플랫폼에서 과제 및 강의 자료 제출.
- 엔터프라이즈 플랫폼에서 프로젝트 문서 및 보고서 업로드.
이러한 시나리오에서 사용자가 업로드해야 하는 파일은 종종 매우 크며, 때로는 수백 메가바이트 또는 심지어 수 기가바이트에 달하기도 합니다. 기존 파일 업로드 방법을 사용하면 앞서 언급한 문제에 직면하게 됩니다.
기존 파일 업로드 방법은 전체 파일을 단일 요청 본문으로 서버에 보내는 것입니다. 이 접근 방식에는 다음과 같은 몇 가지 단점이 있습니다.
- 긴 업로드 시간: 파일 크기가 크기 때문에 데이터 전송에 오랜 시간이 걸리므로 사용자는 업로드 결과를 보기 위해 오랫동안 기다려야 합니다.
- 높은 서버 부하: 대용량 파일은 서버가 한 번에 많은 양의 데이터를 수신하고 처리해야 하므로 과도한 서버 메모리, CPU, 대역폭 및 기타 리소스를 소비할 수 있습니다.
- 불안정한 네트워크에서 실패하기 쉬움: 대용량 파일 전송은 연결 끊김, 시간 초과 또는 패킷 손실과 같은 네트워크 문제에 취약하여 업로드 실패로 이어지고 사용자가 전체 파일을 다시 업로드해야 합니다.
- 높은 브라우저 메모리 사용량: 대용량 파일은 브라우저가 전체 파일을 메모리에 읽어들이고 연결을 유지해야 하므로 높은 메모리 사용량으로 이어지고 다른 페이지의 성능에 영향을 미칠 수 있습니다.
이러한 문제를 해결하려면 대용량 파일 업로드를 최적화하는 것이 필수적입니다.
설계 접근 방식
대용량 파일 업로드를 최적화하는 주요 접근 방식은 다음과 같습니다.
1. 청크 분할
대용량 파일을 더 작은 청크로 나누고 각 청크를 서버에 대한 별도의 요청으로 보냅니다. 이렇게 하면 요청당 데이터 양이 줄어들고 업로드 시간이 단축되며 서버 부하가 줄어들고 재개 가능한 업로드가 가능해집니다.
function sliceFile(file, chunkSize) { const fileSize = file.size; const chunks = Math.ceil(fileSize / chunkSize); const slices = Array.from({ length: chunks }, (_, index) => { const start = index * chunkSize; const end = start + chunkSize; return file.slice(start, end); }); return slices; }
2. 동시성
여러 청크 요청을 서버에 동시에 보내 네트워크 대역폭과 서버 리소스를 최대한 활용하여 사용자 경험을 개선합니다.
async function uploadChunks(fileChunks) { const uploadPromises = fileChunks.map((chunk) => fetch('/upload', { method: 'POST', body: chunk }) ); const responses = await Promise.all(uploadPromises); return responses; }
3. 압축
서버에 보내기 전에 각 청크를 압축하여 데이터 크기를 더욱 줄이고 전송 효율성을 높입니다.
async function compressChunk(chunk) { const compressedChunk = await new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (event) => { const result = pako.deflate(event.target.result); resolve(result); }; reader.onerror = (event) => reject(event.error); reader.readAsArrayBuffer(chunk); }); return compressedChunk; }
4. 유효성 검사
데이터 무결성 및 정확성을 보장하기 위해 전송 전후에 각 청크를 검증하여 중복되거나 잘못된 데이터 전송을 방지합니다.
async function verifyChunk(chunk) { const hash = await calculateHash(chunk); const response = await fetch(`/verify?hash=${hash}`); const result = await response.json(); return result; }
5. 재개 가능한 업로드
네트워크 오류가 발생한 경우 처음부터 다시 시작하는 대신 중단된 지점부터 계속 업로드하여 시간을 절약하고 속도를 향상시킵니다.
async function resumeUpload(file, resumeByte) { const blob = file.slice(resumeByte); const formData = new FormData(); formData.append('file', blob); const response = await fetch('/upload', { method: 'POST', body: formData }); const result = await response.json(); return result; }
6. 즉시 업로드
파일을 분할하여 업로드하기 전에 해당 해시를 계산하여 서버에 보냅니다. 서버가 동일한 파일을 찾으면 즉시 성공 응답을 반환하여 중복 업로드를 방지할 수 있습니다.
async function checkFileExists(file) { const hash = await calculateHash(file); const response = await fetch(`/check?hash=${hash}`); const result = await response.json(); return result; }
요약
이 문서에서는 대용량 파일 업로드를 최적화해야 하는 이유와 주요 최적화 전략을 소개합니다. 코드 예제를 통해 이러한 최적화 방법을 구현하는 방법을 보여줌으로써 독자가 대용량 파일 업로드 최적화를 위한 솔루션을 이해하고 적용할 수 있도록 하는 것을 목표로 합니다.
Leapcell은 백엔드 프로젝트 호스팅을 위한 최고의 선택입니다. 최대 100MB의 파일 업로드를 지원합니다!
Leapcell은 웹 호스팅, 비동기 작업 및 Redis를 위한 차세대 서버리스 플랫폼입니다.
다국어 지원
- Node.js, Python, Go 또는 Rust로 개발하세요.
무제한 프로젝트를 무료로 배포하세요
- 사용량에 대해서만 지불하세요. 요청도 없고 요금도 없습니다.
탁월한 비용 효율성
- 사용한 만큼 지불하고 유휴 요금은 없습니다.
- 예: $25는 평균 응답 시간 60ms에서 694만 건의 요청을 지원합니다.
간소화된 개발자 경험
- 간편한 설정을 위한 직관적인 UI.
- 완전 자동화된 CI/CD 파이프라인 및 GitOps 통합.
- 실행 가능한 통찰력을 위한 실시간 메트릭 및 로깅.
손쉬운 확장성 및 고성능
- 고도의 동시성을 쉽게 처리할 수 있는 자동 확장.
- 운영 오버헤드가 전혀 없으므로 구축에만 집중하세요.
설명서에서 자세히 알아보세요!
X에서 팔로우하세요: @LeapcellHQ