Hook
S3는 단순한 파일 저장소가 아닙니다. 2006년 출시 이후 진화해 온 객체 스토리지 프로토콜이자, 클라우드 데이터 공유의 사실상 표준(de facto standard)입니다.
이 글에서는 S3의 핵심 데이터 모델부터 REST API, AWS CLI/SDK 실전, Pre-signed URL, 멀티파트 업로드, 이벤트 알림까지 한 번에 정리합니다.
TL;DR
- S3는 버킷/객체/키 3가지로 구성: 폴더가 아닌 평면(flat) 구조, prefix로 논리적 분류
- REST API + AWS CLI + SDK(Python/Java/Go)로 접근: 모든 HTTP 도구와 호환
- Pre-signed URL로 임시 접근 권한 위임: IAM 없이도 타인에게 업로드/다운로드 허용
- 멀티파트 업로드로 5TB 파일 처리: 3단계 프로세스로 병렬 업로드, 실패한 파트만 재시도
- 이벤트 알림으로 데이터 파이프라인 자동화: S3 → SQS/SNS/Lambda/EventBridge
S3란?: 프로토콜 관점
Amazon S3(Simple Storage Service)는 HTTP 기반 RESTful API로 동작하는 객체 스토리지입니다. 모든 조작은 표준 HTTP 메서드(PUT, GET, DELETE, HEAD, POST)로 이루어지며, 이 단순함이 S3 API를 업계 표준으로 만들었습니다.
핵심 구성 요소
| 구성 요소 | 설명 | 제약사항 |
|---|---|---|
| Bucket (버킷) | 객체의 컨테이너. 전역 고유 이름 필요 | 계정당 최대 100개(증가 요청 가능), 이름 3~63자 |
| Object (객체) | 데이터의 기본 단위. Key + Value + Metadata | 크기 0Byte~5TB, 단일 PUT 최대 5GB |
| Key (키) | 버킷 내 객체의 고유 식별자. 경로+파일명 형태 | 최대 1,024바이트 UTF-8 |
| Version ID | 버전 관리 활성화 시 특정 버전 식별자 | null 또는 자동 생성 ID |
| Metadata | 시스템 메타데이터 + 사용자 정의 메타데이터 | 사용자 메타데이터는 x-amz-meta- 접두사 |
S3의 “폴더”는 실제로 존재하지 않습니다. Key의 prefix(접두사)와 delimiter(
/)를 조합하여 폴더처럼 보이게 할 뿐입니다.
S3 API가 표준이 된 이유
| 특징 | 설명 |
|---|---|
| 단순성 | 6개의 HTTP 메서드로 모든 조작 표현 |
| 무상태성 | RESTful 설계로 세션 상태 미유지, 수평 확장 용이 |
| 범용성 | HTTP를 지원하는 모든 환경에서 사용 가능 |
| 확장성 | 단일 객체 0Byte~5TB, 버킷 내 객체 수 무제한 |
| 내구성 | 99.999999999%(11 nine) 객체 내구성 보장 |
데이터 일관성 모델
S3는 모든 리전에서 강력한 쓰기 후 읽기(read-after-write) 일관성을 제공합니다. 객체 업로드 직후 GET 요청을 보내면 최신 데이터가 반환됩니다.
스토리지 클래스 비교
| 스토리지 클래스 | 용도 | 비용 | 검색 지연 |
|---|---|---|---|
| Standard | 자주 접근하는 데이터 | 표준 | 즉시 |
| Standard-IA | 드물게 접근하지만 빠른 검색 필요 | 저렴 | 즉시 |
| One Zone-IA | 단일 AZ, 재생성 가능 데이터 | 더 저렴 | 즉시 |
| Glacier Instant | 아카이브, 즉시 검색 | 매우 저렴 | 즉시 |
| Glacier Flexible | 장기 아카이브 | 극히 저렴 | 1~5분~12시간 |
| Glacier Deep Archive | 최장기 아카이브 | 최저가 | 12~48시간 |
S3 인증과 엔드포인트
자격 증명 유형
S3 API 호출 시 모든 요청은 AWS Signature V4(SigV4)로 서명되어야 합니다. 목적에 따라 4가지 자격 증명 유형을 선택할 수 있습니다.
| 유형 | 구성 | 수명 | 용도 |
|---|---|---|---|
| 장기 자격 증명 | Access Key ID + Secret Access Key | 영구 (수동 삭제 시) | IAM 사용자, 서비스 계정 |
| 임시 자격 증명 (STS) | Access Key + Secret Key + Session Token | 15분 ~ 12시간 | 임시 접근, 교차 계정 |
| IAM 역할 (Role) | EC2/Lambda에 자동 부여 | 자동 갱신 | 서비스 간 접근 |
| Pre-signed URL | 서명이 포함된 URL | 최대 7일 (SigV4) | 임시 공유, 브라우저 접근 |
엔드포인트 유형
| 유형 | 형식 | 용도 | 비고 |
|---|---|---|---|
| Virtual-hosted | bucket.s3.region.amazonaws.com | 일반 접근 (권장) | DNS 기반 라우팅, 성능 최적 |
| Path-style | s3.region.amazonaws.com/bucket | 하위 호환 | 레거시, 신규 버킷은 지원 중단 |
| S3 Access Point | ap-account.s3-accesspoint.region.amazonaws.com | 공유/접근 관리 | 별칭 지원 |
| VPC Endpoint | vpce-xxx.s3.region.vpce.amazonaws.com | 프라이빗 접근 | 인터넷 없이 VPC 내부 접근 |
| Express One Zone | bucket.s3express-az.region.amazonaws.com | 고성능 | ms 단위 일관된 지연 |
AWS CLI 설정 예시
# 자격 증명 설정
aws configure
# Access Key ID, Secret Key, Region(ap-northeast-2), Output(json) 입력
# 또는 환경 변수로 설정
export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/...
export AWS_DEFAULT_REGION=ap-northeast-2
S3 API 실전
기본 조작 (CRUD)
AWS CLI는 aws s3(고수준)와 aws s3api(저수준) 두 가지 인터페이스를 제공합니다.
# 버킷 생성 및 목록
aws s3 mb s3://my-data-bucket --region ap-northeast-2
aws s3 ls
# 업로드 / 다운로드 / 동기화
aws s3 cp data.csv s3://my-data-bucket/data/
aws s3 cp s3://my-data-bucket/data/data.csv ./
aws s3 sync ./local-data s3://my-data-bucket/data/
# 삭제 및 버킷 제거
aws s3 rm s3://my-data-bucket/temp/ --recursive
aws s3 rb s3://my-data-bucket --force
Python Boto3로 동일한 작업을 수행할 수 있습니다.
import boto3
s3 = boto3.client('s3', region_name='ap-northeast-2')
# 업로드
s3.put_object(
Bucket='my-data-bucket',
Key='data/hello.txt',
Body=b'Hello, S3!',
ContentType='text/plain',
Metadata={'author': 'data-team'}
)
# 다운로드
resp = s3.get_object(Bucket='my-data-bucket', Key='data/hello.txt')
print(resp['Body'].read().decode('utf-8'))
Pre-signed URL
Pre-signed URL은 S3 객체에 대한 임시 접근 권한을 URL 자체에 인코딩하는 메커니즘입니다. URL에 서명이 포함되어 있어, 자격 증명 없이도 지정된 작업만 지정된 시간 동안 수행할 수 있습니다.
| 유형 | 용도 | HTTP Method | 만료 범위 |
|---|---|---|---|
| GET Pre-signed | 객체 읽기/다운로드 공유 | GET | 최대 7일 |
| PUT Pre-signed | 객체 업로드 공유 | PUT | 최대 7일 |
| POST Pre-signed | 조건부 업로드 (정책 문서) | POST | 최대 7일 |
# 다운로드용 URL (1시간 유효)
url = s3.generate_presigned_url(
'get_object',
Params={'Bucket': 'my-data-bucket', 'Key': 'data/report.csv'},
ExpiresIn=3600
)
# 업로드용 URL (30분 유효)
upload_url = s3.generate_presigned_url(
'put_object',
Params={'Bucket': 'my-data-bucket',
'Key': 'uploads/user-file.csv',
'ContentType': 'text/csv'},
ExpiresIn=1800
)
보안 팁: 최소 만료 시간을 설정하고, HTTPS를 필수로 사용하세요. PUT URL은 Content-Type과 Content-Length를 제한하여 악용을 방지할 수 있습니다.
멀티파트 업로드
대용량 파일(100MB 이상 권장)을 여러 파트로 분할하여 병렬로 업로드하는 메커니즘입니다. 단일 PUT 요청의 5GB 한계를 극복하고, 실패한 파트만 재시도할 수 있습니다.
| 항목 | 제한 |
|---|---|
| 최대 객체 크기 | 5TB (멀티파트 사용 시) |
| 파트 크기 | 5MB ~ 5GB (마지막 파트 제외) |
| 최대 파트 수 | 10,000개 |
3단계 프로세스는 다음과 같습니다:
- CreateMultipartUpload: 업로드 세션 시작,
UploadId발급 - UploadPart: 각 파트를 병렬 업로드,
ETag수신 - CompleteMultipartUpload: 모든 파트 조합하여 객체 완성
AWS CLI는 임계값 이상 파일을 자동으로 멀티파트 처리합니다. Boto3로 수동 제어하려면:
📖 Boto3 멀티파트 업로드 전체 코드 보기
```python import boto3, os from concurrent.futures import ThreadPoolExecutor, as_completed s3 = boto3.client('s3') def multipart_upload(bucket, key, file_path, part_size=100*1024*1024): file_size = os.path.getsize(file_path) # Step 1: 멀티파트 업로드 초기화 mpu = s3.create_multipart_upload(Bucket=bucket, Key=key) upload_id = mpu['UploadId'] # Step 2: 파일을 파트로 분할하여 병렬 업로드 def upload_part(part_num, start, end): with open(file_path, 'rb') as f: f.seek(start) data = f.read(end - start) resp = s3.upload_part( Bucket=bucket, Key=key, PartNumber=part_num, UploadId=upload_id, Body=data ) return {'PartNumber': part_num, 'ETag': resp['ETag']} parts = [] with ThreadPoolExecutor(max_workers=10) as executor: futures, offset, num = [], 0, 1 while offset < file_size: end = min(offset + part_size, file_size) futures.append(executor.submit(upload_part, num, offset, end)) offset, num = end, num + 1 for future in as_completed(futures): parts.append(future.result()) # Step 3: 멀티파트 업로드 완료 parts.sort(key=lambda x: x['PartNumber']) s3.complete_multipart_upload( Bucket=bucket, Key=key, UploadId=upload_id, MultipartUpload={'Parts': parts} ) print(f"업로드 완료: s3://{bucket}/{key}") ```S3 호환 API 생태계
S3 API는 클라우드 스토리지의 사실상 표준이 되었습니다. AWS 외부에서도 동일한 API로 데이터를 저장하고 공유할 수 있습니다.
| S3 호환 스토리지 | 특징 |
|---|---|
| MinIO | 오픈소스, 온프레미스 자체 호스팅 |
| Ceph RGW | 오픈소스, 분산 스토리지 통합 |
| VersityGW | 고성능 온프레미스 S3 게이트웨이 |
| Cloudflare R2 | egress 요금 없는 S3 호환 스토리지 |
| Google Cloud Storage | 호환 모드 지원 |
S3 호환 API를 사용하면 벤더 종속성(vendor lock-in)을 방지할 수 있습니다. 동일한 Boto3 코드로 AWS S3와 MinIO를 모두 제어할 수 있습니다.
# MinIO(온프레미스)에 동일한 Boto3 코드 사용
s3 = boto3.client('s3',
endpoint_url='https://minio.company.com:9000',
aws_access_key_id='minio-key',
aws_secret_access_key='minio-secret'
)
# AWS S3와 완전 동일한 API
s3.put_object(Bucket='shared-data', Key='report.csv', Body=data)
이벤트 기반 데이터 파이프라인
S3 이벤트 알림은 객체 업로드/삭제 등의 변화를 자동으로 다른 서비스에 전달합니다. 별도의 폴링 없이 데이터 파이프라인을 구축할 수 있습니다.
| 라우팅 대상 | 용도 |
|---|---|
| SNS | 이메일/SMS/HTTP 알림, 팬아웃 |
| SQS | 워커 프로세스 큐잉, 배치 처리 |
| Lambda | 데이터 처리/변환/분석 자동 실행 |
| EventBridge | 고급 필터링, 다중 타겟 라우팅 |
대표적인 활용 사례는 파일 업로드 → 자동 처리 파이프라인입니다. CSV가 버킷에 업로드되면 Lambda가 트리거되어 데이터를 정제하고 분석 결과를 저장합니다.
📖 EventBridge 규칙 설정 전체 코드 보기
```bash # EventBridge 규칙 생성 — 특정 버킷의 PutObject 이벤트 감지 aws events put-rule \ --name "S3DataUploadRule" \ --event-pattern '{ "source": ["aws.s3"], "detail-type": ["AWS API Call via CloudTrail"], "detail": { "eventSource": ["s3.amazonaws.com"], "eventName": ["PutObject", "CompleteMultipartUpload"], "requestParameters": { "bucketName": ["my-data-bucket"] } } }' # 규칙에 Lambda 타겟 추가 aws events put-targets \ --rule "S3DataUploadRule" \ --targets '[{ "Id": "ProcessUpload", "Arn": "arn:aws:lambda:ap-northeast-2:123456789012:function:process-upload" }]' ```주요 이벤트 유형은 다음과 같습니다:
| 이벤트 카테고리 | 대표 이벤트 |
|---|---|
| Object Created | s3:ObjectCreated:Put, CompleteMultipartUpload |
| Object Removed | s3:ObjectRemoved:Delete, DeleteMarkerCreated |
| Object Restore | s3:ObjectRestore:Post, Completed |
| Object Tagging | s3:ObjectTagging:Put |
마치며
S3를 처음 다룰 때 가장 놀랐던 점은 “폴더가 사실은 존재하지 않는다”는 사실이었습니다. aws s3 ls를 치면 마치 디렉토리처럼 보이지만, 그 이면은 평면적인 Key-Value 구조일 뿐입니다. 이 단순한 사실을 이해하고 나면, S3의 설계 철학 전체가 달라 보입니다. 파일 시스템의 복잡한 계층 구조를 버리고 오직 HTTP와 평면 네임스페이스만 남겼기 때문에, S3는 무한히 확장할 수 있고 어떤 환경에서든 동일한 방식으로 접근할 수 있습니다.
멀티파트 업로드와 Pre-signed URL을 배우면서 깨달은 것은, S3의 진짜 강점이 저장 용량이 아니라 접근 방식의 유연성이라는 점입니다. 5TB짜리 파일을 파트별로 병렬 업로드하고, 실패한 조각만 다시 보낼 수 있다는 것은 곧 “대용량 처리 = 인프라 문제가 아니라 패턴 문제”라는 뜻이었습니다. IAM 자격 증명 없이 URL 하나로 권한을 위임하는 Pre-signed URL도 마찬가지로, 보안과 편의성 사이의 균형을 프로토콜 수준에서 풀어낸 훌륭한 설계입니다.
S3 API가 이렇게 널리 표준이 된 이유는 결국 단순함에 있습니다. 6개의 HTTP 메서드로 모든 것을 표현하는 RESTful 설계 덕분에 MinIO, Cloudflare R2, VersityGW 같은 호환 생태계가 자연스럽게 성장했습니다. 앞으로 S3를 다루신다면, API 스펙만 익히는 것에 그치지 말고 이 단순함이 만들어낸 생태계 전체를 하나의 언어로 바라보시길 바랍니다.
S3 시리즈 1/7
첫 글입니다 AWS 데이터 공유 서비스 3종. Registry, Data Exchange, Access Points →
게이트웨이 On-promise 제품 팀에서 시스템 모니터링 및 관리를 쉽게 다가갈 수 있도록 하기 위한 업무를 하고 있습니다.
Contact: lhjnano@gmail.com