파일 업로드: Base64 vs FormData vs 클라우드 스토리지 직접 업로드, 어느 게 가장 빠를까
파일 업로드 방식 3가지(Base64, FormData, 클라우드 직접 업로드)의 성능·보안·구현 복잡도를 비교. 파일 크기별 최적 선택 기준을 제시합니다.
파일 업로드 방식, 무엇을 선택할 것인가?
웹 애플리케이션에서 파일을 다루는 방식은 선택지가 여럿이다. 사용자 프로필 사진을 받을 때, 문서를 저장할 때마다 개발자는 고민한다: Base64로 인코딩할까, FormData로 보낼까, 아니면 S3 같은 클라우드 스토리지에 직접 업로드하게 할까?
각 방식은 속도, 보안, 서버 부하, 구현 난이도에서 뚜렷한 차이를 보인다. 이 글에서는 예시 시나리오를 기준으로 세 방식을 나란히 놓고 비교한다.
Base64: 간단하지만 느린 선택
Base64 인코딩은 가장 직관적이다. 파일을 읽어 문자열로 변환한 뒤 JSON 본문에 담아 보내면 된다.
const file = document.querySelector('input[type="file"]').files[0];
const reader = new FileReader();
reader.onload = (e) => {
const base64String = e.target.result;
fetch('/api/upload', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ file: base64String })
});
};
reader.readAsDataURL(file);
문제는 크기다. Base64 인코딩은 원본 크기보다 약 33% 커진다. 1MB 파일은 1.33MB가 되어 네트워크를 타고 간다. 메모리에도 전체 파일이 로드되므로, 큰 파일(100MB 이상)은 브라우저가 먹통이 될 수 있다.
보안 측면에서는 Base64가 암호화가 아니라 단순한 인코딩일 뿐이라는 점을 기억해야 한다. HTTPS 없이는 어떤 방식이든 전송 내용을 누구나 읽을 수 있다. 또한 서버가 모든 파일 처리를 담당해야 해서 서버 CPU와 메모리 사용량이 늘어난다.
언제 쓸까: 프로필 사진처럼 1MB 미만의 작은 파일, 그리고 빠른 프로토타입이 필요할 때만.
FormData: 균형잡힌 중간 선택
FormData는 multipart/form-data 형식으로 파일을 보낸다. Base64보다 효율적이고, 구현도 간단하다.
const file = document.querySelector('input[type="file"]').files[0];
const formData = new FormData();
formData.append('file', file);
fetch('/api/upload', {
method: 'POST',
body: formData
});
FormData는 원본 크기 그대로 전송되므로 Base64보다 전송량이 적다. 메모리도 스트리밍 방식으로 처리할 수 있어 대용량 파일도 가능하다.
다만 여전히 서버가 파일 수신과 저장을 모두 처리해야 한다. 트래픽이 몰리면 서버 대역폭이 병목이 된다. 또한 업로드 중 네트워크가 끊기면 처음부터 다시 해야 한다.
언제 쓸까: 10MB 이하의 이미지·문서, 그리고 서버 인프라가 충분할 때.
클라우드 스토리지 직접 업로드: 빠르고 확장성 있는 선택
S3나 Google Cloud Storage처럼 클라우드 스토리지에 직접 업로드하는 방식이다. 서버는 업로드 권한을 주는 서명된 URL만 발급하고, 클라이언트가 직접 스토리지에 보낸다.
// 1단계: 서버에서 서명된 URL 받기
const response = await fetch('/api/get-upload-url', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ fileName: 'photo.jpg', fileSize: 5242880 })
});
const { uploadUrl } = await response.json();
// 2단계: 클라이언트가 직접 S3에 업로드
const file = document.querySelector('input[type="file"]').files[0];
await fetch(uploadUrl, {
method: 'PUT',
headers: { 'Content-Type': file.type },
body: file
});
이 방식의 장점은 서버를 우회한다는 것이다. 파일이 앱 서버를 거치지 않고 클라이언트에서 스토리지로 바로 올라가므로, 서버 대역폭과 처리 부담이 줄어든다.
업로드 시간은 사용자의 네트워크 환경에 따라 크게 달라지지만, 서버가 병목이 되지 않는다는 점은 분명한 차이다. 특히 멀티파트 업로드를 쓰면 중단된 구간부터 이어서 업로드할 수 있다.
단점은 초기 구현이다. 서버에서 IAM 정책, 서명된 URL 생성 로직을 짜야 한다. CORS 설정도 필요하다. 하지만 한 번 구축하면 파일 크기에 관계없이 안정적이다.
언제 쓸까: 100MB 이상의 대용량 파일, 높은 동시 업로드, 비용 최적화가 필요할 때.
실제 선택 기준
| 방식 | 파일 크기 | 속도 | 서버 부하 | 보안 | 구현 난이도 |
|---|---|---|---|---|---|
| Base64 | 작은 파일 | 느림 | 높음 | 서버에서 검증 | 매우 쉬움 |
| FormData | 중간 크기 | 중간 | 중간 | 서버에서 검증 | 쉬움 |
| 클라우드 직접 업로드 | 대용량 | 빠름 | 매우 낮음 | 서명 URL·권한 설정 필요 | 어려움 |
프로토타입이라면 FormData로 시작하자. 사용자가 늘고 파일이 커지면 클라우드 스토리지로 마이그레이션하면 된다.
보안 체크리스트
세 방식 모두 HTTPS를 필수로 쓴다. Base64나 FormData는 서버에서 파일 타입을 검증해야 한다(확장자나 클라이언트가 보낸 Content-Type만 믿지 말고 파일 내용 기준으로). 클라우드 스토리지는 서명된 URL의 만료 시간을 짧게(5~15분) 설정하고, 접근 권한을 최소한으로 제한한다.
파일 크기별로 어떤 방식이 가장 빠를까요?
작은 파일은 Base64도 충분하지만, 전송량과 서버 부담 면에서 FormData가 대체로 낫다. 중간 크기 파일은 FormData의 영역이고, 파일이 아주 크거나 동시 업로드가 많아지면 클라우드 직접 업로드를 고려해야 한다. 클라우드 직접 업로드는 멀티파트 업로드를 지원하므로 대용량 파일도 중단된 구간부터 이어서 올릴 수 있다.
FormData로 업로드하면 진짜 안전한가요?
HTTPS를 쓰고, 서버에서 파일 타입을 검증하고, 바이러스 스캔을 추가하면 위험을 크게 줄일 수 있다. 다만 대역폭 비용과 서버 저장소 관리가 부담이 될 수 있다는 점은 고려해야 한다.
클라우드 스토리지 직접 업로드는 얼마나 복잡한가요?
S3 기준으로 보면, 서버 코드는 비교적 짧다. boto3(Python) 또는 AWS SDK(Node.js)로 서명된 URL을 생성하는 함수 하나면 충분하다. 클라이언트는 fetch로 PUT 요청만 보내면 되므로 실제로는 간단하다. 처음에는 어려워 보이지만, 한 번 구축하면 모든 파일 업로드에 재사용할 수 있다.
파일 업로드 방식은 애플리케이션의 규모와 함께 진화한다. 지금 당신의 프로젝트에 가장 맞는 방식을 선택하되, 나중에 바꿀 수 있다는 점을 기억하자.
업로드 기능을 구현했다면, 그 결과를 Vibeollio에 프로젝트로 등록해서 다른 개발자들의 피드백을 받아보자. 실제 사용자 반응이 성능 최적화의 가장 좋은 지표가 될 것이다.
