S3 Basics
분류: Layer 3 - AWS 인프라 & 보안
1. 한 줄 정의
섹션 제목: “1. 한 줄 정의”S3(Simple Storage Service)는 AWS의 객체 스토리지 서비스로, 파일을 무제한으로 저장하고 꺼낼 수 있는 클라우드 저장소이다.
2. 왜 중요한가
섹션 제목: “2. 왜 중요한가”이미지, 로그, 백업, 정적 파일, 데이터 아카이브 등 거의 모든 종류의 파일 저장에 S3가 쓰인다. AWS에서 가장 기본적이고 가장 많이 쓰이는 서비스 중 하나.
3. 핵심 개념
섹션 제목: “3. 핵심 개념”Bucket (버킷)
파일을 담는 최상위 컨테이너. 이름이 전 세계적으로 고유해야 한다.
Object (객체)
S3에 저장된 파일 하나. 파일 자체 + 메타데이터로 구성. Key(경로)로 식별한다.
s3://my-bucket/images/profile.jpg→ Bucket: my-bucket, Key: images/profile.jpg
S3가 내부적으로 어떻게 동작하는가
비유: S3는 “무한히 확장되는 클라우드 창고”이다. 일반 파일시스템(폴더 구조)과 달리, S3는 Flat(평탄한) 구조이다.
핵심 차이:
- 일반 파일시스템: 실제 폴더가 존재함
- S3: “폴더”는 없음.
images/profile.jpg는images/라는 폴더가 아니라,images/profile.jpg라는 이름의 Key를 가진 객체 1개
콘솔에서 폴더처럼 보이는 건 AWS 콘솔이 /를 기준으로 시각적으로 분리해서 표시해주는 것이다. 실제로는 Key가 긴 객체일 뿐이다.
데이터 내구성: S3는 내부적으로 데이터를 여러 AZ에 분산 저장한다. Erasure Coding으로 일부 디스크가 손상돼도 데이터를 복구할 수 있다. 공식 내구성 99.999999999%(11-nine).
📖 더 보기: Object Storage Architecture of AWS S3 — S3의 Flat 구조, Erasure Coding, 분산 저장 메커니즘을 그림과 함께 설명 (중급)
Storage Class (스토리지 클래스)
- Standard: 자주 접근하는 데이터 (기본값)
- Infrequent Access (IA): 가끔 접근. Standard보다 저렴. 단, 최소 30일 보관 요구
- Glacier Instant Retrieval: 아카이브 + 즉시 복원 가능. 최소 90일 보관
- Glacier Deep Archive: 장기 아카이브. 매우 저렴하지만 복원에 12시간 소요. 최소 180일 보관
- Intelligent-Tiering: 접근 패턴을 자동 분석하여 최적 클래스로 자동 이동. 예측이 어려운 데이터에 유용
Lifecycle Policy — 비용 절감의 핵심
비유: Lifecycle Policy는 “자동 파일 정리 규칙”이다. “업로드한 지 30일이 지난 로그 파일은 IA로 이동하고, 90일이 지나면 Glacier로, 365일이 지나면 삭제”처럼 규칙을 설정하면 S3가 알아서 처리한다.
일반적인 Lifecycle 전략으로 스토리지 비용을 30~70% 절감할 수 있다:
Day 0: Standard (업로드 직후)Day 30: Standard-IA로 전환 (접근 빈도 감소)Day 90: Glacier Instant Retrieval로 전환Day 365: Glacier Deep Archive로 전환 (또는 삭제)AWS 콘솔에서 설정하는 방법:
S3 → 버킷 선택 → Management 탭 → Lifecycle rules → Create lifecycle rule→ Transition actions: 원하는 날짜에 클래스 전환 설정→ Expiration actions: 특정 날짜 이후 자동 삭제 설정주의: IA/Glacier 전환 시 최소 보관 기간이 있어, 너무 빨리 삭제하면 잔여 기간에 대한 요금이 부과된다.
Intelligent-Tiering — 접근 패턴을 모를 때의 베스트 선택 (2025)
접근 패턴이 불규칙하거나 예측이 어려운 데이터라면 Intelligent-Tiering이 최선이다. 내부적으로 Frequent Access / Infrequent Access / Archive Instant Access 세 계층을 자동으로 이동하며, 별도 검색 비용이 없다.
30일 미접근 → Infrequent Access tier (Standard-IA보다 저렴)90일 미접근 → Archive Instant Access tier→ 다시 접근하면 Frequent Access tier로 즉시 복귀 (검색 지연 없음)제약: 128KB 미만 객체는 자동 티어 이동이 안 됨. 객체당 월 $0.0025의 모니터링 요금 발생.
실제 절감 효과: AWS 발표에 따르면 S3 Intelligent-Tiering 사용자들은 Standard 대비 평균 67% 스토리지 비용 절감을 달성했다.
S3 보안 하드닝 — 2025년 체크리스트
-
HTTPS 강제 적용 (Bucket Policy)
HTTP로 오는 요청을 차단한다. 데이터 전송 중 암호화를 강제하는 버킷 정책이다.
{"Effect": "Deny","Principal": "*","Action": "s3:*","Resource": ["arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*"],"Condition": {"Bool": {"aws:SecureTransport": "false"}}} -
서버 사이드 암호화 강제 (SSE)
모든 업로드 시 암호화를 강제하려면 버킷의 Default Encryption을 활성화한다.
S3 → 버킷 → Properties → Default encryption → SSE-S3 또는 SSE-KMS 선택→ 이후 업로드되는 모든 객체는 자동으로 암호화 -
GuardDuty S3 위협 탐지 활성화
GuardDuty는 S3 데이터 접근 이벤트를 지속적으로 분석하여 이상 접근(비정상적인 IP에서 대량 다운로드 등)을 자동 탐지한다.
경로: GuardDuty → Settings → Enable → S3 Protection 활성화→ 이상 접근 감지 시 CloudWatch Events로 알림 수신 가능
Bucket Policy / ACL
버킷 레벨의 접근 권한 설정. IAM Policy와 함께 “누가 이 버킷의 파일을 읽기/쓰기 가능한지”를 정의.
⚠️ 퍼블릭 접근 주의
기본적으로 S3 버킷은 비공개다. “Block Public Access” 설정이 기본으로 켜져 있다.
이 설정을 끄면 전 세계 누구나 버킷 내용에 접근할 수 있다.
민감 데이터(개인정보, 로그, 백업)가 있는 버킷은 절대 퍼블릭으로 열지 않는다.
퍼블릭 파일 제공이 필요하면 Presigned URL 또는 CloudFront를 사용한다.
Versioning (버전 관리)
같은 Key로 파일을 덮어써도 이전 버전을 보관. 실수로 삭제해도 복구 가능.
주의: Versioning 활성화 후 삭제하면 실제 삭제가 아닌 “Delete Marker”가 생성된다. 완전 삭제하려면 Delete Marker도 함께 제거해야 한다.
Presigned URL — 왜 필요한가
비유: Presigned URL은 “일회용 입장권”이다. S3 버킷은 잠겨 있지만(비공개), 이 티켓을 가진 사람은 지정된 시간 동안 특정 파일에 접근할 수 있다.
사용 시나리오: 사용자가 파일을 업로드하거나 다운로드할 때, 서버가 Presigned URL을 생성해서 클라이언트에게 전달한다. 클라이언트는 이 URL로 S3에 직접 업로드/다운로드 — 서버를 거치지 않아 트래픽 절약.
NestJS에서 Presigned URL 생성 예시:
// npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presignerimport { S3Client, GetObjectCommand, PutObjectCommand,} from "@aws-sdk/client-s3";import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const s3Client = new S3Client({ region: "ap-northeast-2" });
// 다운로드 URL (10분)async function getPresignedDownloadUrl( bucket: string, key: string,): Promise<string> { const command = new GetObjectCommand({ Bucket: bucket, Key: key }); return getSignedUrl(s3Client, command, { expiresIn: 600 });}
// 업로드 URL (5분) — 클라이언트가 직접 S3에 업로드async function getPresignedUploadUrl( bucket: string, key: string,): Promise<string> { const command = new PutObjectCommand({ Bucket: bucket, Key: key, ContentType: "image/jpeg", // 클라이언트도 반드시 이 Content-Type으로 업로드해야 함 }); return getSignedUrl(s3Client, command, { expiresIn: 300 });}
// 사용 예시const url = await getPresignedDownloadUrl("my-bucket", "images/profile.jpg");// 출력 예시:// https://my-bucket.s3.ap-northeast-2.amazonaws.com/images/profile.jpg// ?X-Amz-Algorithm=AWS4-HMAC-SHA256// &X-Amz-Credential=...// &X-Amz-Expires=600// &X-Amz-Signature=...📖 더 보기: NestJS에서 S3 Presigned URL 구현 — 업로드/다운로드 Presigned URL 생성부터 프론트엔드 연동까지 단계별 가이드 (중급)
4. 실무에서 어디에 쓰이나
섹션 제목: “4. 실무에서 어디에 쓰이나”- 사용자 업로드 파일 저장 (이미지, 문서 등)
- 애플리케이션 로그 아카이브
- 정적 웹사이트 호스팅
- DB 백업 저장
- CI/CD 빌드 아티팩트 저장
- 데이터 레이크 (분석용 원시 데이터)
5. 현재 내 업무와 연결점
섹션 제목: “5. 현재 내 업무와 연결점”- 서비스에서 업로드된 파일이 어디에 저장되는지 이해
- 로그 아카이브 확인 시 S3에서 찾아야 함
- 배포 아티팩트가 S3에 있을 수 있음
- 비용 최적화 시 스토리지 클래스 전환 검토
5.5 객체 스토리지 원리의 전이 가능성
섹션 제목: “5.5 객체 스토리지 원리의 전이 가능성”S3에서 배운 핵심 원리는 벤더에 종속되지 않는다. GCS, Azure Blob Storage, MinIO 모두 동일한 객체 스토리지 아키텍처를 따른다.
주요 플랫폼 비교
섹션 제목: “주요 플랫폼 비교”| 항목 | AWS S3 | Google Cloud Storage | Azure Blob Storage | MinIO |
|---|---|---|---|---|
| 키 구조 | Flat namespace — /는 시각적 구분자, 실제 폴더 없음 | Flat namespace (동일) | Flat namespace. Azure Data Lake Gen2에서는 계층형 NS 선택 가능 | S3 호환 Flat namespace |
| Consistency | 강한 일관성 (2020년 이후 — PutObject 즉시 반영) | 강한 일관성 (전역) | 강한 일관성 (단일 리전). 지역 복제 시 eventual | 강한 일관성 (단일 노드/클러스터) |
| 멀티파트 업로드 | Multipart Upload API (5MB~5TB 파트) | Parallel Composite Upload | Block Blob 청킹 (블록 단위 커밋) | S3 Multipart API 완전 호환 |
| 요청 한도 | 3,500 PUT / 5,500 GET per prefix/sec | 자동 스케일 (quota 없음) | 2,000 req/sec per blob | 하드웨어 의존 |
| Presigned URL | X-Amz-Signature 서명 방식 | Signed URL (V4 호환) | SAS Token (비슷한 역할) | S3 Presigned URL 완전 호환 |
| Versioning | Object Versioning + Delete Marker | Object Versioning (유사) | Blob Versioning / Soft Delete | Versioning 지원 |
| 비용 구조 | $0.023/GB (Standard) | $0.020/GB (Standard) | $0.018/GB (Hot tier) | 자체 호스팅 (S3 스토리지 비용 없음) |
S3 원리 → 타 플랫폼 전이 공통 개념
섹션 제목: “S3 원리 → 타 플랫폼 전이 공통 개념”Flat namespace / Key-Value 조회는 객체 스토리지의 보편 원리다. S3의 bucket + key → object 패턴은 GCS와 MinIO에서 동일하게 동작한다. 플랫폼이 바뀌어도 “폴더는 없고 Key 이름에 /가 포함될 뿐”이라는 메커니즘은 그대로 적용된다.
Eventual Consistency → 강한 일관성으로 수렴: 2020년 이전 S3는 overwrite PUT / DELETE에 대해 eventual consistency였다. 현재 S3, GCS, Azure Blob 모두 강한 일관성을 제공하지만, 지역 복제(Cross-Region Replication)를 사용하는 경우 복제 지연(보통 수 초~수 분) 동안은 여전히 eventual하다.
Presigned URL / SAS Token 패턴: “서버가 자격증명 없이 클라이언트에게 임시 접근권을 위임”하는 패턴은 클라우드 스토리지 전반에 동일하게 적용된다. S3는 X-Amz-Signature, GCS는 Signed URL, Azure는 SAS Token이라는 이름을 쓰지만 원리는 동일하다.
Content Addressable Storage(CAS)와의 연결: Git의 .git/objects는 내용의 SHA-1 해시를 Key로 사용하는 객체 스토리지다. “콘텐츠 = Key” 구조는 S3의 Key-Value 패턴과 동일한 아키텍처다. 파일 내용이 같으면 동일한 Key → 중복 저장 방지. Versioning도 같은 원리 — Git의 commit history와 S3 Object Versioning 모두 “변경 불가능한 객체 + 새 버전 추가” 패턴이다.
MinIO: S3 API를 완전히 구현한 오픈소스 객체 스토리지. S3 SDK를 그대로 사용해 온프레미스에 배포 가능하다. Kubernetes 기반 분산 배포를 지원하며, 엔터프라이즈 환경에서 멀티클라우드/하이브리드 아키텍처에 자주 사용된다.
6. 자주 헷갈리는 개념 비교
섹션 제목: “6. 자주 헷갈리는 개념 비교”| 개념 A | 개념 B | 차이점 |
|---|---|---|
| S3 | EBS | S3는 객체 스토리지(파일 단위), EBS는 블록 스토리지(EC2에 붙이는 디스크) |
| Bucket Policy | IAM Policy | Bucket Policy는 버킷에 붙는 권한, IAM Policy는 사용자/역할에 붙는 권한 |
| Standard | Glacier | Standard는 즉시 접근, Glacier는 아카이브용(접근에 시간 소요) |
| Public Access | Presigned URL | Public은 누구나 접근, Presigned URL은 시간 제한 임시 접근 |
| Lifecycle Policy | Intelligent-Tiering | Lifecycle은 수동 규칙 기반 전환, Intelligent-Tiering은 접근 패턴 자동 분석 전환 |
6.5 트러블슈팅
섹션 제목: “6.5 트러블슈팅”🔧 S3 접두사 집중으로 인한 503 SlowDown — 성능 병목
섹션 제목: “🔧 S3 접두사 집중으로 인한 503 SlowDown — 성능 병목”증상: 대량 업로드/다운로드 작업 중 503 Slow Down 에러가 간헐적으로 발생. 재시도해도 반복됨
원인: S3는 내부적으로 요청을 prefix(접두사) 단위로 파티셔닝한다. 파티션당 처리 한도는 AWS 공식 기준으로 초당 3,500 PUT/COPY/POST/DELETE, 5,500 GET/HEAD이다. 동일한 prefix로 요청이 집중되면 파티션이 포화 상태에 이르러 503 Slow Down 응답을 반환한다.
자주 발생하는 패턴:
❌ 날짜 순차 prefix — 특정 날짜에 요청 집중logs/2024-01-15/app-001.loglogs/2024-01-15/app-002.loglogs/2024-01-15/app-003.log→ "logs/2024-01-15/" prefix 1개에 모든 요청 집중 → 503 SlowDown
❌ 순차 번호 prefixuploads/00001.jpg, uploads/00002.jpg, uploads/00003.jpg→ "uploads/" prefix 1개에 집중AWS 공식 권고 파티셔닝 전략:
# 전략 1: 해시 기반 prefix 분산# 원본 Key에서 SHA-256 앞 4자리를 prefix로 사용 → 최대 65,536개 파티션original_key="logs/2024-01-15/app-001.log"hash_prefix=$(echo -n "$original_key" | sha256sum | cut -c1-4)s3_key="${hash_prefix}/${original_key}"# 예: a3f2/logs/2024-01-15/app-001.log
# 전략 2: 타임스탬프 역순 prefix# 최신 데이터가 다른 prefix에 분산됨# 2024-01-15 → 5102-98-40 (역순) → prefix로 사용
# 전략 3: UUID/랜덤 prefix 추가import uuids3_key = f"{uuid.uuid4().hex[:8]}/logs/2024-01-15/app-001.log"# 예: a1b2c3d4/logs/2024-01-15/app-001.logprefix 분산으로 달성 가능한 처리량:
prefix 1개: 5,500 GET/secprefix 10개: 55,000 GET/sec (10배 선형 스케일)prefix 100개: 550,000 GET/secS3는 높은 요청률이 지속되면 내부적으로 파티션을 자동으로 추가 분할하지만, 이 프로세스는 점진적이며 분할 완료 전까지 503이 발생할 수 있다.
재시도 전략 (AWS 권고):
대용량 요청(>128MB): 가장 느린 5% 요청 재시도소용량 요청(<512KB): 2초 후 재시도 → 4초 후 재시도 (exponential backoff)503 발생 시: 즉시 재시도 X — 지수 백오프 + jitter 적용🔧 “403 Forbidden / AccessDenied” — S3 파일 접근 거부
섹션 제목: “🔧 “403 Forbidden / AccessDenied” — S3 파일 접근 거부”증상: aws s3 cp 또는 SDK에서 AccessDenied 에러. 콘솔에서는 보이는데 코드에서 안 됨
원인 1: IAM Policy에 s3:GetObject 또는 s3:PutObject 권한이 없음
원인 2: Bucket Policy가 해당 IAM Role을 명시적으로 Deny하고 있음
원인 3 (가장 흔한 함정): Bucket에 Block Public Access가 켜져 있는데 퍼블릭 접근을 시도함
해결:
- IAM Policy 확인: 해당 Role에
s3:GetObject,s3:PutObject등이 Allow되어 있는지 - Bucket Policy 확인: S3 → 버킷 → Permissions 탭 → Bucket Policy에 Deny 구문이 있는지
- 콘솔에서
aws s3 ls s3://bucket-name으로 접근 테스트
aws s3 ls s3://my-bucket/# AccessDenied 시: 위 3단계 확인# 정상 시:# 2024-01-15 10:00:00 1024 images/profile.jpg# 2024-01-15 10:01:00 2048 logs/app.log🔧 Presigned URL 관련 에러 — 서명 불일치 또는 만료
섹션 제목: “🔧 Presigned URL 관련 에러 — 서명 불일치 또는 만료”증상: 클라이언트에서 Presigned URL로 접근 시 403 Request has expired, SignatureDoesNotMatch, 또는 400 Bad Request 에러
원인별 정리:
| 에러 | 원인 | 해결 |
|---|---|---|
Request has expired | 만료 시간 초과 | 클라이언트에서 URL 발급 후 빠르게 사용하도록 안내, 또는 만료 전 재발급 로직 추가 |
SignatureDoesNotMatch | 서버 시계가 AWS와 5분 이상 차이 | EC2/컨테이너의 NTP 시간 동기화 확인 |
400 Bad Request | 업로드 시 Authorization 헤더를 추가로 붙인 경우 | Presigned URL 요청에는 Authorization 헤더를 추가하면 안 됨 |
SignatureDoesNotMatch (업로드) | URL 생성 시 지정한 ContentType과 실제 업로드 ContentType이 다름 | 클라이언트가 Content-Type 헤더를 URL 생성 시와 동일하게 보내야 함 |
2025년 AWS 보안 권고: Presigned URL의 유효 시간을 최대 15분으로 제한하는 것이 권장된다. 유출 시 피해를 최소화하기 위함이다.
🔧 S3 파일 업로드 후 파일이 안 보이는 경우
섹션 제목: “🔧 S3 파일 업로드 후 파일이 안 보이는 경우”증상: PutObject 성공 응답을 받았지만 콘솔에서 파일이 보이지 않음. 또는 이전 버전의 파일이 계속 반환됨
원인 1: 버킷 이름 또는 Key(경로)를 잘못 지정해서 다른 위치에 저장됨
원인 2: Versioning이 활성화된 버킷에서 삭제 마커(Delete Marker)가 생성되어 최신 파일이 숨겨짐
해결:
- 업로드 응답에서 실제 저장된 Key 확인 후 콘솔에서 직접 검색
- Versioning이 켜진 버킷이면 “Show versions” 토글을 켜서 삭제 마커 확인
🔧 S3 CORS 에러 — 브라우저에서 파일 업로드/다운로드 실패
섹션 제목: “🔧 S3 CORS 에러 — 브라우저에서 파일 업로드/다운로드 실패”증상: 브라우저 콘솔에서 Access to fetch at 'https://...' from origin '...' has been blocked by CORS policy 에러. Presigned URL로 직접 업로드하거나 S3 파일을 프론트엔드에서 직접 가져올 때 발생
원인: S3 버킷의 CORS 설정에 현재 도메인의 HTTP 메서드가 허용되지 않음
해결:
S3 → 버킷 → Permissions 탭 → Cross-origin resource sharing (CORS) → Edit
아래 설정 추가:[ { "AllowedHeaders": ["*"], "AllowedMethods": ["GET", "PUT", "POST"], ← 필요한 메서드 모두 추가 "AllowedOrigins": ["https://my-frontend.com"], ← 실제 도메인으로 변경 "ExposeHeaders": ["ETag"] }]🔧 S3 비용이 예상보다 많이 나오는 경우
섹션 제목: “🔧 S3 비용이 예상보다 많이 나오는 경우”증상: AWS Cost Explorer에서 S3 요금이 급증. 스토리지 요금이 아닌 “Requests” 또는 “Data Transfer” 항목이 주요 원인
원인 1: API 요청 수가 많아 PutObject/GetObject 요청 과금 (S3는 요청 건수로도 과금)
원인 2: 같은 리전 내 서비스 간 Data Transfer는 무료이나, 리전 간 또는 인터넷으로 나가는 트래픽은 과금
원인 3: Versioning이 켜진 버킷에 오래된 버전 데이터가 누적
해결:
# S3 Storage Lens로 버킷별 비용 분석# 경로: S3 → Storage Lens → Create dashboard# → 버킷별 스토리지 크기, 요청 수, 복제 데이터 분석 가능
# Versioning이 켜진 버킷의 오래된 버전 정리 (Lifecycle Rule)# S3 → 버킷 → Management → Lifecycle rules → Create rule# → "Previous versions" 대상, Expiration: 30일 후 삭제 설정
# AWS CLI로 특정 버킷의 버전 개수 확인aws s3api list-object-versions \ --bucket my-bucket \ --query 'length(Versions)' \ --output text# 예상 출력: 15234 ← 버전이 너무 많으면 정리 필요7. 체크리스트
섹션 제목: “7. 체크리스트”- S3의 Bucket과 Object 개념을 설명할 수 있다
- Storage Class의 차이를 설명할 수 있다
- S3 접근 권한이 어디서 관리되는지 안다 (Bucket Policy + IAM)
- Presigned URL이 뭔지, 언제 쓰는지 설명할 수 있다
- Lifecycle Policy로 비용을 절감하는 방법을 설명할 수 있다
- 동일 prefix 집중 시 503 SlowDown이 발생하는 이유와 파티셔닝 전략을 설명할 수 있다
- S3 Flat namespace 원리가 GCS, Azure Blob, MinIO에서도 동일하게 적용됨을 설명할 수 있다
8. 추가 학습 키워드
섹션 제목: “8. 추가 학습 키워드”S3 Event Notification, Lifecycle Policy, Cross-Region Replication, Transfer Acceleration, CloudFront + S3, Intelligent-Tiering, S3 Prefix Partitioning, Content Addressable Storage, MinIO
📚 추천 리소스
섹션 제목: “📚 추천 리소스”- 📖 AWS S3 공식 문서 - Presigned URL 업로드 — Presigned URL로 업로드하는 원리와 코드 예시 공식 가이드 (입문)
- 📖 NestJS S3 Presigned URL 구현 가이드 — NestJS + AWS SDK v3로 업로드/다운로드 URL 생성 단계별 튜토리얼 (중급)
- 📖 S3 403 Forbidden 에러 트러블슈팅 — AccessDenied 원인 12가지 유형별 진단 방법 공식 문서 (입문)
- 📖 AWS S3 보안 베스트 프랙티스 2025 — Block Public Access, HTTPS 강제, 암호화, GuardDuty 연동 AWS 공식 가이드 (중급)
- 📖 S3 Intelligent-Tiering 비용 최적화 가이드 — 접근 패턴 분석과 자동 티어 이동 원리, 비용 절감 시나리오 (중급)
9. 내가 직접 확인해볼 것
섹션 제목: “9. 내가 직접 확인해볼 것”- AWS 콘솔에서 팀 S3 버킷 목록 확인
경로: S3 → Buckets확인: 버킷 이름, 리전, 접근 권한 (Public access blocked 여부)
- 버킷 하나를 열어보고 폴더 구조와 파일 확인
경로: S3 → <버킷 이름> → Objects 탭주의: "폴더"로 보이는 것은 실제로 Key에 /가 포함된 객체들의 그룹
- AWS CLI로 버킷 파일 목록 확인
Terminal window aws s3 ls s3://bucket-name --recursive | head -10# 예상 출력:# 2024-01-15 10:00:00 1024 images/profile.jpg# 2024-01-15 10:01:00 2048 uploads/doc.pdf# 2024-01-15 10:02:00 1048576 backups/db-2024-01-15.sql.gz - Bucket Policy가 어떻게 설정되어 있는지 확인
경로: S3 → <버킷> → Permissions 탭 → Bucket Policy 섹션
10. 5줄 요약
섹션 제목: “10. 5줄 요약”- S3는 파일을 무제한으로 저장할 수 있는 AWS 객체 스토리지이며, 99.999999999%(11-nine) 내구성을 여러 AZ에 분산 저장으로 보장한다
- Bucket(컨테이너) + Object(파일) + Key(경로) 구조이며, “폴더”는 시각적 표현일 뿐 실제로는 Key 이름에
/가 포함된 것이다 - Lifecycle Policy + Intelligent-Tiering으로 스토리지 비용을 30~67% 절감할 수 있다 — 파일 특성에 따라 선택한다
- 접근 권한은 Bucket Policy(버킷 레벨) + IAM Policy(사용자/역할 레벨)의 교집합으로 결정된다, Presigned URL은 비공개 파일의 임시 접근에 사용한다
- S3 버킷은 기본 비공개이며, Block Public Access는 절대 해제하지 않는다 — 퍼블릭 파일이 필요하면 CloudFront를 앞에 둔다
프론트엔드 → 플랫폼 브릿지
섹션 제목: “프론트엔드 → 플랫폼 브릿지”CDN Static File Hosting과 S3의 관계
프론트엔드 개발 시 Netlify나 Vercel에 배포하면 알아서 CDN을 통해 전 세계 사용자에게 빠르게 정적 파일을 제공해준다. AWS에서는 이것을 S3 + CloudFront 조합으로 직접 구성한다:
[Netlify/Vercel 내부 동작 (추상화됨)]git push → 빌드 → CDN 자동 배포
[AWS에서 동일한 구성 (직접 제어)]git push → GitHub Actions 빌드 → S3 업로드 (버킷이 원본 저장소) → CloudFront Invalidation (캐시 갱신)사용자 → CloudFront (엣지 서버, 캐시) → S3 (원본, 직접 접근 차단)S3 버킷 자체는 비공개로 유지하고, CloudFront만 Origin Access Control(OAC)로 접근 권한을 갖는다. 이 구조가 “S3 + CloudFront”의 핵심이다.
프론트에서 파일 업로드 구현 시 두 가지 방법
React 앱에서 사용자가 이미지를 업로드할 때:
방법 1: 서버 경유 (간단하지만 서버 부하)React → multipart/form-data → NestJS API → S3 PutObject단점: 대용량 파일이면 NestJS 서버 메모리/CPU 사용
방법 2: Presigned URL (권장 — 서버 부하 없음)React → NestJS API (Presigned URL 요청)NestJS → Secrets Manager에서 자격증명 → S3 Presigned URL 생성 → React에 반환React → Presigned URL로 S3에 직접 업로드 (NestJS 불경유)NestJS ← S3 Event Notification (업로드 완료 알림)
// React 업로드 코드 예시const uploadFile = async (file: File) => { // 1. 백엔드에서 Presigned URL 받기 const { uploadUrl, fileKey } = await fetch('/api/upload-url', { method: 'POST', body: JSON.stringify({ fileName: file.name, contentType: file.type }) }).then(r => r.json());
// 2. S3에 직접 업로드 (서버 불경유) await fetch(uploadUrl, { method: 'PUT', body: file, headers: { 'Content-Type': file.type } // Presigned URL 생성 시 지정한 것과 동일해야 함 });};왜 S3 파일을 CloudFront 없이 직접 serving하면 안 되나
S3 버킷을 Public으로 열어서 직접 URL(https://my-bucket.s3.amazonaws.com/...)로 서빙하면:
- S3 요청 비용이 직접 청구됨 (CloudFront 캐시 없음)
- 리전이 한국(ap-northeast-2)이면 해외 사용자는 느림
- 버킷이 퍼블릭이라 잘못된 버킷 정책으로 전체 데이터가 노출될 위험
CloudFront를 앞에 두면: 캐시 히트율 높아 S3 요청 비용 90% 절감 + 전 세계 엣지 서버로 빠른 응답 + S3 버킷은 비공개 유지.
실무 아키텍처 패턴 — S3 사용 시나리오별 구성
섹션 제목: “실무 아키텍처 패턴 — S3 사용 시나리오별 구성”[파일 업로드 패턴 — 서버 트래픽 최소화]클라이언트 → NestJS API (Presigned URL 발급, 5~15분 유효)클라이언트 → S3 (직접 업로드, 서버 불경유)NestJS API ← S3 Event Notification (업로드 완료 알림)
[정적 파일 서빙 패턴 — 보안 + 성능]클라이언트 → CloudFront (CDN, HTTPS, 캐시)CloudFront → S3 (Origin Access Control, S3 버킷 비공개 유지)→ S3 버킷에 퍼블릭 접근 없이 CloudFront를 통해서만 파일 제공
[로그 아카이브 패턴]ECS/EC2 → CloudWatch Logs → S3 (Export, 30일 이후 자동 이동)S3 Lifecycle: Standard → Standard-IA (30일) → Glacier (90일) → 삭제 (365일)S3 Express One Zone — 2025년 주목할 신규 스토리지 클래스
2024년 말 출시 후 2025년 4월에 대폭 가격 인하(스토리지 31%, GET 요청 85%)된 고성능 스토리지 클래스이다. 단일 AZ에 저장되어 내구성은 낮지만, S3 Standard 대비 10배 빠른 접근 속도와 단일 자릿수 밀리초 지연시간을 제공한다.
- 최대 처리량: 초당 200만 요청
- 사용 사례: AI/ML 학습 데이터, 실시간 분석, 미디어 렌더링, HPC
- 버킷 타입: 일반 S3 버킷과 다른 Directory Bucket 타입으로 생성
- 주의: 단일 AZ이므로 AZ 장애 시 데이터 손실 위험이 있음 — 중요 데이터에는 사용하지 않는다
📖 더 보기: Amazon S3 Express One Zone 공식 페이지 — 고성능 스토리지 클래스 사양, 가격, 사용 사례 (중급)