Javascript

대용량 파일 업로드 최적화 방법: 초보자도 쉽게 따라 하는 6가지 전략

드리프트2 2025. 3. 24. 23:23

대용량 파일 업로드 최적화 방법: 초보자도 쉽게 따라 하는 6가지 전략

1. 왜 대용량 파일 업로드를 최적화해야 하나요?

프론트엔드 개발을 하다 보면 이미지, 영상, 오디오 파일 등을 업로드해야 하는 상황을 자주 마주치게 됩니다.

그런데 파일 크기가 너무 크면 다음과 같은 문제가 생길 수 있는데요:

  • 업로드 시간이 너무 오래 걸려 사용자 경험이 떨어집니다.
  • 서버에 과도한 부하가 걸려 리소스가 낭비됩니다.
  • 네트워크가 불안정할 때 업로드가 실패해 다시 시도해야 하는 번거로움이 생깁니다.
  • 브라우저 메모리 사용량이 급증해 성능이 저하될 수 있습니다.

이런 문제를 해결하려면 대용량 파일 업로드를 최적화하는 것이 필수입니다.

특히 요즘 인터넷 환경에서는 사용자들의 파일 업로드 요구가 점점 더 커지고 있는데요.

예를 들어:

  • SNS에서는 고화질 사진이나 영상을 공유하고,
  • 교육 플랫폼에서는 과제나 강의 자료를 제출하며,
  • 기업용 플랫폼에서는 프로젝트 문서나 보고서를 업로드합니다.

이때 업로드해야 하는 파일 크기가 수백 메가바이트에서 수 기가바이트에 이를 때도 많습니다.

이런 상황에서 전통적인 파일 업로드 방식을 사용하면 앞서 언급한 문제들이 더욱 심각해질 수밖에 없는데요.

전통적인 파일 업로드 방식은 파일 전체를 하나의 요청 본문으로 서버에 전송하는 방식입니다.

이 방식의 단점은 다음과 같습니다:

  1. 업로드 시간이 오래 걸립니다: 파일 크기가 크기 때문에 데이터 전송에 오랜 시간이 소요되어, 사용자가 업로드 결과를 확인하려면 한참을 기다려야 합니다.
  2. 서버 부하가 커집니다: 대용량 파일을 한 번에 처리하려면 서버의 메모리, CPU, 대역폭 등 리소스가 과도하게 소모됩니다.
  3. 네트워크 불안정에 취약: 대용량 파일 전송 중 네트워크 연결이 끊기거나, 타임아웃이 발생하거나, 패킷이 손실되면 업로드가 실패해 처음부터 다시 업로드해야 합니다.
  4. 브라우저 메모리 사용량이 늘어납니다: 대용량 파일을 브라우저가 메모리에 한 번에 읽어 들이고 연결을 유지해야 하므로, 메모리 사용량이 급증해 다른 페이지의 성능에도 영향을 줄 수 있습니다.

따라서 대용량 파일 업로드를 최적화하는 것은 필수입니다.


2. 대용량 파일 업로드 최적화 방법

대용량 파일 업로드를 최적화하는 주요 방법은 다음과 같습니다:

1) 파일 나누기 (Chunking)

큰 파일을 작은 조각(청크)으로 나눠서 각 청크를 별도의 요청으로 서버에 전송합니다.

이렇게 하면 한 번에 전송하는 데이터 양이 줄어들어 업로드 시간이 단축되고, 서버 부하도 감소하며, 업로드 중간에 실패해도 다시 시작할 수 있습니다.

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) 동시 업로드 (Concurrency)

여러 청크 요청을 동시에 서버에 보내 네트워크 대역폭과 서버 리소스를 최대한 활용해 사용자 경험을 개선합니다.

async function uploadChunks(fileChunks) {
  const uploadPromises = fileChunks.map((chunk) =>
    fetch('/upload', { method: 'POST', body: chunk })
  );
  const responses = await Promise.all(uploadPromises);
  return responses;
}

 

3) 파일 압축 (Compression)

각 청크를 서버에 보내기 전에 압축해 데이터 크기를 더 줄이고 전송 효율을 높입니다.

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) 업로드 검증 (Validation)

각 청크를 전송하기 전이나 후에 검증해 데이터 무결성과 정확성을 확인합니다.

이를 통해 중복되거나 잘못된 데이터 전송을 방지할 수 있습니다.

async function verifyChunk(chunk) {
  const hash = await calculateHash(chunk);
  const response = await fetch(`/verify?hash=${hash}`);
  const result = await response.json();
  return result;
}

 

5) 이어 올리기 (Resumable Uploads)

네트워크가 끊겼을 때 처음부터 다시 업로드하는 대신, 중단된 지점부터 이어서 업로드해 시간과 속도를 절약할 수 있습니다.

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) 즉시 업로드 (Instant Uploads)

파일을 나누기 전에 먼저 해시를 계산해 서버에 전송합니다.

서버에 동일한 파일이 있으면 바로 성공 응답을 반환해 중복 업로드를 방지합니다.

async function checkFileExists(file) {
  const hash = await calculateHash(file);
  const response = await fetch(`/check?hash=${hash}`);
  const result = await response.json();
  return result;
}

3. 마무리

이번 글에서는 대용량 파일 업로드를 왜 최적화해야 하는지, 그리고 주요 최적화 전략이 무엇인지 알아보았습니다.

코드 예시를 통해 각 최적화 방법을 실제로 어떻게 구현할 수 있는지도 살펴봤는데요.

이 글이 대용량 파일 업로드 최적화의 개념을 이해하고, 실제로 적용하는 데 도움이 되었길 바랍니다!