AWS S3·CloudFront 파일 업로드 상한 정리|멀티파트·서명된 URL
AWS를 사용한 파일 업로드 기능을 구축할 때 S3, CloudFront, API Gateway, Lambda는 각각 다른 크기 제한이 있으며, 어느 계층이 병목이 될 수 있는지 파악하는 것이 매우 중요합니다. 본 문서에서는 각 서비스의 제한값을 정리하고, 대용량 파일을 처리하기 위한 멀티파트 업로드 및 서명된 URL의 사용 방법, PHP 구현 예제까지 설명합니다.
Amazon S3 업로드 제한
S3로의 객체 업로드는 방식에 따라 상한 크기가 다릅니다.
| 업로드 방식 | 최대 크기 | 비고 |
|---|---|---|
| PUT Object (단일) | 5 GB | 1요청당 업로드 상한 |
| Multipart Upload | 5 TB | 1개 객체의 최대 크기 |
| 1파트당 | 최소 5 MB ~ 최대 5 GB | 마지막 파트만 5 MB 미만 가능 |
| 파트 수 | 최대 10,000개 파트 | 멀티파트 시 상한 |
AWS는 100 MB 이상의 파일에 멀티파트 업로드를 권장하며, 네트워크 장애 시 재전송 효율과 병렬 전송을 통한 처리량 향상 측면에서도 대용량 파일에 적극적으로 사용해야 합니다.
멀티파트 업로드가 필요한 경우
멀티파트 업로드는 하나의 객체를 여러 파트로 나누어 각각 독립적인 요청으로 송신한 후 마지막에 합치는 방식입니다. 다음과 같은 경우에 효과적입니다.
- <strong>100 MB 이상의 파일</strong>: AWS가 권장하는 멀티파트 전환 라인입니다.
- <strong>네트워크가 불안정한 환경</strong>: 중간에 오류가 발생해도 실패한 파트만 재전송 가능합니다.
- <strong>처리량 최대화</strong>: 여러 파트를 병렬로 업로드하여 전송 속도 향상.
- <strong>업로드 일시 중지·재개</strong>: Upload ID를 저장해두면 세션을 걸쳐 재개 가능합니다.
멀티파트 업로드의 흐름은 크게 3단계입니다.
- <strong>시작</strong>(CreateMultipartUpload): Upload ID를 가져옵니다
- <strong>파트 전송</strong>(UploadPart): 각 파트를 업로드하고 ETag 기록
- <strong>완료</strong>(CompleteMultipartUpload): 파트 번호와 ETag 목록을 전송하여 객체를 확정합니다
<code>AbortMultipartUpload</code>를 호출하지 않고 중단하면 완료되지 않은 파트가 스토리지 요금으로 계속 청구됩니다. S3 버킷의 라이프사이클 정책에서 "완료되지 않은 멀티파트 업로드를 N일 후 삭제"하도록 설정할 것을 강력히 권장합니다.
Presigned URL의 유효 기간 및 크기
서명된 URL (Presigned URL)을 사용하면 S3 접근 권한이 없는 클라이언트 (브라우저·모바일 앱 등)에서 직접 S3로 파일을 업로드할 수 있습니다. 서버를 중계하지 않으므로 대용량 파일도 효율적입니다.
| 항목 | 제한・사양 |
|---|---|
| 최대 유효 기간 (IAM 사용자 발급) | 7일(604,800초) |
| 최대 유효 기간 (IAM 역할 발급) | 역할의 세션 시간 내 (최대 12시간) |
| 업로드 가능한 파일 크기 | 단일 PUT의 경우 최대 5GB |
| 멀티파트 지원 | 각 부분에 개별 Presigned URL 발급 |
Presigned URL은 백엔드에서 생성되어 클라이언트에 반환되며, 클라이언트는 해당 URL에 직접 PUT 요청을 보냅니다. URL에는 서명이 포함되어 있으므로 유효 기간 내에 올바른 파일을 업로드할 때만 성공합니다.
CloudFront의 body size 제한
CloudFront를 통해 오리진(S3나 EC2 등)으로 요청을 전달할 때 요청 본문의 크기에 제한이 있습니다.
| 설정 | 기본값 | 최대값 |
|---|---|---|
| 요청 본문 크기 (기본값) | 1 MB | 배포 설정에서 변경 가능 |
| Lambda@Edge를 통한 경우 | 1 MB | 1 MB (변경 불가) |
| CloudFront Functions의 경우 | — | 요청 본문에 접근할 수 없음 |
CloudFront 배포 설정에서 「Allow requests with body」를 활성화하고 상한을 높일 수 있지만, Lambda@Edge에서는 1 MB 상한을 변경할 수 없음을 주의하세요. 대용량 파일 업로드의 경우 CloudFront를 거치지 않고 Presigned URL을 사용하여 클라이언트에서 S3로 직접 업로드하는 아키텍처를 채택하기를 권장합니다.
API Gateway의 Payload 제한
API Gateway(REST API / HTTP API 모두)에는 페이로드 크기 상한이 있습니다.
| API 유형 | 최대 페이로드 크기 | 비고 |
|---|---|---|
| REST API | 10 MB | 바이너리 지원을 활성화한 경우도 동일 |
| HTTP API | 10 MB | — |
| WebSocket API | 128 KB (메시지) / 32 KB (프레임) | — |
API Gateway의 10 MB 제한은 AWS 서비스 할당량에 분류되며 완화 신청이 불가능합니다. API Gateway를 통해 10 MB를 초과하는 파일을 업로드하는 것은 설계 오류로 간주되므로, Presigned URL 패턴으로의 마이그레이션을 검토하세요.
Lambda를 통한 파일 제한
Lambda 함수에서 파일을 수신할 때 API Gateway에서 전달되는 이벤트 페이로드에 크기 제한이 있습니다. 또한 Lambda 자체에도 다음과 같은 제약이 있습니다.
| 제한 항목 | 값 |
|---|---|
| 동기 호출(Request) 페이로드 | 6 MB |
| 동기 호출(Response) 페이로드 | 6 MB |
| 비동기 호출 페이로드 | 256 KB |
| <code>/tmp</code> 디렉토리 용량 | 기본값 512 MB (최대 10 GB) |
| 최대 실행 시간 | 15분 |
Lambda에서 파일을 처리할 때 임시 파일은 <code>/tmp</code>에 기록됩니다. 기본값은 512 MB이지만 Lambda 설정을 통해 에머럴 스토리지를 최대 10 GB까지 확장할 수 있습니다 (추가 요금 적용).
PHP에서 S3 멀티파트 업로드 코드 예제
AWS SDK for PHP(v3)를 사용한 멀티파트 업로드의 구현 예제입니다. SDK의 <code>MultipartUploader</code> 클래스를 사용하면, 청크 분할·재시도·완료 처리를 자동으로 처리해줍니다.
use Aws\S3\S3Client;
use Aws\S3\MultipartUploader;
use Aws\Exception\MultipartUploadException;
$s3 = new S3Client([
'region' => 'ap-northeast-1',
'version' => 'latest',
]);
$uploader = new MultipartUploader($s3, '/path/to/large-file.mp4', [
'bucket' => 'your-bucket-name',
'key' => 'uploads/' . basename('/path/to/large-file.mp4'),
'before_initiate' => function (\Aws\Command $command) {
// アップロード開始前のフック
},
'before_upload' => function (\Aws\Command $command) {
// 各パートアップロード前のフック(プログレス表示など)
},
'concurrency' => 5, // 並列アップロード数
'part_size' => 10 * 1024 * 1024, // 1パート = 10 MiB
]);
try {
$result = $uploader->upload();
echo 'アップロード完了: ' . $result['ObjectURL'];
} catch (MultipartUploadException $e) {
// 失敗したパートから再開する例
$uploader = new MultipartUploader($s3, '/path/to/large-file.mp4', [
'state' => $e->getState(),
]);
$result = $uploader->upload();
}
Presigned URL을 발급하여 클라이언트가 직접 S3에 업로드하도록 하는 방법은 다음과 같습니다.
use Aws\S3\S3Client;
$s3 = new S3Client([
'region' => 'ap-northeast-1',
'version' => 'latest',
]);
// 署名付きURLを生成(有効期限: 15分)
$cmd = $s3->getCommand('PutObject', [
'Bucket' => 'your-bucket-name',
'Key' => 'uploads/' . uniqid('', true) . '.jpg',
'ContentType' => 'image/jpeg',
]);
$request = $s3->createPresignedRequest($cmd, '+15 minutes');
$presignedUrl = (string) $request->getUri();
// このURLをクライアントに返す
// クライアントはこのURLに PUT リクエストでファイルを直接送信する
echo json_encode(['upload_url' => $presignedUrl]);
아키텍처 선택 가이드라인
- <strong>~10 MB</strong>: API Gateway → Lambda → S3로 간단하게 구현 가능
- <strong>10 MB~100 MB</strong>: Presigned URL로 클라이언트에서 S3로 직접 업로드를 권장합니다.
- <strong>100 MB~5 GB</strong>: Presigned URL + 멀티파트 업로드로 안정적인 전송 구현
- <strong>5 GB 초과</strong>: 멀티파트 업로드 필수 (단독 PUT 불가)
DevLab 임계값 파일을 사용한 테스트 절차
실제로 <code>S3</code> 업로드 설정을 검증하려면 정확한 크기의 테스트 파일이 필요합니다. DevLab의 경계값 테스트 파일을 사용하면 특정 크기 전후의 동작을 효율적으로 확인할 수 있습니다.
- DevLab의 <a href="/ja/files/threshold/">경계값 테스트 파일 목록</a>에서 테스트하려는 크기의 파일을 다운로드합니다.
- 멀티파트 전환 기준을 검증할 때는 <a href="/ja/files/images/png/">PNG 테스트 이미지</a>의 대형 버전도 활용할 수 있습니다.
- <code>aws s3 cp</code> 명령어 또는 SDK의 <code>PutObject</code> / <code>MultipartUploader</code>를 사용하여 다운로드한 파일을 업로드하고, 크기 제한 전후의 파일에서 성공 여부를 확인합니다.
이 기사에서 사용할 수 있는 테스트 파일
- <a href="/ja/files/threshold/" class="text-primary-600 dark:text-primary-400 hover:underline">경계값 테스트 파일 목록</a> — S3, API Gateway, Lambda 각 상한 검증용
- <a href="/ja/files/threshold/10mb/" class="text-primary-600 dark:text-primary-400 hover:underline">10MB 경계값 테스트 세트</a> — API Gateway 10MB 상한 직전·직후 확인
- <a href="/ja/files/threshold/25mb/" class="text-primary-600 dark:text-primary-400 hover:underline">25MB 경계값 테스트 세트</a> — 멀티파트 권장 라인 전후의 동작 확인용
- <a href="/ja/files/images/png/" class="text-primary-600 dark:text-primary-400 hover:underline">PNG 테스트 이미지 목록</a> — 이미지 업로드 파이프라인 동작 확인용
관련 기사
- <a href="/ja/blog/how-to-test-upload-limit/" class="text-primary-600 dark:text-primary-400 hover:underline">파일 업로드 크기 제한을 올바르게 테스트하는 방법</a>
- <a href="/ja/blog/php-file-upload/" class="text-primary-600 dark:text-primary-400 hover:underline">PHP에서 파일 업로드를 구현하는 방법|검증·저장·보안 완벽 가이드</a>
- <a href="/ja/blog/multipart-form-data-overhead/" class="text-primary-600 dark:text-primary-400 hover:underline">multipart/form-data 오버헤드를 정확하게 계산하기</a>
- <a href="/ja/blog/mb-vs-mib-file-size/" class="text-primary-600 dark:text-primary-400 hover:underline">MB와 MiB는 다르다! 파일 크기 단위의 함정</a>