1. S3 (Simple Storage Service)
기본 특성
S3는 객체 스토리지(Object Storage)
- 파일 시스템이 아니라 Key-Value 저장소
- Key: 파일 경로처럼 보이지만 실제로는 문자열
- Value: 파일 내용 (최대 5TB)
예시:
Key: "employees/detail/index.html"
Value: <HTML 파일 바이너리>
Key: "images/logo.png"
Value: <PNG 파일 바이너리>
일반 파일 시스템 vs S3
항목 파일 시스템 S3
| 구조 | 폴더/디렉토리 계층 | 평평한 Key-Value |
| 경로 | /employees/detail/ | "employees/detail/index.html" (문자열) |
| 부분 수정 | 가능 (파일 일부만 수정) | 불가능 (전체 교체만 가능) |
| 동시 쓰기 | 파일 잠금(Lock) 필요 | Last-Write-Wins |
S3의 두 가지 모드
1. 기본 모드 (Object Storage)
요청: GET /employees/detail
S3: "employees/detail"이라는 Key 검색
결과: Key 없음 → 404 Not Found
동작 원리:
- 정확한 Key만 찾음
- 추론 불가 (index.html 자동 매칭 안 됨)
- 단순 파일 저장/조회
2. Website Hosting 모드 (Static Web Server)
요청: GET /employees/detail/
S3: "employees/detail/" Key 검색 → 없음
→ index_document 설정 확인
→ "employees/detail/index.html" 반환
동작 원리:
- 디렉토리 요청 → index.html 자동 추가
- 파일 없음 → error_document 설정 확인
- HTTP 상태 코드 관리 (200, 404)
제약사항:
- HTTPS 미지원 (HTTP만 가능)
- 동적 처리 불가 (PHP, Node.js 실행 안 됨)
- 미리 빌드된 정적 파일만 서빙
S3 URL 형식
일반 S3 URL (REST API):
https://bucket-name.s3.ap-northeast-2.amazonaws.com/key
https://erp-frontend-dev.s3.ap-northeast-2.amazonaws.com/index.html
Website Endpoint (Website Hosting 활성화 시):
http://bucket-name.s3-website.ap-northeast-2.amazonaws.com
http://erp-frontend-dev.s3-website.ap-northeast-2.amazonaws.com
차이점:
- REST API: HTTPS 지원, 정확한 Key만 조회
- Website Endpoint: HTTP만 지원, index.html 자동 매칭
S3 Public Access
4가지 제어 옵션:
block_public_acls = false # Public ACL 허용
block_public_policy = false # Public Bucket Policy 허용
ignore_public_acls = false # Public ACL 무시 안 함
restrict_public_buckets = false # Public 버킷 제한 안 함
모두 false = 완전 Public 허용
Bucket Policy 예시:
{
"Effect": "Allow",
"Principal": "*", // 누구나
"Action": "s3:GetObject", // 읽기만
"Resource": "arn:aws:s3:::bucket-name/*"
}
2. CloudFront (CDN - Content Delivery Network)
기본 특성
CloudFront는 전 세계 캐싱 네트워크
- 450개 이상의 엣지 로케이션(Edge Location)
- 사용자와 가까운 곳에서 콘텐츠 제공
- 오리진(Origin) 서버 부하 감소
동작 원리
사용자 (서울) → CloudFront 엣지 (서울)
↓ 캐시 없음
S3 오리진 (도쿄)
↓ 파일 반환
CloudFront 엣지 (캐싱)
↓
사용자에게 반환
다음 요청 (부산 사용자) → CloudFront 엣지 (서울)
↓ 캐시 있음
즉시 반환 (S3 접근 안 함)
캐싱 정책
TTL (Time To Live):
min_ttl = 0 # 최소 캐싱 시간
default_ttl = 3600 # 기본 1시간
max_ttl = 86400 # 최대 24시간
Cache Behavior:
- cached_methods = ["GET", "HEAD"]: 이 메서드만 캐싱
- compress = true: 자동 압축 (gzip)
- query_string = false: 쿼리 파라미터 무시
Origin 연결 방식
1. S3 REST API 방식
origin {
domain_name = "bucket.s3.ap-northeast-2.amazonaws.com"
origin_access_control_id = aws_cloudfront_oac.main.id # OAC (최신)
# 또는
s3_origin_config {
origin_access_identity = aws_cloudfront_oai.main.id # OAI (기존)
}
}
- S3를 Private으로 유지
- CloudFront만 접근 허용
- Website Hosting 기능 사용 불가
2. S3 Website Endpoint 방식
origin {
domain_name = "bucket.s3-website.ap-northeast-2.amazonaws.com"
custom_origin_config {
origin_protocol_policy = "http-only"
}
}
- S3를 Public으로 열어야 함
- Website Hosting 기능 사용 가능
- CloudFront가 일반 웹서버처럼 S3 접근
SSL/TLS 처리
Viewer → CloudFront:
viewer_protocol_policy = "redirect-to-https" # HTTPS 강제
- 사용자는 무조건 HTTPS로 접근
- HTTP 요청 → 301 리다이렉트 → HTTPS
CloudFront → Origin:
origin_protocol_policy = "http-only" # S3 Website Endpoint는 HTTP만
# 또는
origin_protocol_policy = "https-only" # API Gateway 같은 HTTPS Origin
SSL Termination:
- CloudFront가 HTTPS 암호화/복호화 처리
- Origin까지는 HTTP 가능 (AWS 내부망)
- 사용자는 항상 암호화된 연결 유지
Custom Error Response
SPA 라우팅 처리:
custom_error_response {
error_code = 404
response_code = 200
response_page_path = "/index.html"
}
동작:
- 사용자: /employees/detail 요청
- S3: 파일 없음 → 404 반환
- CloudFront: 404 감지 → custom_error_response 확인
- /index.html 내용을 200 상태로 반환
- React Router가 브라우저에서 경로 처리
3. S3 + CloudFront 연동 방식 비교
| 항목 | OAI/OAC + REST API | Public + Website Endpoint(내 프로젝트 방식) |
| S3 접근 제어 | Private (CloudFront만) | Public (누구나) |
| Website Hosting | 불가 | 가능 |
| SPA 라우팅 | CloudFront Functions 필요 | S3 error_document 사용 |
| 보안 수준 | 최고 | 충분함 (읽기 전용) |
| 설정 복잡도 | 복잡 | 간단 |
| HTTPS 지원 | CloudFront 필수 | CloudFront 필수 |
4. OAI vs OAC 심화 (프로덕션 전환 가이드)
OAI (Origin Access Identity) - 구세대
동작 원리: OAI는 CloudFront가 "특수한 IAM 사용자"처럼 동작한다. AWS가 내부적으로 CloudFront용 IAM Identity를 생성하고, 이 Identity의 ARN을 S3 Bucket Policy에 명시한다.
# OAI 생성
resource "aws_cloudfront_origin_access_identity" "main" {
comment = "OAI for Frontend"
}
# S3 Policy
Principal = {
AWS = "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E123456789"
}
OAI의 한계:
- SSE-KMS 미지원: KMS는 IAM User만 인식하는데, CloudFront Identity는 예외 처리되지 않음
- 일부 리전 미지원: 신규 리전에서 OAI 동작 안 하는 경우 있음
- IAM 의존: CloudFront-S3 신뢰 관계가 IAM에 의존 (구조적 한계)
- 서명 검증 없음: 요청 위조 가능성 존재
OAC (Origin Access Control) - 신세대
동작 원리: OAC는 AWS Service Principal 방식을 사용한다. "CloudFront라는 AWS 서비스 자체"를 신뢰하되, Condition으로 특정 Distribution만 허용한다.
# OAC 생성
resource "aws_cloudfront_origin_access_control" "main" {
name = "frontend-oac"
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}
# CloudFront Origin 설정
origin {
domain_name = aws_s3_bucket.frontend.bucket_regional_domain_name
origin_id = "S3-bucket"
origin_access_control_id = aws_cloudfront_origin_access_control.main.id
}
# S3 Policy
Principal = {
Service = "cloudfront.amazonaws.com"
}
Condition = {
StringEquals = {
"AWS:SourceArn" = "arn:aws:cloudfront::123456789012:distribution/E1234567890ABC"
}
}
OAC 핵심 파라미터:
1. signing_behavior = "always"
- 모든 요청에 서명 강제
- CloudFront가 S3로 보내는 모든 요청에 AWS 서명 추가
- "never"는 서명 안 함, "no-override"는 Origin 설정 따름
2. signing_protocol = "sigv4"
- AWS Signature Version 4 사용
- 각 요청마다 다음 헤더 추가:
Authorization: AWS4-HMAC-SHA256 Credential=...
X-Amz-Date: 20250122T123456Z
X-Amz-Content-SHA256: [해시값]
- S3가 이 서명을 검증해서 "허용된 Distribution인지" 확인
3. origin_access_control_origin_type = "s3"
- S3 전용 OAC
- MediaStore, MediaPackage 등 다른 Origin 타입도 있음
OAC의 장점
1. 특정 Distribution만 허용
Condition = {
StringEquals = {
"AWS:SourceArn" = aws_cloudfront_distribution.frontend.arn
}
}
- 같은 계정의 다른 CloudFront Distribution도 차단
- OAI는 이런 세밀한 제어 불가능
2. SSE-KMS 암호화 호환
resource "aws_s3_bucket_server_side_encryption_configuration" "frontend" {
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.s3.id
}
}
}
- OAI는 KMS 정책에서 CloudFront Identity를 인식 못 함
- OAC는 Service Principal이므로 정상 동작
3. SigV4 서명으로 위조 방지
CloudFront가 S3로 요청 보낼 때:
GET /index.html HTTP/1.1
Host: bucket.s3.amazonaws.com
Authorization: AWS4-HMAC-SHA256
Credential=AKIAIOSFODNN7EXAMPLE/20250122/ap-northeast-2/s3/aws4_request,
SignedHeaders=host;x-amz-date,
Signature=...
X-Amz-Date: 20250122T123456Z
X-Amz-Content-SHA256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
S3는 다음을 검증:
- 서명이 유효한가? (Signature 검증)
- 요청이 허용된 Distribution에서 왔는가? (SourceArn 확인)
- 요청이 변조되지 않았는가? (Content-SHA256 검증)
OAI는 이런 서명 검증이 없어서 상대적으로 취약했다.
프로덕션 전환 체크리스트
1단계: OAC 생성
resource "aws_cloudfront_origin_access_control" "main" {
name = "production-oac"
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}
2단계: CloudFront Origin 변경
# 기존 (Website Endpoint)
domain_name = "bucket.s3-website.ap-northeast-2.amazonaws.com"
# 변경 (REST API Endpoint)
domain_name = "bucket.s3.ap-northeast-2.amazonaws.com"
origin_access_control_id = aws_cloudfront_origin_access_control.main.id
3단계: S3 Public Access 차단
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
4단계: S3 Bucket Policy 업데이트
Principal = {
Service = "cloudfront.amazonaws.com"
}
Condition = {
StringEquals = {
"AWS:SourceArn" = aws_cloudfront_distribution.frontend.arn
}
}
5단계: Website Configuration 삭제
# 제거
# resource "aws_s3_bucket_website_configuration" "frontend" { ... }
6단계: CloudFront Custom Error Response 유지
custom_error_response {
error_code = 404
response_code = 200
response_page_path = "/index.html"
}
마이그레이션 시 주의사항
- 배포 순서 중요
- OAC 생성 → CloudFront 업데이트 → S3 Policy 업데이트 → Public Access 차단
- 순서 틀리면 일시적으로 접근 불가
- 캐시 무효화
- CloudFront 변경 후 캐시 무효화 필요
- aws cloudfront create-invalidation --distribution-id E123 --paths "/*"
- 테스트 환경 먼저
- 프로덕션 직접 변경 금지
- dev/staging에서 먼저 테스트
- 롤백 계획
- OAC 적용 후 문제 발생 시 Public 방식으로 즉시 롤백 가능하도록 준비
5. 면접 대비 핵심 포인트
S3 질문 대응
Q: S3는 파일 시스템인가요? A: 아닙니다. S3는 객체 스토리지로 Key-Value 저장소입니다. 파일 경로처럼 보이지만 실제로는 평평한 구조의 문자열 Key입니다.
Q: S3 Website Hosting과 일반 모드의 차이는? A: 일반 모드는 정확한 Key만 조회하지만, Website Hosting은 index.html 자동 매칭과 error_document 폴백을 지원해서 웹서버처럼 동작합니다.
Q: S3를 Public으로 여는 게 위험하지 않나요? A: s3:GetObject만 허용하면 읽기 전용이므로 안전합니다. 정적 파일은 원래 공개된 코드이고, 백엔드 API는 별도 인증이 필요합니다.
CloudFront 질문 대응
Q: CloudFront는 왜 사용하나요? A: 1) 전 세계 엣지 로케이션에서 캐싱해 속도 향상, 2) HTTPS 적용, 3) Origin 서버 부하 감소, 4) DDoS 보호 기능 제공
Q: CloudFront 없이 S3만 써도 되지 않나요? A: S3 Website Hosting은 HTTP만 지원하고, 캐싱도 없어서 느립니다. CloudFront는 HTTPS와 전 세계 캐싱을 제공합니다.
Q: OAI와 Public 방식 중 어떤 걸 선택해야 하나요? A: Website Hosting 기능(index.html 자동 매칭, error_document)이 필요하면 Public 방식, 보안이 최우선이면 OAI + CloudFront Functions 조합을 선택합니다.